[lbry] rpc: import invalidate/reconsiderblock from bchd

This commit is contained in:
Brannon King 2021-10-28 16:29:03 -04:00 committed by Roy Lee
parent 81862c664e
commit 73d8f4762f
4 changed files with 165 additions and 16 deletions

View file

@ -1642,6 +1642,115 @@ func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Has
return headers return headers
} }
// InvalidateBlock takes a block hash and invalidates it.
//
// This function is safe for concurrent access.
func (b *BlockChain) InvalidateBlock(hash *chainhash.Hash) error {
return b.invalidateBlock(hash)
}
// invalidateBlock takes a block hash and invalidates it.
func (b *BlockChain) invalidateBlock(hash *chainhash.Hash) error {
node := b.index.LookupNode(hash)
if node == nil {
err := fmt.Errorf("block %s is not known", hash)
return err
}
// No need to invalidate if its already invalid.
if node.status.KnownInvalid() {
err := fmt.Errorf("block %s is already invalid", hash)
return err
}
if node.parent == nil {
err := fmt.Errorf("block %s has no parent", hash)
return err
}
b.index.SetStatusFlags(node, statusValidateFailed)
b.index.UnsetStatusFlags(node, statusValid)
b.chainLock.Lock()
defer b.chainLock.Unlock()
detachNodes, attachNodes := b.getReorganizeNodes(node.parent)
err := b.reorganizeChain(detachNodes, attachNodes)
if err != nil {
return err
}
for i, e := 0, detachNodes.Front(); e != nil; i, e = i+1, e.Next() {
n := e.Value.(*blockNode)
b.index.SetStatusFlags(n, statusInvalidAncestor)
b.index.UnsetStatusFlags(n, statusValid)
}
if writeErr := b.index.flushToDB(); writeErr != nil {
log.Warnf("Error flushing block index changes to disk: %v", writeErr)
}
return nil
}
// ReconsiderBlock takes a block hash and allows it to be revalidated.
//
// This function is safe for concurrent access.
func (b *BlockChain) ReconsiderBlock(hash *chainhash.Hash) error {
return b.reconsiderBlock(hash)
}
// reconsiderBlock takes a block hash and allows it to be revalidated.
func (b *BlockChain) reconsiderBlock(hash *chainhash.Hash) error {
node := b.index.LookupNode(hash)
if node == nil {
err := fmt.Errorf("block %s is not known", hash)
return err
}
// No need to reconsider, it is already valid.
if node.status.KnownValid() {
err := fmt.Errorf("block %s is already valid", hash)
return err
}
// Keep a reference to the first node in the chain of invalid
// blocks so we can reprocess after status flags are updated.
firstNode := node
// Find previous node to the point where the blocks are valid again.
for n := node; n.status.KnownInvalid(); n = n.parent {
b.index.UnsetStatusFlags(n, statusInvalidAncestor)
b.index.UnsetStatusFlags(n, statusValidateFailed)
firstNode = n
}
var blk *btcutil.Block
err := b.db.View(func(dbTx database.Tx) error {
var err error
blk, err = dbFetchBlockByNode(dbTx, firstNode)
return err
})
if err != nil {
return err
}
// Process it all again. This will take care of the
// orphans as well.
_, _, err = b.ProcessBlock(blk, BFNoDupBlockCheck)
if err != nil {
return err
}
if writeErr := b.index.flushToDB(); writeErr != nil {
log.Warnf("Error flushing block index changes to disk: %v", writeErr)
}
return nil
}
// ClaimTrie returns the claimTrie associated wit hthe chain. // ClaimTrie returns the claimTrie associated wit hthe chain.
func (b *BlockChain) ClaimTrie() *claimtrie.ClaimTrie { func (b *BlockChain) ClaimTrie() *claimtrie.ClaimTrie {
return b.claimTrie return b.claimTrie

View file

@ -29,6 +29,10 @@ const (
// not be performed. // not be performed.
BFNoPoWCheck BFNoPoWCheck
// BFNoDupBlockCheck signals if the block should skip existence
// checks.
BFNoDupBlockCheck
// BFNone is a convenience value to specifically indicate no flags. // BFNone is a convenience value to specifically indicate no flags.
BFNone BehaviorFlags = 0 BFNone BehaviorFlags = 0
) )
@ -148,24 +152,26 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo
blockHash := block.Hash() blockHash := block.Hash()
log.Tracef("Processing block %v", blockHash) log.Tracef("Processing block %v", blockHash)
// The block must not already exist in the main chain or side chains. if flags&BFNoDupBlockCheck != BFNoDupBlockCheck {
exists, err := b.blockExists(blockHash) // The block must not already exist in the main chain or side chains.
if err != nil { exists, err := b.blockExists(blockHash)
return false, false, err if err != nil {
} return false, false, err
if exists { }
str := fmt.Sprintf("already have block %v", blockHash) if exists {
return false, false, ruleError(ErrDuplicateBlock, str) str := fmt.Sprintf("already have block %v", blockHash)
} return false, false, ruleError(ErrDuplicateBlock, str)
}
// The block must not already exist as an orphan. // The block must not already exist as an orphan.
if _, exists := b.orphans[*blockHash]; exists { if _, exists := b.orphans[*blockHash]; exists {
str := fmt.Sprintf("already have block (orphan) %v", blockHash) str := fmt.Sprintf("already have block (orphan) %v", blockHash)
return false, false, ruleError(ErrDuplicateBlock, str) return false, false, ruleError(ErrDuplicateBlock, str)
}
} }
// Perform preliminary sanity checks on the block and its transactions. // Perform preliminary sanity checks on the block and its transactions.
err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags) err := checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags)
if err != nil { if err != nil {
return false, false, err return false, false, err
} }

