From a734ac0ee9f0d4ce9e6cbc5ee2ea383e8a12c376 Mon Sep 17 00:00:00 2001
From: Brannon King <countprimes@gmail.com>
Date: Fri, 30 Jul 2021 16:24:14 -0400
Subject: [PATCH] [lbry] rpc: support claim related methods

---
 btcjson/chainsvrresults.go      |   7 +
 btcjson/chainsvrresults_test.go |   2 +-
 btcjson/claimcmds.go            |  95 +++++++++
 btcjson/help.go                 |   6 +
 btcjson/jsonrpc.go              |   8 +-
 rpcclaimtrie.go                 | 343 ++++++++++++++++++++++++++++++++
 rpcserverhelp.go                |  85 +++++++-
 7 files changed, 542 insertions(+), 4 deletions(-)
 create mode 100644 btcjson/claimcmds.go
 create mode 100644 rpcclaimtrie.go

diff --git a/btcjson/chainsvrresults.go b/btcjson/chainsvrresults.go
index 405fd867..6a99b2d3 100644
--- a/btcjson/chainsvrresults.go
+++ b/btcjson/chainsvrresults.go
@@ -298,6 +298,8 @@ type GetBlockTemplateResult struct {
 	// Block proposal from BIP 0023.
 	Capabilities  []string `json:"capabilities,omitempty"`
 	RejectReasion string   `json:"reject-reason,omitempty"`
+
+	ClaimTrieHash string `json:"claimtrie"`
 }
 
 // GetMempoolEntryResult models the data returned from the getmempoolentry's
@@ -430,6 +432,9 @@ type ScriptPubKeyResult struct {
 	Hex       string   `json:"hex,omitempty"`
 	ReqSigs   int32    `json:"reqSigs,omitempty"`
 	Type      string   `json:"type"`
+	SubType   string   `json:"subtype"`
+	IsClaim   bool     `json:"isclaim"`
+	IsSupport bool     `json:"issupport"`
 	Addresses []string `json:"addresses,omitempty"`
 }
 
@@ -588,6 +593,8 @@ func (v *Vin) MarshalJSON() ([]byte, error) {
 type PrevOut struct {
 	Addresses []string `json:"addresses,omitempty"`
 	Value     float64  `json:"value"`
+	IsClaim   bool     `json:"isclaim"`
+	IsSupport bool     `json:"issupport"`
 }
 
 // VinPrevOut is like Vin except it includes PrevOut.  It is used by searchrawtransaction
diff --git a/btcjson/chainsvrresults_test.go b/btcjson/chainsvrresults_test.go
index 72dcd8d7..af47ccab 100644
--- a/btcjson/chainsvrresults_test.go
+++ b/btcjson/chainsvrresults_test.go
@@ -70,7 +70,7 @@ func TestChainSvrCustomResults(t *testing.T) {
 				},
 				Sequence: 4294967295,
 			},
-			expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"prevOut":{"addresses":["addr1"],"value":0},"sequence":4294967295}`,
+			expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"prevOut":{"addresses":["addr1"],"value":0,"isclaim":false,"issupport":false},"sequence":4294967295}`,
 		},
 	}
 
