From 3903fb282cbbb01a06e220295896201bb67ed8ec Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 20 Sep 2024 19:34:29 +0300 Subject: [PATCH 1/4] Removed reusing error variables in 'stateManager'. Fixed 'AddDeserializedBlocks' error miss. --- pkg/state/state.go | 63 ++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/pkg/state/state.go b/pkg/state/state.go index 94ce09047..d10bf1f17 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -481,9 +481,9 @@ func initGenesis(state *stateManager, height uint64, settings *settings.Blockcha if err != nil { return errors.Wrap(err, "failed to get genesis block from state") } - err = settings.Genesis.GenerateBlockID(settings.AddressSchemeCharacter) - if err != nil { - return errors.Wrap(err, "failed to generate genesis block id from config") + + if genErr := settings.Genesis.GenerateBlockID(settings.AddressSchemeCharacter); genErr != nil { + return errors.Wrap(genErr, "failed to generate genesis block id from config") } if !bytes.Equal(genesis.ID.Bytes(), settings.Genesis.ID.Bytes()) { return errors.New("genesis blocks from state and config mismatch") @@ -946,9 +946,9 @@ func (s *stateManager) newestAssetBalance(addr proto.AddressID, asset proto.Asse // Something weird happened. return 0, err } - balance, err = diff.applyToAssetBalance(balance) - if err != nil { - return 0, errors.Errorf("given account has negative balance at this point: %v", err) + balance, aErr := diff.applyToAssetBalance(balance) + if aErr != nil { + return 0, errors.Errorf("given account has negative balance at this point: %v", aErr) } return balance, nil } @@ -1382,8 +1382,10 @@ func (s *stateManager) AddDeserializedBlocks( s.newBlocks.setNew(blocks) lastBlock, err := s.addBlocks() if err != nil { - if err = s.rw.syncWithDb(); err != nil { - zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", err) + if syncErr := s.rw.syncWithDb(); syncErr != nil { + zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", + syncErr, + ) } return nil, err } @@ -1613,8 +1615,8 @@ func (s *stateManager) blockchainHeightAction(blockchainHeight uint64, lastBlock return err } if termIsOver { - if err = s.updateBlockReward(lastBlock, blockchainHeight); err != nil { - return err + if ubrErr := s.updateBlockReward(lastBlock, blockchainHeight); ubrErr != nil { + return ubrErr } } return nil @@ -1817,8 +1819,8 @@ func (s *stateManager) addBlocks() (*proto.Block, error) { return nil, wrapErr(DeserializationError, errCurBlock) } - if err = s.beforeAddingBlock(block, lastAppliedBlock, blockchainCurHeight, chans); err != nil { - return nil, err + if badErr := s.beforeAddingBlock(block, lastAppliedBlock, blockchainCurHeight, chans); badErr != nil { + return nil, badErr } sh, errSh := s.stor.stateHashes.newestSnapshotStateHash(blockchainCurHeight) if errSh != nil { @@ -1877,12 +1879,12 @@ func (s *stateManager) addBlocks() (*proto.Block, error) { return nil, wrapErr(ModificationError, shErr) } // Validate consensus (i.e. that all the new blocks were mined fairly). - if err = s.cv.ValidateHeadersBatch(headers[:pos], height); err != nil { - return nil, wrapErr(ValidationError, err) + if vErr := s.cv.ValidateHeadersBatch(headers[:pos], height); vErr != nil { + return nil, wrapErr(ValidationError, vErr) } // After everything is validated, save all the changes to DB. - if err = s.flush(); err != nil { - return nil, wrapErr(ModificationError, err) + if fErr := s.flush(); fErr != nil { + return nil, wrapErr(ModificationError, fErr) } zap.S().Infof( "Height: %d; Block ID: %s, GenSig: %s, ts: %d", @@ -2973,15 +2975,9 @@ func (s *stateManager) TotalWavesAmount(height proto.Height) (uint64, error) { return 0, err } - var rewardBoostActivationHeight uint64 - var rewardBoostLastHeight uint64 - rewardBoostActivated := s.stor.features.isActivatedAtHeight(int16(settings.BoostBlockReward), height) - if rewardBoostActivated { - rewardBoostActivationHeight, err = s.stor.features.activationHeight(int16(settings.BoostBlockReward)) - if err != nil { - return 0, err - } - rewardBoostLastHeight = rewardBoostActivationHeight + s.settings.BlockRewardBoostPeriod - 1 + rewardBoostActivationHeight, rewardBoostLastHeight, err := rewardBoostFeatureInfo(height, s.stor.features, s.settings) + if err != nil { + return 0, err } amount, err := s.stor.monetaryPolicy.totalAmountAtHeight(height, initialTotalAmount, blockRewardActivationHeight, @@ -2992,6 +2988,23 @@ func (s *stateManager) TotalWavesAmount(height proto.Height) (uint64, error) { return amount, nil } +func rewardBoostFeatureInfo( + h proto.Height, + feat featuresState, + bs *settings.BlockchainSettings, +) (proto.Height, proto.Height, error) { + rewardBoostActivated := feat.isActivatedAtHeight(int16(settings.BoostBlockReward), h) + if !rewardBoostActivated { + return 0, 0, nil + } + rewardBoostActivationHeight, err := feat.activationHeight(int16(settings.BoostBlockReward)) + if err != nil { + return 0, 0, err + } + rewardBoostLastHeight := rewardBoostActivationHeight + bs.BlockRewardBoostPeriod - 1 + return rewardBoostActivationHeight, rewardBoostLastHeight, nil +} + func (s *stateManager) SnapshotsAtHeight(height proto.Height) (proto.BlockSnapshot, error) { return s.stor.snapshots.getSnapshots(height) } From 015a318b3149619b5b633ea2eb9d1e6444a261ef Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 20 Sep 2024 19:58:21 +0300 Subject: [PATCH 2/4] Add to fatal log message original error in some state methods. --- pkg/state/state.go | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/pkg/state/state.go b/pkg/state/state.go index d10bf1f17..ca7b67ec5 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -3,6 +3,7 @@ package state import ( "bytes" "context" + stderrs "errors" "fmt" "io/fs" "math/big" @@ -1332,8 +1333,10 @@ func (s *stateManager) AddBlock(block []byte) (*proto.Block, error) { s.newBlocks.setNewBinary([][]byte{block}) rs, err := s.addBlocks() if err != nil { - if err := s.rw.syncWithDb(); err != nil { - zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", err) + if syncErr := s.rw.syncWithDb(); syncErr != nil { + zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", + stderrs.Join(err, syncErr), + ) } return nil, err } @@ -1344,8 +1347,10 @@ func (s *stateManager) AddDeserializedBlock(block *proto.Block) (*proto.Block, e s.newBlocks.setNew([]*proto.Block{block}) rs, err := s.addBlocks() if err != nil { - if err := s.rw.syncWithDb(); err != nil { - zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", err) + if syncErr := s.rw.syncWithDb(); syncErr != nil { + zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", + stderrs.Join(err, syncErr), + ) } return nil, err } @@ -1355,8 +1360,10 @@ func (s *stateManager) AddDeserializedBlock(block *proto.Block) (*proto.Block, e func (s *stateManager) AddBlocks(blockBytes [][]byte) error { s.newBlocks.setNewBinary(blockBytes) if _, err := s.addBlocks(); err != nil { - if err := s.rw.syncWithDb(); err != nil { - zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", err) + if syncErr := s.rw.syncWithDb(); syncErr != nil { + zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", + stderrs.Join(err, syncErr), + ) } return err } @@ -1368,8 +1375,10 @@ func (s *stateManager) AddBlocksWithSnapshots(blockBytes [][]byte, snapshots []* return errors.Wrap(err, "failed to set new blocks with snapshots") } if _, err := s.addBlocks(); err != nil { - if snErr := s.rw.syncWithDb(); snErr != nil { - zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", snErr) + if syncErr := s.rw.syncWithDb(); syncErr != nil { + zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", + stderrs.Join(err, syncErr), + ) } return err } @@ -1384,7 +1393,7 @@ func (s *stateManager) AddDeserializedBlocks( if err != nil { if syncErr := s.rw.syncWithDb(); syncErr != nil { zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", - syncErr, + stderrs.Join(err, syncErr), ) } return nil, err @@ -1401,8 +1410,10 @@ func (s *stateManager) AddDeserializedBlocksWithSnapshots( } lastBlock, err := s.addBlocks() if err != nil { - if err := s.rw.syncWithDb(); err != nil { - zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", err) + if syncErr := s.rw.syncWithDb(); syncErr != nil { + zap.S().Fatalf("Failed to add blocks and can not sync block storage with the database after failure: %v", + stderrs.Join(err, syncErr), + ) } return nil, err } From 90b004ceee2f6a892e8f99e26d44113bd9ace704 Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Fri, 20 Sep 2024 20:08:43 +0300 Subject: [PATCH 3/4] Refactor a bit 'stateManager.addBlocks'. --- pkg/state/state.go | 114 +++++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/pkg/state/state.go b/pkg/state/state.go index ca7b67ec5..563f0fde1 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1787,14 +1787,14 @@ func (s *stateManager) recalculateVotesAfterCappedRewardActivationInVotingPeriod return nil } -func (s *stateManager) addBlocks() (*proto.Block, error) { +func (s *stateManager) addBlocks() (_ *proto.Block, retErr error) { //nolint:nonamedreturns // needs in defer ctx, cancel := context.WithCancel(context.Background()) defer cancel() defer func() { // Reset in-memory storages and load last block in defer. s.reset() - if err := s.loadLastBlock(); err != nil { - zap.S().Fatalf("Failed to load last block: %v", err) + if lbErr := s.loadLastBlock(); lbErr != nil { + zap.S().Fatalf("Failed to load last block: %v", stderrs.Join(retErr, lbErr)) } s.newBlocks.reset() }() @@ -1805,14 +1805,14 @@ func (s *stateManager) addBlocks() (*proto.Block, error) { } // Read some useful values for later. - lastAppliedBlock, err := s.topBlock() - if err != nil { - return nil, wrapErr(RetrievalError, err) + lastAppliedBlock, tbErr := s.topBlock() + if tbErr != nil { + return nil, wrapErr(RetrievalError, tbErr) } zap.S().Debugf("StateManager: parent (top) block ID: %s, ts: %d", lastAppliedBlock.BlockID().String(), lastAppliedBlock.Timestamp) - height, err := s.Height() - if err != nil { - return nil, wrapErr(RetrievalError, err) + height, hErr := s.Height() + if hErr != nil { + return nil, wrapErr(RetrievalError, hErr) } headers := make([]proto.BlockHeader, blocksNumber) @@ -1830,47 +1830,9 @@ func (s *stateManager) addBlocks() (*proto.Block, error) { return nil, wrapErr(DeserializationError, errCurBlock) } - if badErr := s.beforeAddingBlock(block, lastAppliedBlock, blockchainCurHeight, chans); badErr != nil { - return nil, badErr - } - sh, errSh := s.stor.stateHashes.newestSnapshotStateHash(blockchainCurHeight) - if errSh != nil { - return nil, errors.Wrapf(errSh, "failed to get newest snapshot state hash for height %d", - blockchainCurHeight, - ) - } - - // Generate blockchain fix snapshots for the applying block. - fixSnapshots, gbfErr := s.generateBlockchainFix(blockchainCurHeight+1, block.BlockID(), false) - if gbfErr != nil { - return nil, errors.Wrapf(gbfErr, "failed to generate blockchain fix snapshots at block %s", - block.BlockID().String(), - ) - } - if sbfErr := s.saveBlockchainFix(block.BlockID(), fixSnapshots); sbfErr != nil { - return nil, wrapErr(ModificationError, errors.Wrapf(sbfErr, "failed to save blockchain fix for block %s", - block.BlockID().String()), - ) - } - - fixSnapshotsToInitialHash := fixSnapshots // at the block applying stage fix snapshots are only used for hashing - // Save block to storage, check its transactions, create and save balance diffs for its transactions. - addErr := s.addNewBlock( - block, lastAppliedBlock, chans, blockchainCurHeight, optionalSnapshot, fixSnapshotsToInitialHash, sh) - if addErr != nil { - return nil, addErr - } - if fixErr := s.applyBlockchainFix(block.BlockID(), fixSnapshots); fixErr != nil { - return nil, errors.Wrapf(fixErr, "failed to apply fix snapshots after block %s applying", - block.BlockID().String(), - ) - } - blockchainCurHeight++ // we've just added a new block and applied blockchain fix, so we have a new height - - if s.needToFinishVotingPeriod(blockchainCurHeight) { - // If we need to finish voting period on the next block (h+1) then - // we have to check that protobuf will be activated on next block - s.checkProtobufActivation(blockchainCurHeight + 1) + pErr := s.processBlockInPack(block, optionalSnapshot, lastAppliedBlock, blockchainCurHeight, chans) + if pErr != nil { + return nil, pErr } // Prepare for the next iteration. @@ -1907,6 +1869,58 @@ func (s *stateManager) addBlocks() (*proto.Block, error) { return lastAppliedBlock, nil } +func (s *stateManager) processBlockInPack( + block *proto.Block, + optionalSnapshot *proto.BlockSnapshot, + lastAppliedBlock *proto.Block, + blockchainCurHeight uint64, + chans *verifierChans, +) error { + if badErr := s.beforeAddingBlock(block, lastAppliedBlock, blockchainCurHeight, chans); badErr != nil { + return badErr + } + sh, errSh := s.stor.stateHashes.newestSnapshotStateHash(blockchainCurHeight) + if errSh != nil { + return errors.Wrapf(errSh, "failed to get newest snapshot state hash for height %d", + blockchainCurHeight, + ) + } + + // Generate blockchain fix snapshots for the applying block. + fixSnapshots, gbfErr := s.generateBlockchainFix(blockchainCurHeight+1, block.BlockID(), false) + if gbfErr != nil { + return errors.Wrapf(gbfErr, "failed to generate blockchain fix snapshots at block %s", + block.BlockID().String(), + ) + } + if sbfErr := s.saveBlockchainFix(block.BlockID(), fixSnapshots); sbfErr != nil { + return wrapErr(ModificationError, errors.Wrapf(sbfErr, "failed to save blockchain fix for block %s", + block.BlockID().String()), + ) + } + + fixSnapshotsToInitialHash := fixSnapshots // at the block applying stage fix snapshots are only used for hashing + // Save block to storage, check its transactions, create and save balance diffs for its transactions. + addErr := s.addNewBlock( + block, lastAppliedBlock, chans, blockchainCurHeight, optionalSnapshot, fixSnapshotsToInitialHash, sh) + if addErr != nil { + return addErr + } + if fixErr := s.applyBlockchainFix(block.BlockID(), fixSnapshots); fixErr != nil { + return errors.Wrapf(fixErr, "failed to apply fix snapshots after block %s applying", + block.BlockID().String(), + ) + } + blockchainCurHeight++ // we've just added a new block and applied blockchain fix, so we have a new height + + if s.needToFinishVotingPeriod(blockchainCurHeight) { + // If we need to finish voting period on the next block (h+1) then + // we have to check that protobuf will be activated on next block + s.checkProtobufActivation(blockchainCurHeight + 1) + } + return nil +} + func (s *stateManager) beforeAddingBlock( block, lastAppliedBlock *proto.Block, blockchainCurHeight proto.Height, From 9dc4776396e0fd2a12dfb52d358cab44f643985d Mon Sep 17 00:00:00 2001 From: Nikolay Eskov Date: Mon, 23 Sep 2024 23:14:23 +0300 Subject: [PATCH 4/4] Add info about block height in statehash mismatch error. --- pkg/state/appender.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/state/appender.go b/pkg/state/appender.go index f68870772..56ff0e6f0 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -836,8 +836,9 @@ func (a *txAppender) appendBlock(params *appendBlockParams) error { } // check whether the calculated snapshot state hash equals with the provided one if blockStateHash, present := params.block.GetStateHash(); present && blockStateHash != stateHash { - return errors.Wrapf(errBlockSnapshotStateHashMismatch, "state hash mismatch; provided '%s', caluclated '%s'", - blockStateHash.String(), stateHash.String(), + return errors.Wrapf(errBlockSnapshotStateHashMismatch, + "block %d state hash mismatch — provided '%s', caluclated '%s'", + currentBlockHeight, blockStateHash.String(), stateHash.String(), ) }