From 50c30a53155774f2d3128fd4cb221d5572b7c35b Mon Sep 17 00:00:00 2001 From: Lazy Nina Date: Thu, 24 Oct 2024 14:32:02 -0400 Subject: [PATCH] reduce calls to Hash for blocks --- lib/block_view_lockups_test.go | 4 +- lib/blockchain.go | 27 ++++-- lib/blockchain_test.go | 16 +-- lib/db_utils.go | 43 +++++---- lib/load_test.go | 4 +- lib/miner.go | 2 +- lib/pos_blockchain.go | 168 +++++++++++++++++--------------- lib/pos_blockchain_test.go | 171 ++++++++++++++++++--------------- lib/pos_consensus.go | 21 +++- lib/server.go | 6 +- lib/txindex.go | 2 +- 11 files changed, 260 insertions(+), 204 deletions(-) diff --git a/lib/block_view_lockups_test.go b/lib/block_view_lockups_test.go index 6d04aac60..c5458eb32 100644 --- a/lib/block_view_lockups_test.go +++ b/lib/block_view_lockups_test.go @@ -2353,7 +2353,7 @@ func TestLockupBlockConnectsAndDisconnects(t *testing.T) { // Process the first block err = testMeta.miner.BlockProducer.SignBlock(blk1) require.NoError(t, err) - _, _, _, err = testMeta.chain.ProcessBlock(blk1, false) + _, _, _, err = testMeta.chain.ProcessBlock(blk1, nil, false) require.NoError(t, err) // Validate state update @@ -2417,7 +2417,7 @@ func TestLockupBlockConnectsAndDisconnects(t *testing.T) { // Process the second block err = testMeta.miner.BlockProducer.SignBlock(blk2) require.NoError(t, err) - _, _, _, err = testMeta.chain.ProcessBlock(blk2, false) + _, _, _, err = testMeta.chain.ProcessBlock(blk2, nil, false) require.NoError(t, err) // Validate state update diff --git a/lib/blockchain.go b/lib/blockchain.go index 706e1d946..14e769563 100644 --- a/lib/blockchain.go +++ b/lib/blockchain.go @@ -2204,13 +2204,17 @@ func (bc *Blockchain) ProcessHeader(blockHeader *MsgDeSoHeader, headerHash *Bloc // If the header's height is after the PoS cut-over fork height, then we use the PoS header processing logic. // Otherwise, fall back to the PoW logic. if bc.params.IsPoSBlockHeight(blockHeader.Height) { - return bc.processHeaderPoS(blockHeader, verifySignatures) + return bc.processHeaderPoS(blockHeader, headerHash, verifySignatures) } return bc.processHeaderPoW(blockHeader, headerHash) } -func (bc *Blockchain) ProcessBlock(desoBlock *MsgDeSoBlock, verifySignatures bool) (_isMainChain bool, _isOrphan bool, _missingBlockHashes []*BlockHash, _err error) { +func (bc *Blockchain) ProcessBlock( + desoBlock *MsgDeSoBlock, + hash *BlockHash, // hash is not required and will be computed if not provided, but speeds things up if provided. + verifySignatures bool, +) (_isMainChain bool, _isOrphan bool, _missingBlockHashes []*BlockHash, _err error) { bc.ChainLock.Lock() defer bc.ChainLock.Unlock() @@ -2222,14 +2226,14 @@ func (bc *Blockchain) ProcessBlock(desoBlock *MsgDeSoBlock, verifySignatures boo // If the block's height is after the PoS cut-over fork height, then we use the PoS block processing logic. // Otherwise, fall back to the PoW logic. if bc.params.IsPoSBlockHeight(desoBlock.Header.Height) { - return bc.processBlockPoS(desoBlock, 1, verifySignatures) + return bc.processBlockPoS(desoBlock, hash, 1, verifySignatures) } - isMainChain, isOrphan, err := bc.processBlockPoW(desoBlock, verifySignatures) + isMainChain, isOrphan, err := bc.processBlockPoW(desoBlock, hash, verifySignatures) return isMainChain, isOrphan, nil, err } -func (bc *Blockchain) processBlockPoW(desoBlock *MsgDeSoBlock, verifySignatures bool) (_isMainChain bool, _isOrphan bool, err error) { +func (bc *Blockchain) processBlockPoW(desoBlock *MsgDeSoBlock, blockHash *BlockHash, verifySignatures bool) (_isMainChain bool, _isOrphan bool, err error) { // Only accept the block if its height is below the PoS cutover height. if !bc.params.IsPoWBlockHeight(desoBlock.Header.Height) { return false, false, RuleErrorBlockHeightAfterProofOfStakeCutover @@ -2243,10 +2247,13 @@ func (bc *Blockchain) processBlockPoW(desoBlock *MsgDeSoBlock, verifySignatures if blockHeader == nil { return false, false, fmt.Errorf("ProcessBlock: Block header was nil") } - blockHash, err := blockHeader.Hash() - if err != nil { - return false, false, errors.Wrapf(err, "ProcessBlock: Problem computing block hash") + if blockHash == nil { + blockHash, err = blockHeader.Hash() + if err != nil { + return false, false, errors.Wrapf(err, "ProcessBlock: Problem computing block hash") + } } + // If a trusted block producer public key is set, then we only accept blocks // if they have been signed by one of these public keys. if len(bc.trustedBlockProducerPublicKeys) > 0 { @@ -2474,7 +2481,7 @@ func (bc *Blockchain) processBlockPoW(desoBlock *MsgDeSoBlock, verifySignatures // This is needed for disconnects, otherwise GetBlock() will fail (e.g. when we reorg). if err == nil { err = bc.db.Update(func(txn *badger.Txn) error { - if err := PutBlockWithTxn(txn, nil, desoBlock, bc.eventManager); err != nil { + if err := PutBlockWithTxn(txn, nil, desoBlock, blockHash, bc.eventManager); err != nil { return errors.Wrapf(err, "ProcessBlock: Problem putting block with txns") } return nil @@ -2493,7 +2500,7 @@ func (bc *Blockchain) processBlockPoW(desoBlock *MsgDeSoBlock, verifySignatures // set in PutBlockWithTxn. Block rewards are part of the state, and they should be identical to the ones // we've fetched during Hypersync. Is there an edge-case where for some reason they're not identical? Or // somehow ancestral records get corrupted? - if innerErr := PutBlockWithTxn(txn, bc.snapshot, desoBlock, bc.eventManager); innerErr != nil { + if innerErr := PutBlockWithTxn(txn, bc.snapshot, desoBlock, blockHash, bc.eventManager); innerErr != nil { return errors.Wrapf(err, "ProcessBlock: Problem calling PutBlock") } diff --git a/lib/blockchain_test.go b/lib/blockchain_test.go index d18eb6091..658d85fca 100644 --- a/lib/blockchain_test.go +++ b/lib/blockchain_test.go @@ -618,7 +618,7 @@ func TestBasicTransferReorg(t *testing.T) { // Process all of the fork blocks on the original chain to make it // experience a reorg. for _, forkBlock := range forkBlocks { - _, _, _, err := chain1.ProcessBlock(forkBlock, true /*verifySignatures*/) + _, _, _, err := chain1.ProcessBlock(forkBlock, nil, true /*verifySignatures*/) require.NoError(err) } @@ -661,7 +661,7 @@ func _shouldConnectBlock(blk *MsgDeSoBlock, t *testing.T, chain *Blockchain) { blockHash, _ := blk.Hash() verifySignatures := true - isMainChain, isOrphan, _, err := chain.ProcessBlock(blk, verifySignatures) + isMainChain, isOrphan, _, err := chain.ProcessBlock(blk, blockHash, verifySignatures) require.NoError(err) require.Falsef(isOrphan, "Block %v should not be an orphan", blockHash) require.Truef(isMainChain, "Block %v should be on the main chain", blockHash) @@ -826,7 +826,7 @@ func TestProcessBlockReorgBlocks(t *testing.T) { // Block b1 fmt.Println("Connecting block b1") require.Equal(uint64(3), GetUtxoNumEntries(db, chain.snapshot)) - isMainChain, isOrphan, _, err := chain.ProcessBlock(blockB1, verifySignatures) + isMainChain, isOrphan, _, err := chain.ProcessBlock(blockB1, nil, verifySignatures) require.NoError(err) require.Falsef(isOrphan, "Block b1 should not be an orphan") require.Falsef(isMainChain, "Block b1 should not be on the main chain") @@ -842,7 +842,7 @@ func TestProcessBlockReorgBlocks(t *testing.T) { // Block b2 fmt.Println("Connecting block b2") require.Equal(uint64(3), GetUtxoNumEntries(db, chain.snapshot)) - isMainChain, isOrphan, _, err := chain.ProcessBlock(blockB2, verifySignatures) + isMainChain, isOrphan, _, err := chain.ProcessBlock(blockB2, nil, verifySignatures) require.NoError(err) require.Falsef(isOrphan, "Block b2 should not be an orphan") require.Falsef(isMainChain, "Block b2 should not be on the main chain") @@ -1672,7 +1672,7 @@ func TestBadBlockSignature(t *testing.T) { // A bad signature with the right public key should fail. finalBlock1.BlockProducerInfo.PublicKey = senderPkBytes - _, _, _, err = chain.ProcessBlock(finalBlock1, true) + _, _, _, err = chain.ProcessBlock(finalBlock1, nil, true) require.Error(err) require.Contains(err.Error(), RuleErrorInvalidBlockProducerSIgnature) @@ -1681,20 +1681,20 @@ func TestBadBlockSignature(t *testing.T) { require.NoError(err) finalBlock1.BlockProducerInfo.PublicKey = blockSignerPkBytes finalBlock1.BlockProducerInfo.Signature = nil - _, _, _, err = chain.ProcessBlock(finalBlock1, true) + _, _, _, err = chain.ProcessBlock(finalBlock1, nil, true) require.Error(err) require.Contains(err.Error(), RuleErrorMissingBlockProducerSignature) // If all the BlockProducerInfo is missing, things should fail finalBlock1.BlockProducerInfo = nil - _, _, _, err = chain.ProcessBlock(finalBlock1, true) + _, _, _, err = chain.ProcessBlock(finalBlock1, nil, true) require.Error(err) require.Contains(err.Error(), RuleErrorMissingBlockProducerSignature) // Now let's add blockSignerPK to the map of trusted keys and confirm that the block processes. chain.trustedBlockProducerPublicKeys[MakePkMapKey(blockSignerPkBytes)] = true finalBlock1.BlockProducerInfo = blockProducerInfoCopy - _, _, _, err = chain.ProcessBlock(finalBlock1, true) + _, _, _, err = chain.ProcessBlock(finalBlock1, nil, true) require.NoError(err) _, _ = finalBlock1, db diff --git a/lib/db_utils.go b/lib/db_utils.go index 68f63ce61..159258eac 100644 --- a/lib/db_utils.go +++ b/lib/db_utils.go @@ -5044,14 +5044,13 @@ func GetBlock(blockHash *BlockHash, handle *badger.DB, snap *Snapshot) (*MsgDeSo return blockRet, nil } -func PutBlockHashToBlockWithTxn(txn *badger.Txn, snap *Snapshot, block *MsgDeSoBlock, eventManager *EventManager) error { - if block.Header == nil { - return fmt.Errorf("PutBlockHashToBlockWithTxn: Header was nil in block %v", block) - } - blockHash, err := block.Header.Hash() - if err != nil { - return errors.Wrap(err, "PutBlockHashToBlockWithTxn: Problem hashing header: ") - } +func PutBlockHashToBlockWithTxn( + txn *badger.Txn, + snap *Snapshot, + blockHash *BlockHash, + block *MsgDeSoBlock, + eventManager *EventManager, +) error { blockKey := BlockHashToBlockKey(blockHash) data, err := block.ToBytes(false) if err != nil { @@ -5070,12 +5069,14 @@ func PutBlockHashToBlockWithTxn(txn *badger.Txn, snap *Snapshot, block *MsgDeSoB return nil } -func PutBlockWithTxn(txn *badger.Txn, snap *Snapshot, desoBlock *MsgDeSoBlock, eventManager *EventManager) error { - blockHash, err := desoBlock.Header.Hash() - if err != nil { - return errors.Wrapf(err, "PutBlockWithTxn: Problem hashing header: ") - } - if err = PutBlockHashToBlockWithTxn(txn, snap, desoBlock, eventManager); err != nil { +func PutBlockWithTxn( + txn *badger.Txn, + snap *Snapshot, + desoBlock *MsgDeSoBlock, + blockHash *BlockHash, + eventManager *EventManager, +) error { + if err := PutBlockHashToBlockWithTxn(txn, snap, blockHash, desoBlock, eventManager); err != nil { return errors.Wrap(err, "PutBlockWithTxn: Problem putting block hash to block") } blockRewardTxn := desoBlock.Txns[0] @@ -5096,7 +5097,7 @@ func PutBlockWithTxn(txn *badger.Txn, snap *Snapshot, desoBlock *MsgDeSoBlock, e pkMapKey := pkMapKeyIter blockRewardKey := PublicKeyBlockHashToBlockRewardKey(pkMapKey[:], blockHash) - if err = DBSetWithTxn(txn, snap, blockRewardKey, EncodeUint64(blockReward), eventManager); err != nil { + if err := DBSetWithTxn(txn, snap, blockRewardKey, EncodeUint64(blockReward), eventManager); err != nil { return err } } @@ -5104,9 +5105,15 @@ func PutBlockWithTxn(txn *badger.Txn, snap *Snapshot, desoBlock *MsgDeSoBlock, e return nil } -func PutBlock(handle *badger.DB, snap *Snapshot, desoBlock *MsgDeSoBlock, eventManager *EventManager) error { +func PutBlock( + handle *badger.DB, + snap *Snapshot, + desoBlock *MsgDeSoBlock, + blockHash *BlockHash, + eventManager *EventManager, +) error { err := handle.Update(func(txn *badger.Txn) error { - return PutBlockWithTxn(txn, snap, desoBlock, eventManager) + return PutBlockWithTxn(txn, snap, desoBlock, blockHash, eventManager) }) return err @@ -5324,7 +5331,7 @@ func InitDbWithDeSoGenesisBlock(params *DeSoParams, handle *badger.DB, return errors.Wrapf(err, "InitDbWithGenesisBlock: Problem putting genesis block hash into db for block chain") } // Add the genesis block to the (hash -> block) index. - if err := PutBlockWithTxn(txn, snap, genesisBlock, eventManager); err != nil { + if err := PutBlockWithTxn(txn, snap, genesisBlock, blockHash, eventManager); err != nil { return errors.Wrapf(err, "InitDbWithGenesisBlock: Problem putting genesis block into db") } // Add the genesis block to the (height, hash -> node info) index in the db. diff --git a/lib/load_test.go b/lib/load_test.go index 2092ad91c..78a3b056e 100644 --- a/lib/load_test.go +++ b/lib/load_test.go @@ -189,7 +189,7 @@ func TestComputeMaxTPS(t *testing.T) { _, _ = newParams, newDB timeStart := time.Now() for _, blockToConnect := range blocksMined { - _, _, _, err := newChain.ProcessBlock(blockToConnect, true /*verifySignatures*/) + _, _, _, err := newChain.ProcessBlock(blockToConnect, nil, true /*verifySignatures*/) require.NoError(err) } elapsedSecs := (time.Since(timeStart)).Seconds() @@ -236,7 +236,7 @@ func TestConnectBlocksLoadTest(t *testing.T) { pprof.StartCPUProfile(ff) timeStart := time.Now() for _, blockToConnect := range blocksMined { - _, _, _, err := newChain.ProcessBlock(blockToConnect, false /*verifySignatures*/) + _, _, _, err := newChain.ProcessBlock(blockToConnect, nil, false /*verifySignatures*/) require.NoError(err) } elapsedSecs := (time.Since(timeStart)).Seconds() diff --git a/lib/miner.go b/lib/miner.go index 67ed8976a..0e0cc86bb 100644 --- a/lib/miner.go +++ b/lib/miner.go @@ -248,7 +248,7 @@ func (desoMiner *DeSoMiner) MineAndProcessSingleBlock(threadIndex uint32, mempoo verifySignatures := true // TODO(miner): Replace with a call to SubmitBlock. isMainChain, isOrphan, _, err := desoMiner.BlockProducer.chain.ProcessBlock( - blockToMine, verifySignatures) + blockToMine, nil, verifySignatures) glog.V(2).Infof("Called ProcessBlock: isMainChain=(%v), isOrphan=(%v), err=(%v)", isMainChain, isOrphan, err) if err != nil { diff --git a/lib/pos_blockchain.go b/lib/pos_blockchain.go index b63195896..dd70f5753 100644 --- a/lib/pos_blockchain.go +++ b/lib/pos_blockchain.go @@ -38,7 +38,7 @@ import ( // StatusHeaderValidated or StatusHeaderValidateFailed. // 5. Exit early if the's view is less than the current header chain's tip. // 6. Reorg the best header chain if the header's view is higher than the current tip. -func (bc *Blockchain) processHeaderPoS(header *MsgDeSoHeader, verifySignatures bool) ( +func (bc *Blockchain) processHeaderPoS(header *MsgDeSoHeader, headerHash *BlockHash, verifySignatures bool) ( _isMainChain bool, _isOrphan bool, _err error, ) { if !bc.params.IsPoSBlockHeight(header.Height) { @@ -48,9 +48,12 @@ func (bc *Blockchain) processHeaderPoS(header *MsgDeSoHeader, verifySignatures b ) } - headerHash, err := header.Hash() - if err != nil { - return false, false, errors.Wrapf(err, "processHeaderPoS: Problem hashing header") + if headerHash == nil { + var err error + headerHash, err = header.Hash() + if err != nil { + return false, false, errors.Wrapf(err, "processHeaderPoS: Problem hashing header") + } } // If the incoming header is already part of the best header chain, then we can exit early. @@ -191,14 +194,14 @@ func (bc *Blockchain) validateAndIndexHeaderPoS(header *MsgDeSoHeader, headerHas // is also not valid. if parentBlockNode.IsHeaderValidateFailed() { return nil, false, bc.storeValidateFailedHeaderInBlockIndexWithWrapperError( - header, errors.New("validateAndIndexHeaderPoS: Parent header failed validations"), + header, headerHash, errors.New("validateAndIndexHeaderPoS: Parent header failed validations"), ) } // Verify that the header is properly formed. if err := bc.isValidBlockHeaderPoS(header); err != nil { return nil, false, bc.storeValidateFailedHeaderInBlockIndexWithWrapperError( - header, errors.New("validateAndIndexHeaderPoS: Header failed validations"), + header, headerHash, errors.New("validateAndIndexHeaderPoS: Header failed validations"), ) } @@ -210,13 +213,13 @@ func (bc *Blockchain) validateAndIndexHeaderPoS(header *MsgDeSoHeader, headerHas } if !isValidRandomSeedSignature { return nil, false, bc.storeValidateFailedHeaderInBlockIndexWithWrapperError( - header, errors.New("validateAndIndexHeaderPoS: Header has invalid random seed signature"), + header, headerHash, errors.New("validateAndIndexHeaderPoS: Header has invalid random seed signature"), ) } } // Store it as HeaderValidated now that it has passed all validations. - blockNode, err = bc.storeValidatedHeaderInBlockIndex(header) + blockNode, err = bc.storeValidatedHeaderInBlockIndex(header, headerHash) if err != nil { return nil, false, errors.Wrapf(err, "validateAndIndexHeaderPoS: Problem adding header to block index: ") } @@ -226,7 +229,7 @@ func (bc *Blockchain) validateAndIndexHeaderPoS(header *MsgDeSoHeader, headerHas } // ProcessBlockPoS simply acquires the chain lock and calls processBlockPoS. -func (bc *Blockchain) ProcessBlockPoS(block *MsgDeSoBlock, currentView uint64, verifySignatures bool) ( +func (bc *Blockchain) ProcessBlockPoS(block *MsgDeSoBlock, blockHash *BlockHash, currentView uint64, verifySignatures bool) ( _success bool, _isOrphan bool, _missingBlockHashes []*BlockHash, @@ -241,7 +244,7 @@ func (bc *Blockchain) ProcessBlockPoS(block *MsgDeSoBlock, currentView uint64, v return false, false, nil, fmt.Errorf("ProcessBlockPoS: Block is nil") } - return bc.processBlockPoS(block, currentView, verifySignatures) + return bc.processBlockPoS(block, blockHash, currentView, verifySignatures) } // processBlockPoS runs the Fast-HotStuff block connect and commit rule as follows: @@ -253,7 +256,7 @@ func (bc *Blockchain) ProcessBlockPoS(block *MsgDeSoBlock, currentView uint64, v // 5. Try to apply the incoming block as the tip (performing reorgs as necessary). If it can't be applied, exit here. // 6. Run the commit rule - If applicable, flushes the incoming block's grandparent to the DB // 7. Notify listeners via the EventManager of which blocks have been removed and added. -func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, verifySignatures bool) ( +func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, blockHash *BlockHash, currentView uint64, verifySignatures bool) ( _success bool, _isOrphan bool, _missingBlockHashes []*BlockHash, @@ -268,14 +271,18 @@ func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, v } // If we can't hash the block, we can never store in the block index and we should throw it out immediately. - if _, err := block.Hash(); err != nil { - return false, false, nil, errors.Wrapf(err, "processBlockPoS: Problem hashing block") + var err error + if blockHash == nil { + blockHash, err = block.Hash() + if err != nil { + return false, false, nil, errors.Wrap(err, "processBlockPoS: Problem hashing block") + } } // In hypersync archival mode, we may receive blocks that have already been processed and committed during state // synchronization. However, we may want to store these blocks in the db for archival purposes. We check if the // block we're dealing with is an archival block. If it is, we store it and return early. - if success, err := bc.checkAndStoreArchivalBlock(block); err != nil { + if success, err := bc.checkAndStoreArchivalBlock(block, blockHash); err != nil { return false, false, nil, errors.Wrap(err, "processBlockPoS: Problem checking and storing archival block") } else if success { return true, false, nil, nil @@ -300,7 +307,7 @@ func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, v // on our best chain. Try to process the orphan by running basic validations. // If it passes basic integrity checks, we'll store it with the hope that we // will eventually get a parent that connects to our best chain. - return false, true, missingBlockHashes, bc.processOrphanBlockPoS(block) + return false, true, missingBlockHashes, bc.processOrphanBlockPoS(block, blockHash) } if err != nil { @@ -320,7 +327,7 @@ func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, v parentUtxoView := parentUtxoViewAndUtxoOps.UtxoView // First, we perform a validation of the leader and the QC to prevent spam. // If the block fails this check, we throw it away. - passedSpamPreventionCheck, err := bc.validateLeaderAndQC(block, parentUtxoView, verifySignatures) + passedSpamPreventionCheck, err := bc.validateLeaderAndQC(block, blockHash, parentUtxoView, verifySignatures) if err != nil { // If we hit an error, we can't store it since we're not sure if it passed the spam prevention check. return false, false, nil, errors.Wrap(err, "processBlockPoS: Problem validating leader and QC") @@ -331,7 +338,7 @@ func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, v } // Validate the block and store it in the block index. The block is guaranteed to not be an orphan. - blockNode, err := bc.validateAndIndexBlockPoS(block, parentUtxoView, verifySignatures) + blockNode, err := bc.validateAndIndexBlockPoS(block, blockHash, parentUtxoView, verifySignatures) if err != nil { return false, false, nil, errors.Wrap(err, "processBlockPoS: Problem validating block: ") @@ -354,7 +361,7 @@ func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, v // header and applying it to the header chain will result in the two chains being out of // sync. The header chain is less critical and mutations to it are reversible. So we attempt // to mutate it first before attempting to mutate the block chain. - if _, _, err = bc.processHeaderPoS(block.Header, verifySignatures); err != nil { + if _, _, err = bc.processHeaderPoS(block.Header, blockHash, verifySignatures); err != nil { return false, false, nil, errors.Wrap(err, "processBlockPoS: Problem processing header") } @@ -415,7 +422,7 @@ func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, v } var appliedNewTipOrphan bool if appliedNewTipOrphan, _, _, err = bc.processBlockPoS( - orphanBlock, currentView, verifySignatures); err != nil { + orphanBlock, blockNodeAtNextHeight.Hash, currentView, verifySignatures); err != nil { glog.Errorf("processBlockPoS: Problem validating orphan block %v", blockNodeAtNextHeight.Hash) continue } @@ -434,7 +441,7 @@ func (bc *Blockchain) processBlockPoS(block *MsgDeSoBlock, currentView uint64, v // As a spam-prevention measure, we will not store a block if it fails the QC or leader check // and simply throw it away. If it fails the other integrity checks, we'll store it // as validate failed. -func (bc *Blockchain) processOrphanBlockPoS(block *MsgDeSoBlock) error { +func (bc *Blockchain) processOrphanBlockPoS(block *MsgDeSoBlock, hash *BlockHash) error { // Construct a UtxoView, so we can perform the QC and leader checks. utxoView := bc.GetCommittedTipView() @@ -544,32 +551,24 @@ func (bc *Blockchain) processOrphanBlockPoS(block *MsgDeSoBlock) error { // As a spam-prevention measure, we just throw away this block and don't store it. return nil } - if err != nil { - return errors.Wrap(err, "processOrphanBlockPoS: Problem getting snapshot global params") - } // All blocks should pass the basic integrity validations, which ensure the block // is not malformed. If the block is malformed, we should store it as ValidateFailed. if err = bc.isProperlyFormedBlockPoS(block); err != nil { - if _, innerErr := bc.storeValidateFailedBlockInBlockIndex(block); innerErr != nil { + if _, innerErr := bc.storeValidateFailedBlockInBlockIndex(block, hash); innerErr != nil { return errors.Wrapf(innerErr, "processOrphanBlockPoS: Problem adding validate failed block to block index: %v", err) } return nil } // Add to blockIndexByHash with status STORED only as we are not sure if it's valid yet. - _, err = bc.storeBlockInBlockIndex(block) + _, err = bc.storeBlockInBlockIndex(block, hash) return errors.Wrap(err, "processBlockPoS: Problem adding block to block index: ") } // checkAndStoreArchivalBlock is a helper function that takes in a block and checks if it's an archival block. // If it is, it stores the block in the db and returns true. If it's not, it returns false, or false and an error. -func (bc *Blockchain) checkAndStoreArchivalBlock(block *MsgDeSoBlock) (_success bool, _err error) { - // First, get the block hash and lookup the block index. - blockHash, err := block.Hash() - if err != nil { - return false, errors.Wrap(err, "checkAndStoreArchivalBlock: Problem hashing block") - } - blockNode, exists := bc.blockIndexByHash.Get(*blockHash) +func (bc *Blockchain) checkAndStoreArchivalBlock(block *MsgDeSoBlock, hash *BlockHash) (_success bool, _err error) { + blockNode, exists := bc.blockIndexByHash.Get(*hash) // If the blockNode doesn't exist, or the block is not committed, or it's already stored, then we're not dealing // with an archival block. Archival blocks must have an existing blockNode, be committed, and not be stored. if !exists || !blockNode.IsCommitted() || blockNode.IsStored() { @@ -578,8 +577,7 @@ func (bc *Blockchain) checkAndStoreArchivalBlock(block *MsgDeSoBlock) (_success // If we get to this point, we're dealing with an archival block, so we'll attempt to store it. // This means, this block node is already marked as COMMITTED and VALIDATED, and we just need to store it. - _, err = bc.storeBlockInBlockIndex(block) - if err != nil { + if _, err := bc.storeBlockInBlockIndex(block, hash); err != nil { return false, errors.Wrap(err, "checkAndStoreArchivalBlock: Problem storing block in block index") } return true, nil @@ -587,9 +585,9 @@ func (bc *Blockchain) checkAndStoreArchivalBlock(block *MsgDeSoBlock) (_success // storeValidateFailedBlockWithWrappedError is a helper function that takes in a block and an error and // stores the block in the block index with status VALIDATE_FAILED. It returns the resulting BlockNode. -func (bc *Blockchain) storeValidateFailedBlockWithWrappedError(block *MsgDeSoBlock, outerErr error) ( +func (bc *Blockchain) storeValidateFailedBlockWithWrappedError(block *MsgDeSoBlock, hash *BlockHash, outerErr error) ( *BlockNode, error) { - blockNode, innerErr := bc.storeValidateFailedBlockInBlockIndex(block) + blockNode, innerErr := bc.storeValidateFailedBlockInBlockIndex(block, hash) if innerErr != nil { return nil, errors.Wrapf(innerErr, "storeValidateFailedBlockWithWrappedError: Problem adding validate failed block to block index: %v", @@ -600,6 +598,7 @@ func (bc *Blockchain) storeValidateFailedBlockWithWrappedError(block *MsgDeSoBlo func (bc *Blockchain) validateLeaderAndQC( block *MsgDeSoBlock, + blockHash *BlockHash, parentUtxoView *UtxoView, verifySignatures bool, ) (_passedSpamPreventionCheck bool, _err error) { @@ -621,7 +620,8 @@ func (bc *Blockchain) validateLeaderAndQC( "validateLeaderAndQC: Problem getting snapshot epoch number for epoch #%d", currentEpochEntry.EpochNumber) } - isValidPartialSig, err := parentUtxoView.hasValidProposerPartialSignaturePoS(block, snapshotAtEpochNumber) + isValidPartialSig, err := parentUtxoView.hasValidProposerPartialSignaturePoS( + block, blockHash, snapshotAtEpochNumber) if err != nil { return false, errors.Wrap(err, "validateLeaderAndQC: Problem validating proposer partial sig") @@ -679,11 +679,10 @@ func (bc *Blockchain) validateLeaderAndQC( // return the new BlockNode. // - Error case: Something goes wrong that doesn't result in the block being marked VALIDATE or VALIDATE_FAILED. In // this case, we will add the block to the block index with status STORED and return the BlockNode. -func (bc *Blockchain) validateAndIndexBlockPoS(block *MsgDeSoBlock, parentUtxoView *UtxoView, verifySignatures bool) ( +func (bc *Blockchain) validateAndIndexBlockPoS(block *MsgDeSoBlock, blockHash *BlockHash, parentUtxoView *UtxoView, verifySignatures bool) ( *BlockNode, error) { - blockHash, err := block.Header.Hash() - if err != nil { - return nil, errors.Wrapf(err, "validateAndIndexBlockPoS: Problem hashing block %v", block) + if blockHash == nil { + return nil, fmt.Errorf("validateAndIndexBlockPoS: no block hash provided: %v", block) } // Base case - Check if the block is validated or validate failed. If so, we can return early. @@ -717,13 +716,14 @@ func (bc *Blockchain) validateAndIndexBlockPoS(block *MsgDeSoBlock, parentUtxoVi // this block as ValidateFailed. If the parent is not ValidateFailed, we ONLY store the block and move on. // We don't want to store it as ValidateFailed because we don't know if it's actually invalid. if parentBlockNode.IsValidateFailed() { - return bc.storeValidateFailedBlockWithWrappedError(block, errors.New("parent block is ValidateFailed")) + return bc.storeValidateFailedBlockWithWrappedError( + block, blockHash, errors.New("parent block is ValidateFailed")) } // If the parent block still has a Stored status, it means that we weren't able to validate it // despite trying. The current block will also be stored as a Stored block. if !parentBlockNode.IsValidated() { - return bc.storeBlockInBlockIndex(block) + return bc.storeBlockInBlockIndex(block, blockHash) } // Validate the block's random seed signature @@ -731,14 +731,15 @@ func (bc *Blockchain) validateAndIndexBlockPoS(block *MsgDeSoBlock, parentUtxoVi isValidRandomSeedSignature, err := bc.hasValidProposerRandomSeedSignaturePoS(block.Header) if err != nil { var innerErr error - blockNode, innerErr = bc.storeBlockInBlockIndex(block) + blockNode, innerErr = bc.storeBlockInBlockIndex(block, blockHash) if innerErr != nil { return nil, errors.Wrapf(innerErr, "validateAndIndexBlockPoS: Problem adding block to block index: %v", err) } return blockNode, errors.Wrap(err, "validateAndIndexBlockPoS: Problem validating random seed signature") } if !isValidRandomSeedSignature { - return bc.storeValidateFailedBlockWithWrappedError(block, errors.New("invalid random seed signature")) + return bc.storeValidateFailedBlockWithWrappedError( + block, blockHash, errors.New("invalid random seed signature")) } } @@ -746,15 +747,15 @@ func (bc *Blockchain) validateAndIndexBlockPoS(block *MsgDeSoBlock, parentUtxoVi serializedBlock, err := block.ToBytes(false) if err != nil { return bc.storeValidateFailedBlockWithWrappedError( - block, errors.Wrap(err, "validateAndIndexBlockPoS: Problem serializing block")) + block, blockHash, errors.Wrap(err, "validateAndIndexBlockPoS: Problem serializing block")) } if uint64(len(serializedBlock)) > parentUtxoView.GetCurrentGlobalParamsEntry().MaxBlockSizeBytesPoS { - return bc.storeValidateFailedBlockWithWrappedError(block, RuleErrorBlockTooBig) + return bc.storeValidateFailedBlockWithWrappedError(block, blockHash, RuleErrorBlockTooBig) } // Check if the block is properly formed and passes all basic validations. if err = bc.isValidBlockPoS(block); err != nil { - return bc.storeValidateFailedBlockWithWrappedError(block, err) + return bc.storeValidateFailedBlockWithWrappedError(block, blockHash, err) } // Connect this block to the parent block's UtxoView. @@ -765,7 +766,7 @@ func (bc *Blockchain) validateAndIndexBlockPoS(block *MsgDeSoBlock, parentUtxoVi // If we fail to connect the block, then it means the block is invalid. We should store it as ValidateFailed. if _, err = parentUtxoView.ConnectBlock(block, txHashes, verifySignatures, nil, block.Header.Height); err != nil { // If it doesn't connect, we want to mark it as ValidateFailed. - return bc.storeValidateFailedBlockWithWrappedError(block, err) + return bc.storeValidateFailedBlockWithWrappedError(block, blockHash, err) } // If the block is too far in the future, we leave it as STORED and return early. @@ -774,11 +775,11 @@ func (bc *Blockchain) validateAndIndexBlockPoS(block *MsgDeSoBlock, parentUtxoVi return blockNode, errors.Wrap(err, "validateAndIndexBlockPoS: Problem checking block timestamp") } if failsTimestampDriftCheck { - return bc.storeBlockInBlockIndex(block) + return bc.storeBlockInBlockIndex(block, blockHash) } // We can now add this block to the block index since we have performed all basic validations. - blockNode, err = bc.storeValidatedBlockInBlockIndex(block) + blockNode, err = bc.storeValidatedBlockInBlockIndex(block, blockHash) if err != nil { return blockNode, errors.Wrap(err, "validateAndIndexBlockPoS: Problem adding block to block index: ") } @@ -823,7 +824,7 @@ func (bc *Blockchain) validatePreviouslyIndexedBlockPoS( parentUtxoView := parentUtxoViewAndUtxoOps.UtxoView // If the block isn't validated or validate failed, we need to run the anti-spam checks on it. - passedSpamPreventionCheck, err := bc.validateLeaderAndQC(block, parentUtxoView, verifySignatures) + passedSpamPreventionCheck, err := bc.validateLeaderAndQC(block, blockHash, parentUtxoView, verifySignatures) if err != nil { // If we hit an error, that means there was an intermittent issue when trying to // validate the QC or the leader. @@ -832,7 +833,7 @@ func (bc *Blockchain) validatePreviouslyIndexedBlockPoS( if !passedSpamPreventionCheck { // If the QC or Leader check failed, we'll never accept this block, but we've already stored it, // so we need to mark it as ValidateFailed. - blockNode, err = bc.storeValidateFailedBlockInBlockIndex(block) + blockNode, err = bc.storeValidateFailedBlockInBlockIndex(block, blockHash) if err != nil { return nil, errors.Wrap(err, "validatePreviouslyIndexedBlockPoS: Problem adding validate failed block to block index") @@ -841,7 +842,7 @@ func (bc *Blockchain) validatePreviouslyIndexedBlockPoS( } // We run the full validation algorithm on the block. - return bc.validateAndIndexBlockPoS(block, parentUtxoView, verifySignatures) + return bc.validateAndIndexBlockPoS(block, blockHash, parentUtxoView, verifySignatures) } // isValidBlockPoS performs all basic block integrity checks. Any error @@ -1115,8 +1116,11 @@ func (bc *Blockchain) hasValidProposerRandomSeedSignaturePoS(header *MsgDeSoHead return isVerified, nil } -func (bav *UtxoView) hasValidProposerPartialSignaturePoS(block *MsgDeSoBlock, snapshotAtEpochNumber uint64) ( - bool, error) { +func (bav *UtxoView) hasValidProposerPartialSignaturePoS( + block *MsgDeSoBlock, + blockHash *BlockHash, + snapshotAtEpochNumber uint64, +) (bool, error) { votingPublicKey := block.Header.ProposerVotingPublicKey proposerPartialSig := block.Header.ProposerVotePartialSignature // If the proposer partial sig is nil, we can't validate it. That's an error. @@ -1141,10 +1145,13 @@ func (bav *UtxoView) hasValidProposerPartialSignaturePoS(block *MsgDeSoBlock, sn return false, nil } // Get the block's hash - blockHash, err := block.Header.Hash() - if err != nil { - return false, errors.Wrapf(err, "hasValidProposerPartialSignaturePoS: Problem hashing block") + if blockHash == nil { + blockHash, err = block.Header.Hash() + if err != nil { + return false, errors.Wrapf(err, "hasValidProposerPartialSignaturePoS: Problem hashing block") + } } + // Now that we have the snapshot validator entry and validated that the // voting public key from this block's header matches the snapshotted // voting public key, we can validate the partial sig. @@ -1361,10 +1368,9 @@ func (bc *Blockchain) getStoredLineageFromCommittedTip(header *MsgDeSoHeader) ( // getOrCreateBlockNodeFromBlockIndex returns the block node from the block index if it exists. // Otherwise, it creates a new block node and adds it to the blockIndexByHash and blockIndexByHeight. -func (bc *Blockchain) getOrCreateBlockNodeFromBlockIndex(block *MsgDeSoBlock) (*BlockNode, error) { - hash, err := block.Header.Hash() - if err != nil { - return nil, errors.Wrapf(err, "getOrCreateBlockNodeFromBlockIndex: Problem hashing block %v", block) +func (bc *Blockchain) getOrCreateBlockNodeFromBlockIndex(block *MsgDeSoBlock, hash *BlockHash) (*BlockNode, error) { + if hash == nil { + return nil, fmt.Errorf("getOrCreateBlockNodeFromBlockIndex: Block hash is nil: %v", block) } blockNode, _ := bc.blockIndexByHash.Get(*hash) prevBlockNode, _ := bc.blockIndexByHash.Get(*block.Header.PrevBlockHash) @@ -1382,8 +1388,8 @@ func (bc *Blockchain) getOrCreateBlockNodeFromBlockIndex(block *MsgDeSoBlock) (* // storeBlockInBlockIndex upserts the blocks into the in-memory block index & badger and updates its status to // StatusBlockStored. It also writes the block to the block index in badger -func (bc *Blockchain) storeValidatedHeaderInBlockIndex(header *MsgDeSoHeader) (*BlockNode, error) { - blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(&MsgDeSoBlock{Header: header}) +func (bc *Blockchain) storeValidatedHeaderInBlockIndex(header *MsgDeSoHeader, hash *BlockHash) (*BlockNode, error) { + blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(&MsgDeSoBlock{Header: header}, hash) if err != nil { return nil, errors.Wrapf(err, "storeValidatedHeaderInBlockIndex: Problem getting or creating block node") } @@ -1401,8 +1407,12 @@ func (bc *Blockchain) storeValidatedHeaderInBlockIndex(header *MsgDeSoHeader) (* return blockNode, nil } -func (bc *Blockchain) storeValidateFailedHeaderInBlockIndexWithWrapperError(header *MsgDeSoHeader, wrapperError error) error { - if _, innerErr := bc.storeValidateFailedHeaderInBlockIndex(header); innerErr != nil { +func (bc *Blockchain) storeValidateFailedHeaderInBlockIndexWithWrapperError( + header *MsgDeSoHeader, + hash *BlockHash, + wrapperError error, +) error { + if _, innerErr := bc.storeValidateFailedHeaderInBlockIndex(header, hash); innerErr != nil { return errors.Wrapf(innerErr, "%v", wrapperError) } return wrapperError @@ -1410,8 +1420,11 @@ func (bc *Blockchain) storeValidateFailedHeaderInBlockIndexWithWrapperError(head // storeValidateFailedHeaderInBlockIndex stores the header in the block index only and sets its status to // StatusHeaderValidateFailed. It does not write the header to the DB. -func (bc *Blockchain) storeValidateFailedHeaderInBlockIndex(header *MsgDeSoHeader) (*BlockNode, error) { - blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(&MsgDeSoBlock{Header: header}) +func (bc *Blockchain) storeValidateFailedHeaderInBlockIndex( + header *MsgDeSoHeader, + hash *BlockHash, +) (*BlockNode, error) { + blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(&MsgDeSoBlock{Header: header}, hash) if err != nil { return nil, errors.Wrapf(err, "storeValidateFailedHeaderInBlockIndex: Problem getting or creating block node") } @@ -1432,8 +1445,8 @@ func (bc *Blockchain) storeValidateFailedHeaderInBlockIndex(header *MsgDeSoHeade // storeBlockInBlockIndex upserts the blocks into the in-memory block index & badger and updates its status to // StatusBlockStored. It also writes the block to the block index in badger // by calling upsertBlockAndBlockNodeToDB. -func (bc *Blockchain) storeBlockInBlockIndex(block *MsgDeSoBlock) (*BlockNode, error) { - blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(block) +func (bc *Blockchain) storeBlockInBlockIndex(block *MsgDeSoBlock, hash *BlockHash) (*BlockNode, error) { + blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(block, hash) if err != nil { return nil, errors.Wrapf(err, "storeBlockInBlockIndex: Problem getting or creating block node") } @@ -1453,8 +1466,8 @@ func (bc *Blockchain) storeBlockInBlockIndex(block *MsgDeSoBlock) (*BlockNode, e // status to StatusBlockValidated. If it does not have the status StatusBlockStored already, we add that as we // will store the block in the DB after updating its status. It also writes the block to the block index in // badger by calling upsertBlockAndBlockNodeToDB. -func (bc *Blockchain) storeValidatedBlockInBlockIndex(block *MsgDeSoBlock) (*BlockNode, error) { - blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(block) +func (bc *Blockchain) storeValidatedBlockInBlockIndex(block *MsgDeSoBlock, hash *BlockHash) (*BlockNode, error) { + blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(block, hash) if err != nil { return nil, errors.Wrapf(err, "storeValidatedBlockInBlockIndex: Problem getting or creating block node") } @@ -1483,8 +1496,8 @@ func (bc *Blockchain) storeValidatedBlockInBlockIndex(block *MsgDeSoBlock) (*Blo // status to StatusBlockValidateFailed. If it does not have the status StatusBlockStored already, we add that as we // will store the block in the DB after updating its status. It also writes the block to the block index in badger // by calling upsertBlockAndBlockNodeToDB. -func (bc *Blockchain) storeValidateFailedBlockInBlockIndex(block *MsgDeSoBlock) (*BlockNode, error) { - blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(block) +func (bc *Blockchain) storeValidateFailedBlockInBlockIndex(block *MsgDeSoBlock, hash *BlockHash) (*BlockNode, error) { + blockNode, err := bc.getOrCreateBlockNodeFromBlockIndex(block, hash) if err != nil { return nil, errors.Wrapf(err, "storeValidateFailedBlockInBlockIndex: Problem getting or creating block node") } @@ -1517,7 +1530,10 @@ func (bc *Blockchain) upsertBlockAndBlockNodeToDB(block *MsgDeSoBlock, blockNode // Store the block in badger err := bc.db.Update(func(txn *badger.Txn) error { if storeFullBlock { - if innerErr := PutBlockHashToBlockWithTxn(txn, bc.snapshot, block, bc.eventManager); innerErr != nil { + // TODO: we can make PutBlockHashToBlockWithTxn faster by passing in the hash instead of + // computing it from the block. + if innerErr := PutBlockHashToBlockWithTxn( + txn, bc.snapshot, blockNode.Hash, block, bc.eventManager); innerErr != nil { return errors.Wrapf(innerErr, "upsertBlockAndBlockNodeToDB: Problem calling PutBlockHashToBlockWithTxn") } } diff --git a/lib/pos_blockchain_test.go b/lib/pos_blockchain_test.go index 5553887c4..9d1edbb59 100644 --- a/lib/pos_blockchain_test.go +++ b/lib/pos_blockchain_test.go @@ -2,7 +2,6 @@ package lib import ( "bytes" - "fmt" "math" "math/rand" "testing" @@ -372,10 +371,10 @@ func TestUpsertBlockAndBlockNodeToDB(t *testing.T) { }, }, } - blockNode, err := bc.storeBlockInBlockIndex(block) - require.NoError(t, err) newHash, err := block.Hash() require.NoError(t, err) + blockNode, err := bc.storeBlockInBlockIndex(block, newHash) + require.NoError(t, err) // Check the block index by hash blockNodeFromIndex, exists := bc.blockIndexByHash.Get(*newHash) require.True(t, exists) @@ -399,7 +398,7 @@ func TestUpsertBlockAndBlockNodeToDB(t *testing.T) { require.NoError(t, err) require.True(t, bytes.Equal(uncommittedBytes, origBlockBytes)) // Okay now we update the status of the block to include validated. - blockNode, err = bc.storeValidatedBlockInBlockIndex(block) + blockNode, err = bc.storeValidatedBlockInBlockIndex(block, newHash) require.NoError(t, err) blockNodeFromIndex, exists = bc.blockIndexByHash.Get(*newHash) require.True(t, exists) @@ -425,7 +424,7 @@ func TestUpsertBlockAndBlockNodeToDB(t *testing.T) { require.False(t, updatedBlockHash.IsEqual(newHash)) // Okay now put this new block in there. - blockNode, err = bc.storeBlockInBlockIndex(block) + blockNode, err = bc.storeBlockInBlockIndex(block, updatedBlockHash) require.NoError(t, err) // Make sure the blockIndexByHash is correct. updatedBlockNode, exists := bc.blockIndexByHash.Get(*updatedBlockHash) @@ -446,7 +445,10 @@ func TestUpsertBlockAndBlockNodeToDB(t *testing.T) { // If we're missing a field in the header, we should get an error // as we can't compute the hash. block.Header.ProposerVotingPublicKey = nil - _, err = bc.storeBlockInBlockIndex(block) + missingFieldHash, err := block.Header.Hash() + require.Error(t, err) + require.Nil(t, missingFieldHash) + _, err = bc.storeBlockInBlockIndex(block, missingFieldHash) require.Error(t, err) } @@ -1776,10 +1778,16 @@ func TestProcessBlockPoS(t *testing.T) { // 4. Process a regular block that reorgs from the previous tip // 5. Process an orphan, which tests the block's storage and the return value of missingBlockHashes func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { + var err error + var success bool + var isOrphan bool + var missingBlockHashes []*BlockHash { // Create a bad block and try to process it. dummyBlock := _generateDummyBlock(testMeta, 12, 12, 887) - success, isOrphan, missingBlockHashes, err := testMeta.chain.ProcessBlockPoS(dummyBlock, 12, true) + dummyBlockHash, err := dummyBlock.Header.Hash() + require.NoError(t, err) + success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(dummyBlock, dummyBlockHash, 12, true) require.False(t, success) require.False(t, isOrphan) require.Len(t, missingBlockHashes, 0) @@ -1790,7 +1798,9 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { { var realBlock *MsgDeSoBlock realBlock = _generateRealBlock(testMeta, 12, 12, 889, testMeta.chain.BlockTip().Hash, false) - success, isOrphan, missingBlockHashes, err := testMeta.chain.ProcessBlockPoS(realBlock, 12, true) + blockHash1, err = realBlock.Hash() + require.NoError(t, err) + success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(realBlock, blockHash1, 12, true) require.True(t, success) require.False(t, isOrphan) require.Len(t, missingBlockHashes, 0) @@ -1798,8 +1808,6 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { // Okay now we can check the best chain. // We expect the block to be uncommitted. - blockHash1, err = realBlock.Hash() - require.NoError(t, err) _verifyCommitRuleHelper(testMeta, []*BlockHash{}, []*BlockHash{blockHash1}, nil) _verifyRandomSeedHashHelper(testMeta, realBlock) } @@ -1809,20 +1817,18 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { // Now let's try adding two more blocks on top of this one to make sure commit rule works properly. var realBlock2 *MsgDeSoBlock realBlock2 = _generateRealBlock(testMeta, 13, 13, 950, blockHash1, false) - success, _, _, err := testMeta.chain.ProcessBlockPoS(realBlock2, 13, true) - require.True(t, success) blockHash2, err = realBlock2.Hash() require.NoError(t, err) + success, _, _, err = testMeta.chain.ProcessBlockPoS(realBlock2, blockHash2, 13, true) + require.True(t, success) var realBlock3 *MsgDeSoBlock realBlock3 = _generateRealBlock(testMeta, 14, 14, 378, blockHash2, false) - - success, _, _, err = testMeta.chain.ProcessBlockPoS(realBlock3, 14, true) - require.True(t, success) - // Okay now we expect blockHash1 to be committed, but blockHash2 and 3 to not be committed. blockHash3, err = realBlock3.Hash() require.NoError(t, err) - + success, _, _, err = testMeta.chain.ProcessBlockPoS(realBlock3, blockHash3, 14, true) + require.True(t, success) + // Okay now we expect blockHash1 to be committed, but blockHash2 and 3 to not be committed. _verifyCommitRuleHelper(testMeta, []*BlockHash{blockHash1}, []*BlockHash{blockHash2, blockHash3}, blockHash1) _verifyRandomSeedHashHelper(testMeta, realBlock3) @@ -1830,15 +1836,14 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { var futureBlock *MsgDeSoBlock futureBlock = _generateRealBlockWithTimestampOffset(testMeta, 15, 15, 870, blockHash3, false, time.Hour) - success, isOrphan, missingBlockHashes, err := testMeta.chain.ProcessBlockPoS(futureBlock, 15, true) + futureBlockHash, err = futureBlock.Hash() + require.NoError(t, err) + success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(futureBlock, futureBlockHash, 15, true) require.False(t, success) require.False(t, isOrphan) require.Len(t, missingBlockHashes, 0) require.Error(t, err) - futureBlockHash, err = futureBlock.Hash() - require.NoError(t, err) - futureBlockNode, exists := testMeta.chain.blockIndexByHash.Get(*futureBlockHash) require.True(t, exists) require.False(t, futureBlockNode.IsCommitted()) @@ -1852,12 +1857,11 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { // Okay let's timeout view 15 var timeoutBlock *MsgDeSoBlock timeoutBlock = _generateRealBlock(testMeta, 15, 16, 381, blockHash3, true) - success, _, _, err := testMeta.chain.ProcessBlockPoS(timeoutBlock, 15, true) - fmt.Println(err) - require.True(t, success) timeoutBlockHash, err = timeoutBlock.Hash() require.NoError(t, err) - + success, _, _, err = testMeta.chain.ProcessBlockPoS(timeoutBlock, timeoutBlockHash, 15, true) + require.NoError(t, err) + require.True(t, success) _verifyCommitRuleHelper(testMeta, []*BlockHash{blockHash1, blockHash2}, []*BlockHash{blockHash3, timeoutBlockHash}, blockHash2) } @@ -1866,10 +1870,11 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { // Okay let's introduce a reorg. New block at view 15 with block 3 as its parent. var reorgBlock *MsgDeSoBlock reorgBlock = _generateRealBlock(testMeta, 15, 15, 373, blockHash3, false) - success, _, _, err := testMeta.chain.ProcessBlockPoS(reorgBlock, 15, true) - require.True(t, success) reorgBlockHash, err = reorgBlock.Hash() require.NoError(t, err) + success, _, _, err = testMeta.chain.ProcessBlockPoS(reorgBlock, reorgBlockHash, 15, true) + require.True(t, success) + require.NoError(t, err) // We expect blockHash1 and blockHash2 to be committed, but blockHash3 and reorgBlockHash to not be committed. // Timeout block will no longer be in best chain, and will still be in an uncommitted state in the block index _verifyCommitRuleHelper(testMeta, []*BlockHash{blockHash1, blockHash2}, []*BlockHash{blockHash3, reorgBlockHash}, blockHash2) @@ -1885,7 +1890,6 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { { // Let's process an orphan block. var dummyParentBlock *MsgDeSoBlock - var err error dummyParentBlock = _generateRealBlock(testMeta, 16, 16, 272, reorgBlockHash, false) dummyParentBlockHash, err = dummyParentBlock.Hash() require.NoError(t, err) @@ -1899,7 +1903,7 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { updateProposerVotePartialSignatureForBlock(testMeta, orphanBlock) orphanBlockHash, err = orphanBlock.Hash() require.NoError(t, err) - success, isOrphan, missingBlockHashes, err := testMeta.chain.ProcessBlockPoS(orphanBlock, 17, true) + success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(orphanBlock, orphanBlockHash, 17, true) require.False(t, success) require.True(t, isOrphan) require.Len(t, missingBlockHashes, 1) @@ -1912,7 +1916,7 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { require.False(t, orphanBlockInIndex.IsValidated()) // Okay now if we process the parent block, the orphan should get updated to be validated. - success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(dummyParentBlock, 16, true) + success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(dummyParentBlock, dummyParentBlockHash, 16, true) require.True(t, success) require.False(t, isOrphan) require.Len(t, missingBlockHashes, 0) @@ -1937,7 +1941,7 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { updateProposerVotePartialSignatureForBlock(testMeta, malformedOrphanBlock) malformedOrphanBlockHash, err := malformedOrphanBlock.Hash() require.NoError(t, err) - success, isOrphan, missingBlockHashes, err := testMeta.chain.ProcessBlockPoS(malformedOrphanBlock, 18, true) + success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(malformedOrphanBlock, malformedOrphanBlockHash, 18, true) require.False(t, success) require.True(t, isOrphan) require.Len(t, missingBlockHashes, 1) @@ -1952,7 +1956,7 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { // If a block can't be hashed, we expect to get an error. malformedOrphanBlock.Header.Version = HeaderVersion2 malformedOrphanBlock.Header.ProposerVotingPublicKey = nil - success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(malformedOrphanBlock, 18, true) + success, isOrphan, missingBlockHashes, err = testMeta.chain.ProcessBlockPoS(malformedOrphanBlock, nil, 18, true) require.False(t, success) require.False(t, isOrphan) require.Len(t, missingBlockHashes, 0) @@ -1962,10 +1966,10 @@ func testProcessBlockPoS(t *testing.T, testMeta *TestMeta) { { var blockWithFailingTxn *MsgDeSoBlock blockWithFailingTxn = _generateRealBlockWithFailingTxn(testMeta, 18, 18, 123722, orphanBlockHash, false, 1, 0) - success, _, _, err := testMeta.chain.ProcessBlockPoS(blockWithFailingTxn, 18, true) - require.True(t, success) blockWithFailingTxnHash, err = blockWithFailingTxn.Hash() require.NoError(t, err) + success, _, _, err = testMeta.chain.ProcessBlockPoS(blockWithFailingTxn, blockWithFailingTxnHash, 18, true) + require.True(t, success) _verifyCommitRuleHelper(testMeta, []*BlockHash{blockHash1, blockHash2, blockHash3, reorgBlockHash, dummyParentBlockHash}, []*BlockHash{orphanBlockHash, blockWithFailingTxnHash}, dummyParentBlockHash) } @@ -1986,7 +1990,7 @@ func TestGetSafeBlocks(t *testing.T) { block1Hash, err := block1.Hash() require.NoError(t, err) // Add block 1 w/ stored and validated - bn1, err := testMeta.chain.storeValidatedBlockInBlockIndex(block1) + bn1, err := testMeta.chain.storeValidatedBlockInBlockIndex(block1, block1Hash) require.NoError(t, err) require.True(t, bn1.Hash.IsEqual(block1Hash)) // Create block 2 w/ block 1 as parent and add it to the block index w/ stored & validated @@ -1994,24 +1998,24 @@ func TestGetSafeBlocks(t *testing.T) { block2 = _generateRealBlock(testMeta, uint64(testMeta.savedHeight+1), uint64(testMeta.savedHeight+1), 1293, block1Hash, false) block2Hash, err := block2.Hash() require.NoError(t, err) - bn2, err := testMeta.chain.storeValidatedBlockInBlockIndex(block2) + bn2, err := testMeta.chain.storeValidatedBlockInBlockIndex(block2, block2Hash) require.NoError(t, err) require.True(t, bn2.Hash.IsEqual(block2Hash)) // Add block 3 only as stored and validated var block3 *MsgDeSoBlock block3 = _generateRealBlock(testMeta, uint64(testMeta.savedHeight+2), uint64(testMeta.savedHeight+2), 1372, block2Hash, false) - bn3, err := testMeta.chain.storeValidatedBlockInBlockIndex(block3) - require.NoError(t, err) block3Hash, err := block3.Hash() require.NoError(t, err) + bn3, err := testMeta.chain.storeValidatedBlockInBlockIndex(block3, block3Hash) + require.NoError(t, err) require.True(t, bn3.Hash.IsEqual(block3Hash)) // Add block 3' only as stored var block3Prime *MsgDeSoBlock block3Prime = _generateRealBlock(testMeta, uint64(testMeta.savedHeight+2), uint64(testMeta.savedHeight+3), 137175, block2Hash, false) - bn3Prime, err := testMeta.chain.storeBlockInBlockIndex(block3Prime) - require.NoError(t, err) block3PrimeHash, err := block3Prime.Hash() require.NoError(t, err) + bn3Prime, err := testMeta.chain.storeBlockInBlockIndex(block3Prime, block3PrimeHash) + require.NoError(t, err) require.True(t, bn3Prime.Hash.IsEqual(block3PrimeHash)) // Add block 5 as Stored & Validated (this could never really happen, but it illustrates a point!) var block5 *MsgDeSoBlock @@ -2019,7 +2023,7 @@ func TestGetSafeBlocks(t *testing.T) { block5.Header.Height = uint64(testMeta.savedHeight + 5) block5Hash, err := block5.Hash() require.NoError(t, err) - _, err = testMeta.chain.storeValidatedBlockInBlockIndex(block5) + _, err = testMeta.chain.storeValidatedBlockInBlockIndex(block5, block5Hash) require.NoError(t, err) // Okay let's get the safe blocks. safeBlocks, err := testMeta.chain.GetSafeBlocks() @@ -2040,7 +2044,7 @@ func TestGetSafeBlocks(t *testing.T) { require.False(t, _checkSafeBlocksForBlockHash(block5Hash, safeBlocks)) // Update block 3 prime to be validated and it should now be a safe block. - bn3Prime, err = testMeta.chain.storeValidatedBlockInBlockIndex(block3Prime) + bn3Prime, err = testMeta.chain.storeValidatedBlockInBlockIndex(block3Prime, block3PrimeHash) require.NoError(t, err) require.True(t, bn3Prime.IsValidated()) safeBlocks, err = testMeta.chain.GetSafeBlocks() @@ -2062,11 +2066,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { // Give the block a random parent, so it is truly an orphan. realBlock.Header.PrevBlockHash = NewBlockHash(RandomBytes(32)) updateProposerVotePartialSignatureForBlock(testMeta, realBlock) - err := testMeta.chain.processOrphanBlockPoS(realBlock) - require.NoError(t, err) - // Get the block node from the block index. blockHash, err := realBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(realBlock, blockHash) + require.NoError(t, err) + // Get the block node from the block index. blockNode, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.True(t, exists) require.True(t, blockNode.IsStored()) @@ -2083,11 +2087,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { realBlock.Header.Version = 1 updateProposerVotePartialSignatureForBlock(testMeta, realBlock) // There should be no error, but the block should be marked as ValidateFailed. - err := testMeta.chain.processOrphanBlockPoS(realBlock) - require.NoError(t, err) - // Get the block node from the block index. blockHash, err := realBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(realBlock, blockHash) + require.NoError(t, err) + // Get the block node from the block index. blockNode, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.True(t, exists) require.True(t, blockNode.IsStored()) @@ -2110,11 +2114,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { realBlock.Header.ProposerVotingPublicKey = _generateRandomBLSPrivateKey(t).PublicKey() updateProposerVotePartialSignatureForBlock(testMeta, realBlock) // There should be no error, but the block should be marked as ValidateFailed. - err = testMeta.chain.processOrphanBlockPoS(realBlock) - require.NoError(t, err) - // Get the block node from the block index. blockHash, err := realBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(realBlock, blockHash) + require.NoError(t, err) + // Get the block node from the block index. _, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.False(t, exists) } @@ -2162,11 +2166,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { } updateProposerVotePartialSignatureForBlock(testMeta, realBlock) // There should be no error, but the block should be marked as ValidateFailed. - err = testMeta.chain.processOrphanBlockPoS(realBlock) - require.NoError(t, err) - // Get the block node from the block index. blockHash, err := realBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(realBlock, blockHash) + require.NoError(t, err) + // Get the block node from the block index. _, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.False(t, exists) } @@ -2180,11 +2184,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { // Give the block a random parent, so it is truly an orphan. nextEpochBlock.Header.PrevBlockHash = NewBlockHash(RandomBytes(32)) updateProposerVotePartialSignatureForBlock(testMeta, nextEpochBlock) - err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock) - require.NoError(t, err) - // Get the block node from the block index. blockHash, err := nextEpochBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock, blockHash) + require.NoError(t, err) + // Get the block node from the block index. blockNode, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.True(t, exists) require.True(t, blockNode.IsStored()) @@ -2205,11 +2209,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { nextEpochBlock.Header.ProposerVotingPublicKey = _generateRandomBLSPrivateKey(t).PublicKey() updateProposerVotePartialSignatureForBlock(testMeta, nextEpochBlock) // There should be no error, but the block should be marked as ValidateFailed. - err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock) - require.NoError(t, err) - // Get the block node from the block index. blockHash, err := nextEpochBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock, blockHash) + require.NoError(t, err) + // Get the block node from the block index. _, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.False(t, exists) } @@ -2224,7 +2228,9 @@ func TestProcessOrphanBlockPoS(t *testing.T) { nextEpochBlock.Header.PrevBlockHash = NewBlockHash(RandomBytes(32)) updateProposerVotePartialSignatureForBlock(testMeta, nextEpochBlock) // Update the QC to not have a supermajority. - err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock) + nextEpochBlockHash, err := nextEpochBlock.Hash() + require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock, nextEpochBlockHash) require.NoError(t, err) // Update the QC to not have a supermajority. // Get all the bls keys for the validators that aren't the leader. @@ -2256,11 +2262,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { Signature: aggregatedSignature, } updateProposerVotePartialSignatureForBlock(testMeta, nextEpochBlock) - err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock) - require.NoError(t, err) - // Get the block node from the block index. blockHash, err := nextEpochBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(nextEpochBlock, blockHash) + require.NoError(t, err) + // Get the block node from the block index. _, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.False(t, exists) } @@ -2285,11 +2291,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { twoEpochsInFutureBlock.Header.PrevBlockHash = NewBlockHash(RandomBytes(32)) updateProposerVotePartialSignatureForBlock(testMeta, twoEpochsInFutureBlock) // We should get an error that this block is too far in the future. - err = testMeta.chain.processOrphanBlockPoS(twoEpochsInFutureBlock) - require.Error(t, err) - // The block shouldn't be in the block index. blockHash, err := twoEpochsInFutureBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(twoEpochsInFutureBlock, blockHash) + require.Error(t, err) + // The block shouldn't be in the block index. _, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.False(t, exists) } @@ -2302,11 +2308,11 @@ func TestProcessOrphanBlockPoS(t *testing.T) { require.NoError(t, err) var prevEpochBlock *MsgDeSoBlock prevEpochBlock = _generateRealBlock(testMeta, prevEpochEntry.FinalBlockHeight, prevEpochEntry.FinalBlockHeight, 17283, testMeta.chain.BlockTip().Hash, false) - err = testMeta.chain.processOrphanBlockPoS(prevEpochBlock) - require.NoError(t, err) - // The block should be in the block index. blockHash, err := prevEpochBlock.Hash() require.NoError(t, err) + err = testMeta.chain.processOrphanBlockPoS(prevEpochBlock, blockHash) + require.NoError(t, err) + // The block should be in the block index. blockNode, exists := testMeta.chain.blockIndexByHash.Get(*blockHash) require.True(t, exists) require.True(t, blockNode.IsStored()) @@ -2320,10 +2326,12 @@ func TestHasValidProposerPartialSignaturePoS(t *testing.T) { // Generate a real block and make sure it doesn't hit any errors. var realBlock *MsgDeSoBlock realBlock = _generateRealBlock(testMeta, 12, 12, 889, testMeta.chain.BlockTip().Hash, false) + realBlockHash, err := realBlock.Hash() + require.NoError(t, err) utxoView := _newUtxoView(testMeta) snapshotEpochNumber, err := utxoView.GetCurrentSnapshotEpochNumber() require.NoError(t, err) - isValid, err := utxoView.hasValidProposerPartialSignaturePoS(realBlock, snapshotEpochNumber) + isValid, err := utxoView.hasValidProposerPartialSignaturePoS(realBlock, realBlockHash, snapshotEpochNumber) require.NoError(t, err) require.True(t, isValid) @@ -2331,7 +2339,9 @@ func TestHasValidProposerPartialSignaturePoS(t *testing.T) { realVotingPublicKey := realBlock.Header.ProposerVotingPublicKey { realBlock.Header.ProposerVotingPublicKey = _generateRandomBLSPrivateKey(t).PublicKey() - isValid, err = utxoView.hasValidProposerPartialSignaturePoS(realBlock, snapshotEpochNumber) + realBlockHash, err = realBlock.Hash() + require.NoError(t, err) + isValid, err = utxoView.hasValidProposerPartialSignaturePoS(realBlock, realBlockHash, snapshotEpochNumber) require.NoError(t, err) require.False(t, isValid) // Reset the proposer voting public key @@ -2343,20 +2353,23 @@ func TestHasValidProposerPartialSignaturePoS(t *testing.T) { incorrectPayload := consensus.GetVoteSignaturePayload(13, testMeta.chain.BlockTip().Hash) realBlock.Header.ProposerVotePartialSignature, err = testMeta.blsPubKeyToBLSKeyMap[realBlock.Header.ProposerVotingPublicKey.ToString()].Sign(incorrectPayload[:]) - isValid, err = utxoView.hasValidProposerPartialSignaturePoS(realBlock, snapshotEpochNumber) + realBlockHash, err = realBlock.Hash() + require.NoError(t, err) + isValid, err = utxoView.hasValidProposerPartialSignaturePoS(realBlock, realBlockHash, snapshotEpochNumber) require.NoError(t, err) require.False(t, isValid) } // Signature on correct payload from wrong public key should fail. { - var realBlockHash *BlockHash realBlockHash, err = realBlock.Hash() require.NoError(t, err) correctPayload := consensus.GetVoteSignaturePayload(12, realBlockHash) wrongPrivateKey := _generateRandomBLSPrivateKey(t) realBlock.Header.ProposerVotePartialSignature, err = wrongPrivateKey.Sign(correctPayload[:]) - isValid, err = utxoView.hasValidProposerPartialSignaturePoS(realBlock, snapshotEpochNumber) + realBlockHash, err = realBlock.Hash() + require.NoError(t, err) + isValid, err = utxoView.hasValidProposerPartialSignaturePoS(realBlock, realBlockHash, snapshotEpochNumber) require.NoError(t, err) require.False(t, isValid) } @@ -2371,10 +2384,10 @@ func TestHasValidProposerRandomSeedSignaturePoS(t *testing.T) { isValid, err := testMeta.chain.hasValidProposerRandomSeedSignaturePoS(realBlock.Header) require.NoError(t, err) require.True(t, isValid) - _, _, _, err = testMeta.chain.ProcessBlockPoS(realBlock, 12, true) - require.NoError(t, err) realBlockHash, err := realBlock.Hash() require.NoError(t, err) + _, _, _, err = testMeta.chain.ProcessBlockPoS(realBlock, realBlockHash, 12, true) + require.NoError(t, err) realBlockNode, exists := testMeta.chain.blockIndexByHash.Get(*realBlockHash) require.True(t, exists) require.True(t, realBlockNode.IsStored()) @@ -2521,7 +2534,7 @@ func _generateDummyBlock(testMeta *TestMeta, blockHeight uint64, view uint64, se require.NoError(testMeta.t, err) // Add block to block index. - blockNode, err := testMeta.chain.storeBlockInBlockIndex(msgDesoBlock) + blockNode, err := testMeta.chain.storeBlockInBlockIndex(msgDesoBlock, newBlockHash) require.NoError(testMeta.t, err) require.True(testMeta.t, blockNode.IsStored()) _, exists := testMeta.chain.blockIndexByHash.Get(*newBlockHash) @@ -2543,7 +2556,7 @@ func _generateBlockAndAddToBestChain(testMeta *TestMeta, blockHeight uint64, vie newBlockHash, err := msgDesoBlock.Hash() require.NoError(testMeta.t, err) // Add block to block index. - blockNode, err := testMeta.chain.storeValidatedBlockInBlockIndex(msgDesoBlock) + blockNode, err := testMeta.chain.storeValidatedBlockInBlockIndex(msgDesoBlock, newBlockHash) require.NoError(testMeta.t, err) require.True(testMeta.t, blockNode.IsStored()) require.True(testMeta.t, blockNode.IsValidated()) diff --git a/lib/pos_consensus.go b/lib/pos_consensus.go index ea731def3..11f81d366 100644 --- a/lib/pos_consensus.go +++ b/lib/pos_consensus.go @@ -314,7 +314,7 @@ func (fc *FastHotStuffConsensus) handleBlockProposalEvent( } // Process the block locally - missingBlockHashes, err := fc.tryProcessBlockAsNewTip(blockProposal) + missingBlockHashes, err := fc.tryProcessBlockAsNewTip(blockProposal, nil) if err != nil { return errors.Errorf("Error processing block locally: %v", err) } @@ -575,7 +575,11 @@ func (fc *FastHotStuffConsensus) HandleValidatorTimeout(pp *Peer, msg *MsgDeSoVa return nil, nil } -func (fc *FastHotStuffConsensus) HandleBlock(pp *Peer, msg *MsgDeSoBlock) (missingBlockHashes []*BlockHash, _err error) { +func (fc *FastHotStuffConsensus) HandleBlock( + pp *Peer, + msg *MsgDeSoBlock, + blockHash *BlockHash, +) (missingBlockHashes []*BlockHash, _err error) { glog.V(2).Infof("FastHotStuffConsensus.HandleBlock: Received block: \n%s", msg.String()) glog.V(2).Infof("FastHotStuffConsensus.HandleBlock: %s", fc.fastHotStuffEventLoop.ToString()) @@ -597,7 +601,7 @@ func (fc *FastHotStuffConsensus) HandleBlock(pp *Peer, msg *MsgDeSoBlock) (missi // Try to apply the block as the new tip of the blockchain. If the block is an orphan, then // we will get back a list of missing ancestor block hashes. We can fetch the missing blocks // from the network and retry. - missingBlockHashes, err := fc.tryProcessBlockAsNewTip(msg) + missingBlockHashes, err := fc.tryProcessBlockAsNewTip(msg, blockHash) if err != nil { // If we get an error here, it means something went wrong with the block processing algorithm. // Nothing we can do to recover here. @@ -627,10 +631,19 @@ func (fc *FastHotStuffConsensus) HandleBlock(pp *Peer, msg *MsgDeSoBlock) (missi // // Reference Implementation: // https://github.com/deso-protocol/hotstuff_pseudocode/blob/6409b51c3a9a953b383e90619076887e9cebf38d/fast_hotstuff_bls.go#L573 -func (fc *FastHotStuffConsensus) tryProcessBlockAsNewTip(block *MsgDeSoBlock) ([]*BlockHash, error) { +func (fc *FastHotStuffConsensus) tryProcessBlockAsNewTip(block *MsgDeSoBlock, hash *BlockHash) ([]*BlockHash, error) { + if hash == nil { + var err error + hash, err = block.Hash() + if err != nil { + return nil, errors.Errorf("Error hashing block: %v", err) + } + } + // Try to apply the block locally as the new tip of the blockchain successfullyAppliedNewTip, _, missingBlockHashes, err := fc.blockchain.processBlockPoS( block, // Pass in the block itself + hash, fc.fastHotStuffEventLoop.GetCurrentView(), // Pass in the current view to ensure we don't process a stale block true, // Make sure we verify signatures in the block ) diff --git a/lib/server.go b/lib/server.go index 0069540cb..19e3f0b48 100644 --- a/lib/server.go +++ b/lib/server.go @@ -2318,14 +2318,14 @@ func (srv *Server) _handleBlock(pp *Peer, blk *MsgDeSoBlock, isLastBlock bool) { "Server._handleBlock: Processing block %v with FastHotStuffConsensus with SyncState=%v for peer %v", blk, srv.blockchain.chainState(), pp, ))) - blockHashesToRequest, err = srv.fastHotStuffConsensus.HandleBlock(pp, blk) + blockHashesToRequest, err = srv.fastHotStuffConsensus.HandleBlock(pp, blk, blockHash) isOrphan = len(blockHashesToRequest) > 0 } else if !verifySignatures { glog.V(0).Infof(CLog(Cyan, fmt.Sprintf( "Server._handleBlock: Processing block %v WITHOUT signature checking because SyncState=%v for peer %v", blk, srv.blockchain.chainState(), pp, ))) - _, isOrphan, blockHashesToRequest, err = srv.blockchain.ProcessBlock(blk, false) + _, isOrphan, blockHashesToRequest, err = srv.blockchain.ProcessBlock(blk, blockHash, false) } else { // TODO: Signature checking slows things down because it acquires the ChainLock. // The optimal solution is to check signatures in a way that doesn't acquire the @@ -2334,7 +2334,7 @@ func (srv *Server) _handleBlock(pp *Peer, blk *MsgDeSoBlock, isLastBlock bool) { "Server._handleBlock: Processing block %v WITH signature checking because SyncState=%v for peer %v", blk, srv.blockchain.chainState(), pp, ))) - _, isOrphan, blockHashesToRequest, err = srv.blockchain.ProcessBlock(blk, true) + _, isOrphan, blockHashesToRequest, err = srv.blockchain.ProcessBlock(blk, blockHash, true) } // If we hit an error then abort mission entirely. We should generally never diff --git a/lib/txindex.go b/lib/txindex.go index e16c4f68c..91a759f69 100644 --- a/lib/txindex.go +++ b/lib/txindex.go @@ -448,7 +448,7 @@ func (txi *TXIndex) Update() error { // Now that we have added all the txns to our TxIndex db, attach the block // to update our chain. - _, _, _, err = txi.TXIndexChain.ProcessBlock(blockMsg, false /*verifySignatures*/) + _, _, _, err = txi.TXIndexChain.ProcessBlock(blockMsg, blockToAttach.Hash, false /*verifySignatures*/) if err != nil { return fmt.Errorf("Update: Problem attaching block %v: %v", blockToAttach, err)