diff --git a/btcjson/claimcmds.go b/btcjson/claimcmds.go
new file mode 100644
index 00000000..cb98fbf8
--- /dev/null
+++ b/btcjson/claimcmds.go
@@ -0,0 +1,95 @@
+package btcjson
+
+func init() {
+	// No special flags for commands in this file.
+	flags := UsageFlag(0)
+
+	MustRegisterCmd("getchangesinblock", (*GetChangesInBlockCmd)(nil), flags)
+	MustRegisterCmd("getclaimsforname", (*GetClaimsForNameCmd)(nil), flags)
+	MustRegisterCmd("getclaimsfornamebyid", (*GetClaimsForNameByIDCmd)(nil), flags)
+	MustRegisterCmd("getclaimsfornamebybid", (*GetClaimsForNameByBidCmd)(nil), flags)
+	MustRegisterCmd("getclaimsfornamebyseq", (*GetClaimsForNameBySeqCmd)(nil), flags)
+	MustRegisterCmd("normalize", (*GetNormalizedCmd)(nil), flags)
+}
+
+// optional inputs are required to be pointers, but they support things like `jsonrpcdefault:"false"`
+// optional inputs have to be at the bottom of the struct
+// optional outputs require ",omitempty"
+// traditional bitcoin fields are all lowercase
+
+type GetChangesInBlockCmd struct {
+	HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
+}
+
+type GetChangesInBlockResult struct {
+	Hash   string   `json:"hash"`
+	Height int32    `json:"height"`
+	Names  []string `json:"names"`
+}
+
+type GetClaimsForNameCmd struct {
+	Name          string  `json:"name"`
+	HashOrHeight  *string `json:"hashorheight" jsonrpcdefault:""`
+	IncludeValues *bool   `json:"includevalues" jsonrpcdefault:"false"`
+}
+
+type GetClaimsForNameByIDCmd struct {
+	Name            string   `json:"name"`
+	PartialClaimIDs []string `json:"partialclaimids"`
+	HashOrHeight    *string  `json:"hashorheight" jsonrpcdefault:""`
+	IncludeValues   *bool    `json:"includevalues" jsonrpcdefault:"false"`
+}
+
+type GetClaimsForNameByBidCmd struct {
+	Name          string  `json:"name"`
+	Bids          []int32 `json:"bids"`
+	HashOrHeight  *string `json:"hashorheight" jsonrpcdefault:""`
+	IncludeValues *bool   `json:"includevalues" jsonrpcdefault:"false"`
+}
+
+type GetClaimsForNameBySeqCmd struct {
+	Name          string  `json:"name"`
+	Sequences     []int32 `json:"sequences" jsonrpcusage:"[sequence,...]"`
+	HashOrHeight  *string `json:"hashorheight" jsonrpcdefault:""`
+	IncludeValues *bool   `json:"includevalues" jsonrpcdefault:"false"`
+}
+
+type GetClaimsForNameResult struct {
+	Hash           string        `json:"hash"`
+	Height         int32         `json:"height"`
+	NormalizedName string        `json:"normalizedname"`
+	Claims         []ClaimResult `json:"claims"`
+	// UnclaimedSupports []SupportResult `json:"unclaimedSupports"` how would this work with other constraints?
+}
+
+type SupportResult struct {
+	TXID          string `json:"txid"`
+	N             uint32 `json:"n"`
+	Height        int32  `json:"height"`
+	ValidAtHeight int32  `json:"validatheight"`
+	Amount        int64  `json:"amount"`
+	Address       string `json:"address,omitempty"`
+	Value         string `json:"value,omitempty"`
+}
+
+type ClaimResult struct {
+	ClaimID         string          `json:"claimid"`
+	TXID            string          `json:"txid"`
+	N               uint32          `json:"n"`
+	Bid             int32           `json:"bid"`
+	Sequence        int32           `json:"sequence"`
+	Height          int32           `json:"height"`
+	ValidAtHeight   int32           `json:"validatheight"`
+	EffectiveAmount int64           `json:"effectiveamount"`
+	Supports        []SupportResult `json:"supports,omitempty"`
+	Address         string          `json:"address,omitempty"`
+	Value           string          `json:"value,omitempty"`
+}
+
+type GetNormalizedCmd struct {
+	Name string `json:"name"`
+}
+
+type GetNormalizedResult struct {
+	NormalizedName string `json:"normalizedname"`
+}
diff --git a/btcjson/help.go b/btcjson/help.go
index f502d09f..04d85635 100644
--- a/btcjson/help.go
+++ b/btcjson/help.go
@@ -547,6 +547,12 @@ func GenerateHelp(method string, descs map[string]string, resultTypes ...interfa
 			return desc
 		}
 
+		if strings.Contains(key, "base-") {
+			if desc, ok := descs[strings.ReplaceAll(key, "base-", "-")]; ok {
+				return desc
+			}
+		}
+
 		missingKey = key
 		return key
 	}
