diff --git a/rpcclient/chain.go b/rpcclient/chain.go index 1d0a8be2..c8562b8e 100644 --- a/rpcclient/chain.go +++ b/rpcclient/chain.go @@ -1419,3 +1419,38 @@ func (c *Client) GetDescriptorInfoAsync(descriptor string) FutureGetDescriptorIn func (c *Client) GetDescriptorInfo(descriptor string) (*btcjson.GetDescriptorInfoResult, error) { return c.GetDescriptorInfoAsync(descriptor).Receive() } + +// FutureReconsiderBlockResult is a future promise to deliver the result of a +// ReconsiderBlockAsync RPC invocation (or an applicable error). +type FutureReconsiderBlockResult chan *Response + +// Receive waits for the Response promised by the future and returns the raw +// block requested from the server given its hash. +func (r FutureReconsiderBlockResult) Receive() error { + _, err := ReceiveFuture(r) + return err +} + +// ReconsiderBlockAsync 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. +// +// See ReconsiderBlock for the blocking version and more details. +func (c *Client) ReconsiderBlockAsync( + blockHash *chainhash.Hash) FutureReconsiderBlockResult { + + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + + cmd := btcjson.NewReconsiderBlockCmd(hash) + return c.SendCmd(cmd) +} + +// ReconsiderBlock reconsiders an verifies a specific block and the branch that +// the block is included in. If the block is valid on reconsideration, the chain +// will reorg to that block if it has more PoW than the current tip. +func (c *Client) ReconsiderBlock(blockHash *chainhash.Hash) error { + return c.ReconsiderBlockAsync(blockHash).Receive() +} diff --git a/rpcserver.go b/rpcserver.go index f82b95ca..a3c4062b 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -170,8 +170,10 @@ var rpcHandlersBeforeInit = map[string]commandHandler{ "getrawtransaction": handleGetRawTransaction, "gettxout": handleGetTxOut, "help": handleHelp, + "invalidateblock": handleInvalidateBlock, "node": handleNode, "ping": handlePing, + "reconsiderblock": handleReconsiderBlock, "searchrawtransactions": handleSearchRawTransactions, "sendrawtransaction": handleSendRawTransaction, "setgenerate": handleSetGenerate, @@ -241,9 +243,7 @@ var rpcUnimplemented = map[string]struct{}{ "getmempoolentry": {}, "getnetworkinfo": {}, "getwork": {}, - "invalidateblock": {}, "preciousblock": {}, - "reconsiderblock": {}, } // Commands that are available to a limited user @@ -284,6 +284,8 @@ var rpcLimited = map[string]struct{}{ "getrawmempool": {}, "getrawtransaction": {}, "gettxout": {}, + "invalidateblock": {}, + "reconsiderblock": {}, "searchrawtransactions": {}, "sendrawtransaction": {}, "submitblock": {}, @@ -2850,6 +2852,23 @@ func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i return txOutReply, nil } +// handleInvalidateBlock implements the invalidateblock command. +func handleInvalidateBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.InvalidateBlockCmd) + + invalidateHash, err := chainhash.NewHashFromStr(c.BlockHash) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: fmt.Sprintf("Failed to deserialize blockhash from string of %s", + invalidateHash), + } + } + + err = s.cfg.Chain.InvalidateBlock(invalidateHash) + return nil, err +} + // handleHelp implements the help command. func handleHelp(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.HelpCmd) @@ -3123,6 +3142,23 @@ func fetchMempoolTxnsForAddress(s *rpcServer, addr btcutil.Address, numToSkip, n return mpTxns[numToSkip:rangeEnd], numToSkip } +// handleReconsiderBlock implements the reconsiderblock command. +func handleReconsiderBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.ReconsiderBlockCmd) + + reconsiderHash, err := chainhash.NewHashFromStr(c.BlockHash) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: fmt.Sprintf("Failed to deserialize blockhash from string of %s", + reconsiderHash), + } + } + + err = s.cfg.Chain.ReconsiderBlock(reconsiderHash) + return nil, err +} + // handleSearchRawTransactions implements the searchrawtransactions command. func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Respond with an error if the address index is not enabled. diff --git a/rpcserverhelp.go b/rpcserverhelp.go index 1f8451a5..71f96e99 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -544,6 +544,10 @@ var helpDescsEnUS = map[string]string{ "gettxout-vout": "The index of the output", "gettxout-includemempool": "Include the mempool when true", + // InvalidateBlockCmd help. + "invalidateblock--synopsis": "Invalidates the block of the given block hash. To re-validate the invalidated block, use the reconsiderblock rpc", + "invalidateblock-blockhash": "The block hash of the block to invalidate", + // HelpCmd help. "help--synopsis": "Returns a list of all commands or help for a specified command.", "help-command": "The command to retrieve help for", @@ -681,6 +685,10 @@ var helpDescsEnUS = map[string]string{ "loadtxfilter-addresses": "Array of addresses to add to the transaction filter", "loadtxfilter-outpoints": "Array of outpoints to add to the transaction filter", + // ReconsiderBlockCmd help. + "reconsiderblock--synopsis": "Reconsiders the block of the given block hash. Can be used to re-validate blocks invalidated with invalidateblock", + "reconsiderblock-blockhash": "The block hash of the block to reconsider", + // Rescan help. "rescan--synopsis": "Rescan block chain for transactions to addresses.\n" + "When the endblock parameter is omitted, the rescan continues through the best block in the main chain.\n" + @@ -788,7 +796,9 @@ var rpcResultTypes = map[string][]interface{}{ "gettxout": {(*btcjson.GetTxOutResult)(nil)}, "node": nil, "help": {(*string)(nil), (*string)(nil)}, + "invalidateblock": nil, "ping": nil, + "reconsiderblock": nil, "searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)}, "sendrawtransaction": {(*string)(nil)}, "setgenerate": nil,