From 6834591d526b89fe58e79416697f5dae91d21f47 Mon Sep 17 00:00:00 2001 From: Brannon King 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 | 97 +++++++++ btcjson/help.go | 6 + btcjson/jsonrpc.go | 8 +- rpcclaimtrie.go | 352 ++++++++++++++++++++++++++++++++ rpcserverhelp.go | 84 ++++++++ 7 files changed, 553 insertions(+), 3 deletions(-) create mode 100644 btcjson/claimcmds.go create mode 100644 rpcclaimtrie.go diff --git a/btcjson/chainsvrresults.go b/btcjson/chainsvrresults.go index e658cccf..e82a14bc 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 bb04a003..cbdca095 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..8f50fc0c --- /dev/null +++ b/btcjson/claimcmds.go @@ -0,0 +1,97 @@ +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"` + LastTakeoverHeight int32 `json:"lasttakeoverheight"` + NormalizedName string `json:"normalizedname"` + Claims []ClaimResult `json:"claims"` + // UnclaimedSupports []SupportResult `json:"supportswithoutclaim"` 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"` + Amount int64 `json:"amount"` + 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..11706920 --- /dev/null +++ b/rpcclaimtrie.go @@ -0,0 +1,352 @@ +package main + +import ( + "bytes" + "encoding/hex" + "strconv" + "strings" + + "github.com/lbryio/lbcd/btcjson" + "github.com/lbryio/lbcd/chaincfg/chainhash" + "github.com/lbryio/lbcd/claimtrie/node" + "github.com/lbryio/lbcd/claimtrie/normalization" + "github.com/lbryio/lbcd/database" + "github.com/lbryio/lbcd/txscript" + "github.com/lbryio/lbcd/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, + LastTakeoverHeight: n.TakenOverAt, + 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, + LastTakeoverHeight: n.TakenOverAt, + 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, + LastTakeoverHeight: n.TakenOverAt, + 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, + LastTakeoverHeight: n.TakenOverAt, + NormalizedName: name, + Claims: results, + }, nil +} + +func toClaimResult(s *rpcServer, i int32, n *node.Node, includeValues *bool) (btcjson.ClaimResult, error) { + claim := n.Claims[i] + address, value, err := lookupValue(s, claim.OutPoint, includeValues) + supports, err := toSupportResults(s, i, n, includeValues) + effectiveAmount := n.SupportSums[claim.ClaimID.Key()] // should only be active supports + if claim.Status == node.Activated { + effectiveAmount += claim.Amount + } + 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 + Amount: claim.Amount, + EffectiveAmount: effectiveAmount, + 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.ExtractClaimScript(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 e8f7a640..1ab92644 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -247,6 +247,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", @@ -288,6 +289,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", @@ -339,6 +341,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" + @@ -708,6 +712,78 @@ 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-lasttakeoverheight": "Height of the most recent name takeover", + "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-validatheight": "The height when the stake becomes valid", + "claimresult-height": "The height when the stake was created or updated", + "claimresult-amount": "The stake amount in sats", + "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. @@ -776,6 +852,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