diff --git a/btcjson/jsonrpc.go b/btcjson/jsonrpc.go
index 553a7bc3..e94653da 100644
--- a/btcjson/jsonrpc.go
+++ b/btcjson/jsonrpc.go
@@ -226,8 +226,12 @@ func NewResponse(rpcVersion RPCVersion, id interface{}, marshalledResult []byte,
 // JSON-RPC client.
 func MarshalResponse(rpcVersion RPCVersion, id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) {
 	if !rpcVersion.IsValid() {
-		str := fmt.Sprintf("rpcversion '%s' is invalid", rpcVersion)
-		return nil, makeError(ErrInvalidType, str)
+		if rpcVersion == "" {
+			rpcVersion = RpcVersion1
+		} else {
+			str := fmt.Sprintf("rpcversion '%s' is unsupported", rpcVersion)
+			return nil, makeError(ErrInvalidType, str)
+		}
 	}
 
 	marshalledResult, err := json.Marshal(result)
diff --git a/rpcclaimtrie.go b/rpcclaimtrie.go
new file mode 100644
index 00000000..d58f1cc1
--- /dev/null
+++ b/rpcclaimtrie.go
@@ -0,0 +1,343 @@
+package main
+
+import (
+	"bytes"
+	"encoding/hex"
+	"strconv"
+	"strings"
+
+	"github.com/btcsuite/btcd/btcjson"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/btcsuite/btcd/claimtrie/node"
+	"github.com/btcsuite/btcd/claimtrie/normalization"
+	"github.com/btcsuite/btcd/database"
+	"github.com/btcsuite/btcd/txscript"
+	"github.com/btcsuite/btcd/wire"
+)
+
+var claimtrieHandlers = map[string]commandHandler{
+	"getchangesinblock":     handleGetChangesInBlock,
+	"getclaimsforname":      handleGetClaimsForName,
+	"getclaimsfornamebyid":  handleGetClaimsForNameByID,
+	"getclaimsfornamebybid": handleGetClaimsForNameByBid,
+	"getclaimsfornamebyseq": handleGetClaimsForNameBySeq,
+	"normalize":             handleGetNormalized,
+}
+
+func handleGetChangesInBlock(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
+
+	c := cmd.(*btcjson.GetChangesInBlockCmd)
+	hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
+	if err != nil {
+		return nil, err
+	}
+
+	names, err := s.cfg.Chain.GetNamesChangedInBlock(height)
+	if err != nil {
+		return nil, &btcjson.RPCError{
+			Code:    btcjson.ErrRPCMisc,
+			Message: "Message: " + err.Error(),
+		}
+	}
+
+	return btcjson.GetChangesInBlockResult{
+		Hash:   hash,
+		Height: height,
+		Names:  names,
+	}, nil
+}
+
+func parseHashOrHeight(s *rpcServer, hashOrHeight *string) (string, int32, error) {
+	if hashOrHeight == nil || len(*hashOrHeight) == 0 {
+
+		if !s.cfg.Chain.IsCurrent() {
+			return "", 0, &btcjson.RPCError{
+				Code:    btcjson.ErrRPCClientInInitialDownload,
+				Message: "Unable to query the chain tip during initial download",
+			}
+		}
+
+		// just give them the latest block if a specific one wasn't requested
+		best := s.cfg.Chain.BestSnapshot()
+		return best.Hash.String(), best.Height, nil
+	}
+
+	ht, err := strconv.ParseInt(*hashOrHeight, 10, 32)
+	if err == nil && len(*hashOrHeight) < 32 {
+		hs, err := s.cfg.Chain.BlockHashByHeight(int32(ht))
+		if err != nil {
+			return "", 0, &btcjson.RPCError{
+				Code:    btcjson.ErrRPCBlockNotFound,
+				Message: "Unable to locate a block at height " + *hashOrHeight + ": " + err.Error(),
+			}
+		}
+		return hs.String(), int32(ht), nil
+	}
+
+	hs, err := chainhash.NewHashFromStr(*hashOrHeight)
+	if err != nil {
+		return "", 0, &btcjson.RPCError{
+			Code:    btcjson.ErrRPCInvalidParameter,
+			Message: "Unable to parse a height or hash from " + *hashOrHeight + ": " + err.Error(),
+		}
+	}
+	h, err := s.cfg.Chain.BlockHeightByHash(hs)
+	if err != nil {
+		return hs.String(), h, &btcjson.RPCError{
+			Code:    btcjson.ErrRPCBlockNotFound,
+			Message: "Unable to find a block with hash " + hs.String() + ": " + err.Error(),
+		}
+	}
+	return hs.String(), h, nil
+}
+
+func handleGetClaimsForName(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
+
+	c := cmd.(*btcjson.GetClaimsForNameCmd)
+	hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
+	if err != nil {
+		return nil, err
+	}
+
+	name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
+	if err != nil {
+		return nil, &btcjson.RPCError{
+			Code:    btcjson.ErrRPCMisc,
+			Message: "Message: " + err.Error(),
+		}
+	}
+
+	var results []btcjson.ClaimResult
+	for i := range n.Claims {
+		cr, err := toClaimResult(s, int32(i), n, c.IncludeValues)
+		if err != nil {
+			return nil, err
+		}
+		results = append(results, cr)
+	}
+
+	return btcjson.GetClaimsForNameResult{
+		Hash:           hash,
+		Height:         height,
+		NormalizedName: name,
+		Claims:         results,
+	}, nil
+}
+
+func handleGetClaimsForNameByID(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
+
+	c := cmd.(*btcjson.GetClaimsForNameByIDCmd)
+	hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
+	if err != nil {
+		return nil, err
+	}
+
+	name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
+	if err != nil {
+		return nil, &btcjson.RPCError{
+			Code:    btcjson.ErrRPCMisc,
+			Message: "Message: " + err.Error(),
+		}
+	}
+
+	var results []btcjson.ClaimResult
+	for i := 0; i < len(n.Claims); i++ {
+		for _, id := range c.PartialClaimIDs {
+			if strings.HasPrefix(n.Claims[i].ClaimID.String(), id) {
+				cr, err := toClaimResult(s, int32(i), n, c.IncludeValues)
+				if err != nil {
+					return nil, err
+				}
+				results = append(results, cr)
+				break
+			}
+		}
+	}
+
+	return btcjson.GetClaimsForNameResult{
+		Hash:           hash,
+		Height:         height,
+		NormalizedName: name,
+		Claims:         results,
+	}, nil
+}
+
+func handleGetClaimsForNameByBid(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
+
+	c := cmd.(*btcjson.GetClaimsForNameByBidCmd)
+	hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
+	if err != nil {
+		return nil, err
+	}
+
+	name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
+	if err != nil {
+		return nil, &btcjson.RPCError{
+			Code:    btcjson.ErrRPCMisc,
+			Message: "Message: " + err.Error(),
+		}
+	}
+
+	var results []btcjson.ClaimResult
+	for _, b := range c.Bids { // claims are already sorted in bid order
+		if b >= 0 && int(b) < len(n.Claims) {
+			cr, err := toClaimResult(s, b, n, c.IncludeValues)
+			if err != nil {
+				return nil, err
+			}
+			results = append(results, cr)
+		}
+	}
+
+	return btcjson.GetClaimsForNameResult{
+		Hash:           hash,
+		Height:         height,
+		NormalizedName: name,
+		Claims:         results,
+	}, nil
+}
+
+func handleGetClaimsForNameBySeq(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
+
+	c := cmd.(*btcjson.GetClaimsForNameBySeqCmd)
+	hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
+	if err != nil {
+		return nil, err
+	}
+
+	name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
+	if err != nil {
+		return nil, &btcjson.RPCError{
+			Code:    btcjson.ErrRPCMisc,
+			Message: "Message: " + err.Error(),
+		}
+	}
+
+	sm := map[int32]bool{}
+	for _, seq := range c.Sequences {
+		sm[seq] = true
+	}
+
+	var results []btcjson.ClaimResult
+	for i := 0; i < len(n.Claims); i++ {
+		if sm[n.Claims[i].Sequence] {
+			cr, err := toClaimResult(s, int32(i), n, c.IncludeValues)
+			if err != nil {
+				return nil, err
+			}
+			results = append(results, cr)
+		}
+	}
+
+	return btcjson.GetClaimsForNameResult{
+		Hash:           hash,
+		Height:         height,
+		NormalizedName: name,
+		Claims:         results,
+	}, nil
+}
+
+func toClaimResult(s *rpcServer, i int32, node *node.Node, includeValues *bool) (btcjson.ClaimResult, error) {
+	claim := node.Claims[i]
+	address, value, err := lookupValue(s, claim.OutPoint, includeValues)
+	supports, err := toSupportResults(s, i, node, includeValues)
+	return btcjson.ClaimResult{
+		ClaimID:         claim.ClaimID.String(),
+		Height:          claim.AcceptedAt,
+		ValidAtHeight:   claim.ActiveAt,
+		TXID:            claim.OutPoint.Hash.String(),
+		N:               claim.OutPoint.Index,
+		Bid:             i, // assuming sorted by bid
+		EffectiveAmount: claim.Amount + node.SupportSums[claim.ClaimID.Key()],
+		Sequence:        claim.Sequence,
+		Supports:        supports,
+		Address:         address,
+		Value:           value,
+	}, err
+}
+
+func toSupportResults(s *rpcServer, i int32, n *node.Node, includeValues *bool) ([]btcjson.SupportResult, error) {
+	var results []btcjson.SupportResult
+	c := n.Claims[i]
+	for _, sup := range n.Supports {
+		if sup.Status == node.Activated && c.ClaimID == sup.ClaimID {
+			address, value, err := lookupValue(s, sup.OutPoint, includeValues)
+			if err != nil {
+				return results, err
+			}
+			results = append(results, btcjson.SupportResult{
+				TXID:          sup.OutPoint.Hash.String(),
+				N:             sup.OutPoint.Index,
+				Height:        sup.AcceptedAt,
+				ValidAtHeight: sup.ActiveAt,
+				Amount:        sup.Amount,
+				Value:         value,
+				Address:       address,
+			})
+		}
+	}
+	return results, nil
+}
+
+func lookupValue(s *rpcServer, outpoint wire.OutPoint, includeValues *bool) (string, string, error) {
+	if includeValues == nil || !*includeValues {
+		return "", "", nil
+	}
+	// TODO: maybe use addrIndex if the txIndex is not available
+
+	if s.cfg.TxIndex == nil {
+		return "", "", &btcjson.RPCError{
+			Code: btcjson.ErrRPCNoTxInfo,
+			Message: "The transaction index must be " +
+				"enabled to query the blockchain " +
+				"(specify --txindex)",
+		}
+	}
+
+	txHash := &outpoint.Hash
+	blockRegion, err := s.cfg.TxIndex.TxBlockRegion(txHash)
+	if err != nil {
+		context := "Failed to retrieve transaction location"
+		return "", "", internalRPCError(err.Error(), context)
+	}
+	if blockRegion == nil {
+		return "", "", rpcNoTxInfoError(txHash)
+	}
+
+	// Load the raw transaction bytes from the database.
+	var txBytes []byte
+	err = s.cfg.DB.View(func(dbTx database.Tx) error {
+		var err error
+		txBytes, err = dbTx.FetchBlockRegion(blockRegion)
+		return err
+	})
+	if err != nil {
+		return "", "", rpcNoTxInfoError(txHash)
+	}
+
+	// Deserialize the transaction
+	var msgTx wire.MsgTx
+	err = msgTx.Deserialize(bytes.NewReader(txBytes))
+	if err != nil {
+		context := "Failed to deserialize transaction"
+		return "", "", internalRPCError(err.Error(), context)
+	}
+
+	txo := msgTx.TxOut[outpoint.Index]
+	cs, err := txscript.DecodeClaimScript(txo.PkScript)
+	if err != nil {
+		context := "Failed to decode the claim script"
+		return "", "", internalRPCError(err.Error(), context)
+	}
+
+	_, addresses, _, _ := txscript.ExtractPkScriptAddrs(txo.PkScript[cs.Size():], s.cfg.ChainParams)
+	return addresses[0].EncodeAddress(), hex.EncodeToString(cs.Value()), nil
+}
+
+func handleGetNormalized(_ *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
+	c := cmd.(*btcjson.GetNormalizedCmd)
+	r := btcjson.GetNormalizedResult{
+		NormalizedName: string(normalization.Normalize([]byte(c.Name))),
+	}
+	return r, nil
+}
diff --git a/rpcserverhelp.go b/rpcserverhelp.go
index 654fee01..13301c53 100644
--- a/rpcserverhelp.go
+++ b/rpcserverhelp.go
@@ -245,6 +245,7 @@ var helpDescsEnUS = map[string]string{
 	"getblockverboseresult-version":           "The block version",
 	"getblockverboseresult-versionHex":        "The block version in hexadecimal",
 	"getblockverboseresult-merkleroot":        "Root hash of the merkle tree",
+	"getblockverboseresult-nameclaimroot":     "Root hash of the claim trie",
 	"getblockverboseresult-tx":                "The transaction hashes (only when verbosity=1)",
 	"getblockverboseresult-rawtx":             "The transactions as JSON objects (only when verbosity=2)",
 	"getblockverboseresult-time":              "The block time in seconds since 1 Jan 1970 GMT",
@@ -286,6 +287,7 @@ var helpDescsEnUS = map[string]string{
 	"getblockheaderverboseresult-difficulty":        "The proof-of-work difficulty as a multiple of the minimum difficulty",
 	"getblockheaderverboseresult-previousblockhash": "The hash of the previous block",
 	"getblockheaderverboseresult-nextblockhash":     "The hash of the next block (only if there is one)",
+	"getblockheaderverboseresult-nameclaimroot":     "The hash of the root of the claim trie",
 
 	// TemplateRequest help.
 	"templaterequest-mode":         "This is 'template', 'proposal', or omitted",
@@ -337,6 +339,8 @@ var helpDescsEnUS = map[string]string{
 	"getblocktemplateresult-reject-reason":              "Reason the proposal was invalid as-is (only applies to proposal responses)",
 	"getblocktemplateresult-default_witness_commitment": "The witness commitment itself. Will be populated if the block has witness data",
 	"getblocktemplateresult-weightlimit":                "The current limit on the max allowed weight of a block",
+	"getblocktemplateresult-rules":                      "Rules that are required to process the output",
+	"getblocktemplateresult-claimtrie":                  "The hash of the root of the claim trie - a necessary block header",
 
 	// GetBlockTemplateCmd help.
 	"getblocktemplate--synopsis": "Returns a JSON object with information necessary to construct a block to mine or accepts a proposal to validate.\n" +
@@ -528,7 +532,7 @@ var helpDescsEnUS = map[string]string{
 	"gettxoutresult-coinbase":      "Whether or not the transaction is a coinbase",
 
 	// GetTxOutCmd help.
-	"gettxout--synopsis":      "Returns information about an unspent transaction output..",
+	"gettxout--synopsis":      "Returns information about an unspent transaction output.",
 	"gettxout-txid":           "The hash of the transaction",
 	"gettxout-vout":           "The index of the output",
 	"gettxout-includemempool": "Include the mempool when true",
@@ -706,6 +710,77 @@ var helpDescsEnUS = map[string]string{
 	"versionresult-patch":         "The patch component of the JSON-RPC API version",
 	"versionresult-prerelease":    "Prerelease info about the current build",
 	"versionresult-buildmetadata": "Metadata about the current build",
+
+	"getclaimsforname--synopsis":      "Look up claims for the given name as they stand at a give block",
+	"getclaimsfornamebyid--synopsis":  "Look up claims for the given name as they stand at a give block",
+	"getclaimsfornamebybid--synopsis": "Look up claims for the given name as they stand at a give block",
+	"getclaimsfornamebyseq--synopsis": "Look up claims for the given name as they stand at a give block",
+
+	"getclaimsforname-hashorheight":      "Requested block hash or height; default to tip",
+	"getclaimsfornamebyid-hashorheight":  "Requested block hash or height; default to tip",
+	"getclaimsfornamebybid-hashorheight": "Requested block hash or height; default to tip",
+	"getclaimsfornamebyseq-hashorheight": "Requested block hash or height; default to tip",
+
+	"getclaimsforname-name":      "Requested name for lookup",
+	"getclaimsfornamebyid-name":  "Requested name for lookup",
+	"getclaimsfornamebybid-name": "Requested name for lookup",
+	"getclaimsfornamebyseq-name": "Requested name for lookup",
+
+	"getclaimsfornamebyid-partialclaimids": "Limit the returned claims to those with matching (partial) claimIDs in this list",
+	"getclaimsfornamebybid-bids":           "Limit the returned claims to those with bids to this list",
+	"getclaimsfornamebyseq-sequences":      "Limit the returned claims to those with bids to this list",
+
+	"getclaimsforname-includevalues":      "Return the metadata and address",
+	"getclaimsfornamebyseq-includevalues": "Return the metadata and address",
+	"getclaimsfornamebybid-includevalues": "Return the metadata and address",
+	"getclaimsfornamebyid-includevalues":  "Return the metadata and address",
+
+	"getclaimsfornameresult-claims":         "All the active claims on the given name",
+	"getclaimsfornameresult-normalizedname": "Lower-case version of the passed-in name",
+	"getclaimsfornameresult-height":         "Height of the requested block",
+	"getclaimsfornameresult-hash":           "Hash of the requested block",
+
+	"getchangesinblock--synopsis":    "Returns a list of names affected by a given block",
+	"getchangesinblockresult-names":  "Names that changed (or were at least checked for change) on the given height",
+	"getchangesinblockresult-height": "Height that was requested",
+	"getchangesinblockresult-hash":   "Hash of the block at the height requested",
+
+	"scriptpubkeyresult-subtype": "Claims return Non-standard address types, but they use standard address types internally exposed here",
+
+	"supportresult-value":         "This is the metadata given as part of the support",
+	"supportresult-txid":          "The hash of the transaction",
+	"supportresult-n":             "The output (TXO) index",
+	"supportresult-address":       "The destination address for the support",
+	"supportresult-amount":        "LBC staked",
+	"supportresult-height":        "The height when the stake was created or updated",
+	"supportresult-validatheight": "The height when the stake becomes valid",
+	"claimresult-value":           "This is the metadata given as part of the claim",
+	"claimresult-txid":            "The hash of the transaction",
+	"claimresult-n":               "The output (TXO) index",
+	"claimresult-address":         "The destination address for the claim",
+	"claimresult-supports":        "The list of supports active on the claim",
+	"claimresult-amount":          "LBC staked",
+	"claimresult-validatheight":   "The height when the stake becomes valid",
+	"claimresult-height":          "The height when the stake was created or updated",
+	"claimresult-effectiveamount": "The stake amount plus the active supports' amounts",
+	"claimresult-sequence":        "The order this claim was created compared to other claims on this name",
+	"claimresult-bid":             "Bid of 0 means that this claim currently owns the name",
+	"claimresult-claimid":         "20-byte hash of TXID:N, often used in indexes for the claims",
+
+	"generatetoaddress--synopsis":    "Mine blocks and send their reward to a given address",
+	"generatetoaddress--result0":     "The list of generated blocks' hashes",
+	"generatetoaddress-maxtries":     "The maximum number of hashes to attempt",
+	"generatetoaddress-address":      "The destination -- the place where the LBC will be sent",
+	"generatetoaddress-numblocks":    "The number of blocks to mine",
+	"getchangesinblock-hashorheight": "The requested height or block hash whose changes are of interest",
+
+	"normalize--synopsis": "Used to show how lbcd will normalize a string",
+	"normalize--result0":  "The normalized name",
+	"normalize-name":      "The string to be normalized",
+
+	"getblockverboseresult-getblockverboseresultbase": "",
+	"prevout-issupport": "Previous output created a support",
+	"prevout-isclaim":   "Previous output created or updated a claim",
 }
 
 // rpcResultTypes specifies the result types that each RPC command can return.
@@ -774,6 +849,14 @@ var rpcResultTypes = map[string][]interface{}{
 	"stopnotifyspent":           nil,
 	"rescan":                    nil,
 	"rescanblocks":              {(*[]btcjson.RescannedBlock)(nil)},
+
+	// ClaimTrie
+	"getclaimsforname":      {(*btcjson.GetClaimsForNameResult)(nil)},
+	"getclaimsfornamebyid":  {(*btcjson.GetClaimsForNameResult)(nil)},
+	"getclaimsfornamebybid": {(*btcjson.GetClaimsForNameResult)(nil)},
+	"getclaimsfornamebyseq": {(*btcjson.GetClaimsForNameResult)(nil)},
+	"normalize":             {(*string)(nil)},
+	"getchangesinblock":     {(*btcjson.GetChangesInBlockResult)(nil)},
 }
 
 // helpCacher provides a concurrent safe type that provides help and usage for