View file

@ -168,8 +168,10 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
"getrawtransaction": handleGetRawTransaction, "getrawtransaction": handleGetRawTransaction,
"gettxout": handleGetTxOut, "gettxout": handleGetTxOut,
"help": handleHelp, "help": handleHelp,
"invalidateblock": handleInvalidateBlock,
"node": handleNode, "node": handleNode,
"ping": handlePing, "ping": handlePing,
"reconsiderblock": handleReconsiderBlock,
"searchrawtransactions": handleSearchRawTransactions, "searchrawtransactions": handleSearchRawTransactions,
"sendrawtransaction": handleSendRawTransaction, "sendrawtransaction": handleSendRawTransaction,
"setgenerate": handleSetGenerate, "setgenerate": handleSetGenerate,
@ -237,9 +239,7 @@ var rpcUnimplemented = map[string]struct{}{
"getchaintips": {}, "getchaintips": {},
"getmempoolentry": {}, "getmempoolentry": {},
"getwork": {}, "getwork": {},
"invalidateblock": {},
"preciousblock": {}, "preciousblock": {},
"reconsiderblock": {},
} }
// Commands that are available to a limited user // Commands that are available to a limited user
@ -2977,6 +2977,30 @@ func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
return txOutReply, nil return txOutReply, nil
} }
// handleInvalidateBlock implements the invalidateblock command
func handleInvalidateBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.InvalidateBlockCmd)
hash, err := chainhash.NewHashFromStr(c.BlockHash)
if err != nil {
return nil, err
}
return nil, s.cfg.Chain.InvalidateBlock(hash)
}
// handleReconsiderBlock implements the reconsiderblock command
func handleReconsiderBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.ReconsiderBlockCmd)
hash, err := chainhash.NewHashFromStr(c.BlockHash)
if err != nil {
return nil, err
}
return nil, s.cfg.Chain.ReconsiderBlock(hash)
}
// handleHelp implements the help command. // handleHelp implements the help command.
func handleHelp(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { func handleHelp(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.HelpCmd) c := cmd.(*btcjson.HelpCmd)

View file

@ -568,10 +568,18 @@ var helpDescsEnUS = map[string]string{
"help--result0": "List of commands", "help--result0": "List of commands",
"help--result1": "Help for specified command", "help--result1": "Help for specified command",
// InvalidateBlockCmd
"invalidateblock--synopsis": "Invalidate a block.",
"invalidateblock-blockhash": "Hash of the block you want to invalidate",
// PingCmd help. // PingCmd help.
"ping--synopsis": "Queues a ping to be sent to each connected peer.\n" + "ping--synopsis": "Queues a ping to be sent to each connected peer.\n" +
"Ping times are provided by getpeerinfo via the pingtime and pingwait fields.", "Ping times are provided by getpeerinfo via the pingtime and pingwait fields.",
// ReconsiderBlockCmd
"reconsiderblock--synopsis": "Reconsider a block for validation.",
"reconsiderblock-blockhash": "Hash of the block you want to reconsider",
// SearchRawTransactionsCmd help. // SearchRawTransactionsCmd help.
"searchrawtransactions--synopsis": "Returns raw data for transactions involving the passed address.\n" + "searchrawtransactions--synopsis": "Returns raw data for transactions involving the passed address.\n" +
"Returned transactions are pulled from both the database, and transactions currently in the mempool.\n" + "Returned transactions are pulled from both the database, and transactions currently in the mempool.\n" +
@ -848,7 +856,9 @@ var rpcResultTypes = map[string][]interface{}{
"gettxout": {(*btcjson.GetTxOutResult)(nil)}, "gettxout": {(*btcjson.GetTxOutResult)(nil)},
"node": nil, "node": nil,
"help": {(*string)(nil), (*string)(nil)}, "help": {(*string)(nil), (*string)(nil)},
"invalidateblock": nil,
"ping": nil, "ping": nil,
"reconsiderblock": nil,
"searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)}, "searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)},
"sendrawtransaction": {(*string)(nil)}, "sendrawtransaction": {(*string)(nil)},
"setgenerate": nil, "setgenerate": nil,