diff --git a/btcjson/chainsvrcmds.go b/btcjson/chainsvrcmds.go index 22552e7bcd..a9beee1062 100644 --- a/btcjson/chainsvrcmds.go +++ b/btcjson/chainsvrcmds.go @@ -225,6 +225,15 @@ func NewGetBlockChainInfoCmd() *GetBlockChainInfoCmd { return &GetBlockChainInfoCmd{} } +// GetIndexInfoCmd defines the getindexinfo JSON-RPC command. +type GetIndexInfoCmd struct{} + +// NewGetIndexInfoCmd returns a new instance which can be used to issue a +// getindexinfo JSON-RPC command. +func NewGetIndexInfoCmd() *GetIndexInfoCmd { + return &GetIndexInfoCmd{} +} + // GetBlockCountCmd defines the getblockcount JSON-RPC command. type GetBlockCountCmd struct{} @@ -1131,6 +1140,7 @@ func init() { MustRegisterCmd("getgenerate", (*GetGenerateCmd)(nil), flags) MustRegisterCmd("gethashespersec", (*GetHashesPerSecCmd)(nil), flags) MustRegisterCmd("getinfo", (*GetInfoCmd)(nil), flags) + MustRegisterCmd("getindexinfo", (*GetIndexInfoCmd)(nil), flags) MustRegisterCmd("getmempoolentry", (*GetMempoolEntryCmd)(nil), flags) MustRegisterCmd("getmempoolinfo", (*GetMempoolInfoCmd)(nil), flags) MustRegisterCmd("getmininginfo", (*GetMiningInfoCmd)(nil), flags) diff --git a/btcjson/chainsvrcmds_test.go b/btcjson/chainsvrcmds_test.go index 38113a687e..8dcde3a36e 100644 --- a/btcjson/chainsvrcmds_test.go +++ b/btcjson/chainsvrcmds_test.go @@ -369,6 +369,17 @@ func TestChainSvrCmds(t *testing.T) { marshalled: `{"jsonrpc":"1.0","method":"getblockchaininfo","params":[],"id":1}`, unmarshalled: &btcjson.GetBlockChainInfoCmd{}, }, + { + name: "getindexinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getindexinfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetIndexInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getindexinfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetIndexInfoCmd{}, + }, { name: "getblockcount", newCmd: func() (interface{}, error) { diff --git a/btcjson/chainsvrresults.go b/btcjson/chainsvrresults.go index 433bdda8eb..828435151a 100644 --- a/btcjson/chainsvrresults.go +++ b/btcjson/chainsvrresults.go @@ -242,6 +242,17 @@ type GetBlockChainInfoResult struct { *UnifiedSoftForks } +// GetIndexInfoResult models the data returned from the getindexinfo command. +type GetIndexInfoResult struct { + *TxIndex `json:"txindex,omitempty"` +} + +// TxIndex describes the current txindex results from getindexinfo. +type TxIndex struct { + Synced bool `json:"synced"` + BestBlockHeight int32 `json:"best_block_height"` +} + // GetBlockFilterResult models the data returned from the getblockfilter // command. type GetBlockFilterResult struct { diff --git a/rpcclient/chain.go b/rpcclient/chain.go index c8562b8e65..3606423c72 100644 --- a/rpcclient/chain.go +++ b/rpcclient/chain.go @@ -507,6 +507,54 @@ func (c *Client) GetBlockChainInfo() (*btcjson.GetBlockChainInfoResult, error) { return c.GetBlockChainInfoAsync().Receive() } +// FutureGetIndexInfoResult is a promise to deliver the result of a +// GetIndexInfoAsync RPC invocation (or an applicable error). +type FutureGetIndexInfoResult struct { + client *Client + Response chan *Response +} + +// unmarshalPartialGetIndexInfoResult unmarshals the response into an +// instance of GetIndexInfoResult. +func unmarshalPartialGetIndexInfoResult(res []byte) (*btcjson.GetIndexInfoResult, error) { + var indexInfo btcjson.GetIndexInfoResult + if err := json.Unmarshal(res, &indexInfo); err != nil { + return nil, err + } + return &indexInfo, nil +} + +// Receive waits for the Response promised by the future and returns txindex info +// result provided by the server. +func (r FutureGetIndexInfoResult) Receive() (*btcjson.GetIndexInfoResult, error) { + res, err := ReceiveFuture(r.Response) + if err != nil { + return nil, err + } + indexInfo, err := unmarshalPartialGetIndexInfoResult(res) + if err != nil { + return nil, err + } + + return indexInfo, nil +} + +// GetIndexInfoAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function +// on the returned instance. +func (c *Client) GetIndexInfoAsync() FutureGetIndexInfoResult { + cmd := btcjson.NewGetIndexInfoCmd() + return FutureGetIndexInfoResult{ + client: c, + Response: c.SendCmd(cmd), + } +} + +// GetIndexInfo returns information related to the txindex state. +func (c *Client) GetIndexInfo() (*btcjson.GetIndexInfoResult, error) { + return c.GetIndexInfoAsync().Receive() +} + // FutureGetBlockFilterResult is a future promise to deliver the result of a // GetBlockFilterAsync RPC invocation (or an applicable error). type FutureGetBlockFilterResult chan *Response diff --git a/rpcclient/chain_test.go b/rpcclient/chain_test.go index ad1fb7aa2a..17e3081a84 100644 --- a/rpcclient/chain_test.go +++ b/rpcclient/chain_test.go @@ -103,6 +103,61 @@ func TestUnmarshalGetBlockChainInfoResultSoftForks(t *testing.T) { } } +// TestUnmarshalGetIndexInfoResult ensures that the Txindex of GetIndexInfoResult +// are properly unmarshaled. +func TestUnmarshalGetIndexInfoResult(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + res []byte + enabled bool + blockHeight int32 + }{ + { + name: "txindex synced and best block height valid", + res: []byte(`{"txindex": {"synced": true, "best_block_height": 3522710}}`), + enabled: true, + blockHeight: 3522710, + }, + { + name: "txindex not enabled", + res: []byte(`{}`), + enabled: false, + blockHeight: 0, + }, + } + + for _, test := range tests { + success := t.Run(test.name, func(t *testing.T) { + // We'll start by unmarshalling the JSON into a struct. + info, err := unmarshalPartialGetIndexInfoResult(test.res) + if err != nil { + t.Fatal(err) + } + + if test.enabled { + if info.TxIndex == nil { + t.Fatalf("unable to unmarshal txindex: %v", err) + } + if info.TxIndex.Synced == false { + t.Fatalf("expected TxIndex.Synced to be true") + } + if info.TxIndex.BestBlockHeight != test.blockHeight { + t.Fatalf("expected TxIndex.BestBlockHeight to be equal") + } + } else { + if info.TxIndex != nil { + t.Fatalf("expected TxIndex to be empty") + } + } + }) + if !success { + return + } + } +} + func TestFutureGetBlockCountResultReceiveErrors(t *testing.T) { responseChan := FutureGetBlockCountResult(make(chan *Response)) response := Response{