From 863169367792ff498c02c2257b4c71ee99461ec6 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Thu, 30 Nov 2023 11:39:43 +0100 Subject: [PATCH] introduced post durango verification --- vms/proposervm/batched_vm_test.go | 1 + vms/proposervm/block.go | 90 ++++++++++++++++-------- vms/proposervm/block_test.go | 1 + vms/proposervm/config.go | 4 ++ vms/proposervm/post_fork_option_test.go | 1 + vms/proposervm/state_syncable_vm_test.go | 1 + vms/proposervm/vm_regression_test.go | 2 + 7 files changed, 70 insertions(+), 30 deletions(-) diff --git a/vms/proposervm/batched_vm_test.go b/vms/proposervm/batched_vm_test.go index a7838dec56c0..920b8f43b38a 100644 --- a/vms/proposervm/batched_vm_test.go +++ b/vms/proposervm/batched_vm_test.go @@ -1025,6 +1025,7 @@ func initTestRemoteProposerVM( proVM := New( Config{ ActivationTime: proBlkStartTime, + DurangoTime: mockable.MaxTime, MinimumPChainHeight: 0, MinBlkDelay: DefaultMinBlockDelay, NumHistoricalBlocks: DefaultNumHistoricalBlocks, diff --git a/vms/proposervm/block.go b/vms/proposervm/block.go index 86196eaf6f6a..2b72f78c6316 100644 --- a/vms/proposervm/block.go +++ b/vms/proposervm/block.go @@ -35,6 +35,7 @@ var ( errPChainHeightNotReached = errors.New("block P-chain height larger than current P-chain height") errTimeTooAdvanced = errors.New("time is too far advanced") errProposerWindowNotStarted = errors.New("proposer window hasn't started") + errUnexpectedProposer = errors.New("unexpected proposer for current window") errProposersNotActivated = errors.New("proposers haven't been activated yet") errPChainHeightTooLow = errors.New("block P-chain height is too low") ) @@ -124,13 +125,39 @@ func (p *postForkCommonComponents) Verify( // If the node is currently syncing - we don't assume that the P-chain has // been synced up to this point yet. if p.vm.consensusState == snow.NormalOp { - delay, minDelay, err := p.verifyBlockDelay(ctx, parentTimestamp, parentPChainHeight, child) + currentPChainHeight, err := p.vm.ctx.ValidatorState.GetCurrentHeight(ctx) if err != nil { + p.vm.ctx.Log.Error("block verification failed", + zap.String("reason", "failed to get current P-Chain height"), + zap.Stringer("blkID", child.ID()), + zap.Error(err), + ) return err } + if childPChainHeight > currentPChainHeight { + return fmt.Errorf("%w: %d > %d", + errPChainHeightNotReached, + childPChainHeight, + currentPChainHeight, + ) + } + + shouldHaveProposer := true // post Durango this is always the case + if p.vm.IsDurangoActivated(parentTimestamp) { + err := p.verifyPostDurangoBlockDelay(ctx, parentTimestamp, parentPChainHeight, child) + if err != nil { + return err + } + } else { + delay, err := p.verifyPreDurangoBlockDelay(ctx, parentTimestamp, parentPChainHeight, child) + if err != nil { + return err + } + + shouldHaveProposer = delay < proposer.MaxVerifyDelay + } // Verify the signature of the node - shouldHaveProposer := delay < proposer.MaxVerifyDelay if err := child.SignedBlock.Verify(shouldHaveProposer, p.vm.ctx.ChainID); err != nil { return err } @@ -138,7 +165,6 @@ func (p *postForkCommonComponents) Verify( p.vm.ctx.Log.Debug("verified post-fork block", zap.Stringer("blkID", child.ID()), zap.Time("parentTimestamp", parentTimestamp), - zap.Duration("minDelay", minDelay), zap.Time("blockTimestamp", childTimestamp), ) } @@ -310,45 +336,49 @@ func verifyIsNotOracleBlock(ctx context.Context, b snowman.Block) error { } } -func (p *postForkCommonComponents) verifyBlockDelay( +func (p *postForkCommonComponents) verifyPreDurangoBlockDelay( ctx context.Context, parentTimestamp time.Time, parentPChainHeight uint64, blk *postForkBlock, -) (time.Duration, time.Duration, error) { +) (time.Duration, error) { var ( - blkID = blk.ID() - blkPChainHeight = blk.PChainHeight() - blkTimestamp = blk.Timestamp() + blkTimestamp = blk.Timestamp() + childHeight = blk.Height() + proposerID = blk.Proposer() ) - currentPChainHeight, err := p.vm.ctx.ValidatorState.GetCurrentHeight(ctx) - if err != nil { - p.vm.ctx.Log.Error("block verification failed", - zap.String("reason", "failed to get current P-Chain height"), - zap.Stringer("blkID", blkID), - zap.Error(err), - ) - return 0, 0, err - } - if blkPChainHeight > currentPChainHeight { - return 0, 0, fmt.Errorf("%w: %d > %d", - errPChainHeightNotReached, - blkPChainHeight, - currentPChainHeight, - ) - } - - childHeight := blk.Height() - proposerID := blk.Proposer() minDelay, err := p.vm.Windower.Delay(ctx, childHeight, parentPChainHeight, proposerID, proposer.MaxVerifyWindows) if err != nil { - return 0, 0, err + return 0, err } delay := blkTimestamp.Sub(parentTimestamp) if delay < minDelay { - return 0, 0, errProposerWindowNotStarted + return 0, errProposerWindowNotStarted + } + + return delay, nil +} + +func (p *postForkCommonComponents) verifyPostDurangoBlockDelay( + ctx context.Context, + parentTimestamp time.Time, + parentPChainHeight uint64, + blk *postForkBlock, +) error { + var ( + blkTimestamp = blk.Timestamp() + blkHeight = blk.Height() + proposerID = blk.Proposer() + ) + + expectedProposerID, err := p.vm.Windower.ExpectedProposer(ctx, blkHeight, parentPChainHeight, blkTimestamp, parentTimestamp) + if err != nil { + return err + } + if expectedProposerID != proposerID { + return errUnexpectedProposer } - return delay, minDelay, nil + return nil } diff --git a/vms/proposervm/block_test.go b/vms/proposervm/block_test.go index 2bdc4e104c0d..b3fd8d0d5dbc 100644 --- a/vms/proposervm/block_test.go +++ b/vms/proposervm/block_test.go @@ -62,6 +62,7 @@ func TestPostForkCommonComponents_buildChild(t *testing.T) { require.NoError(err) vm := &VM{ Config: Config{ + DurangoTime: mockable.MaxTime, StakingCertLeaf: &staking.Certificate{}, StakingLeafSigner: pk, }, diff --git a/vms/proposervm/config.go b/vms/proposervm/config.go index 05d0f9f42cf8..6b2b7a64d20b 100644 --- a/vms/proposervm/config.go +++ b/vms/proposervm/config.go @@ -23,3 +23,7 @@ type Config struct { // block certificate StakingCertLeaf *staking.Certificate } + +func (c *Config) IsDurangoActivated(timestamp time.Time) bool { + return !timestamp.Before(c.DurangoTime) +} diff --git a/vms/proposervm/post_fork_option_test.go b/vms/proposervm/post_fork_option_test.go index b5fdb3326248..83ba84528e30 100644 --- a/vms/proposervm/post_fork_option_test.go +++ b/vms/proposervm/post_fork_option_test.go @@ -686,6 +686,7 @@ func TestOptionTimestampValidity(t *testing.T) { proVM = New( Config{ ActivationTime: time.Time{}, + DurangoTime: mockable.MaxTime, MinimumPChainHeight: 0, MinBlkDelay: DefaultMinBlockDelay, NumHistoricalBlocks: DefaultNumHistoricalBlocks, diff --git a/vms/proposervm/state_syncable_vm_test.go b/vms/proposervm/state_syncable_vm_test.go index 0c9a4c20c5a3..afb85f375a49 100644 --- a/vms/proposervm/state_syncable_vm_test.go +++ b/vms/proposervm/state_syncable_vm_test.go @@ -72,6 +72,7 @@ func helperBuildStateSyncTestObjects(t *testing.T) (*fullVM, *VM) { vm := New( Config{ ActivationTime: time.Time{}, + DurangoTime: mockable.MaxTime, MinimumPChainHeight: 0, MinBlkDelay: DefaultMinBlockDelay, NumHistoricalBlocks: DefaultNumHistoricalBlocks, diff --git a/vms/proposervm/vm_regression_test.go b/vms/proposervm/vm_regression_test.go index 792090a1f5cf..ee17d4e5d983 100644 --- a/vms/proposervm/vm_regression_test.go +++ b/vms/proposervm/vm_regression_test.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/avalanchego/utils/timer/mockable" ) func TestProposerVMInitializeShouldFailIfInnerVMCantVerifyItsHeightIndex(t *testing.T) { @@ -47,6 +48,7 @@ func TestProposerVMInitializeShouldFailIfInnerVMCantVerifyItsHeightIndex(t *test proVM := New( Config{ ActivationTime: time.Time{}, + DurangoTime: mockable.MaxTime, MinimumPChainHeight: 0, MinBlkDelay: DefaultMinBlockDelay, NumHistoricalBlocks: DefaultNumHistoricalBlocks,