github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/block/executor/verifier_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package executor 5 6 import ( 7 "context" 8 "testing" 9 "time" 10 11 "github.com/prometheus/client_golang/prometheus" 12 "github.com/stretchr/testify/require" 13 "go.uber.org/mock/gomock" 14 15 "github.com/ava-labs/avalanchego/chains/atomic" 16 "github.com/ava-labs/avalanchego/database" 17 "github.com/ava-labs/avalanchego/genesis" 18 "github.com/ava-labs/avalanchego/ids" 19 "github.com/ava-labs/avalanchego/snow" 20 "github.com/ava-labs/avalanchego/snow/snowtest" 21 "github.com/ava-labs/avalanchego/upgrade/upgradetest" 22 "github.com/ava-labs/avalanchego/utils" 23 "github.com/ava-labs/avalanchego/utils/constants" 24 "github.com/ava-labs/avalanchego/utils/logging" 25 "github.com/ava-labs/avalanchego/utils/set" 26 "github.com/ava-labs/avalanchego/utils/timer/mockable" 27 "github.com/ava-labs/avalanchego/vms/components/avax" 28 "github.com/ava-labs/avalanchego/vms/components/gas" 29 "github.com/ava-labs/avalanchego/vms/components/verify" 30 "github.com/ava-labs/avalanchego/vms/platformvm/block" 31 "github.com/ava-labs/avalanchego/vms/platformvm/config" 32 "github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest" 33 "github.com/ava-labs/avalanchego/vms/platformvm/state" 34 "github.com/ava-labs/avalanchego/vms/platformvm/state/statetest" 35 "github.com/ava-labs/avalanchego/vms/platformvm/status" 36 "github.com/ava-labs/avalanchego/vms/platformvm/txs" 37 "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" 38 "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" 39 "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" 40 "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool/mempoolmock" 41 "github.com/ava-labs/avalanchego/vms/platformvm/txs/txsmock" 42 "github.com/ava-labs/avalanchego/vms/platformvm/txs/txstest" 43 "github.com/ava-labs/avalanchego/vms/platformvm/utxo" 44 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 45 ) 46 47 func newTestVerifier(t testing.TB, s state.State) *verifier { 48 require := require.New(t) 49 50 mempool, err := mempool.New("", prometheus.NewRegistry(), nil) 51 require.NoError(err) 52 53 var ( 54 upgrades = upgradetest.GetConfig(upgradetest.Latest) 55 ctx = snowtest.Context(t, constants.PlatformChainID) 56 clock = &mockable.Clock{} 57 fx = &secp256k1fx.Fx{} 58 ) 59 require.NoError(fx.InitializeVM(&secp256k1fx.TestVM{ 60 Clk: *clock, 61 Log: logging.NoLog{}, 62 })) 63 64 return &verifier{ 65 backend: &backend{ 66 Mempool: mempool, 67 lastAccepted: s.GetLastAccepted(), 68 blkIDToState: make(map[ids.ID]*blockState), 69 state: s, 70 ctx: ctx, 71 }, 72 txExecutorBackend: &executor.Backend{ 73 Config: &config.Config{ 74 CreateAssetTxFee: genesis.LocalParams.CreateAssetTxFee, 75 StaticFeeConfig: genesis.LocalParams.StaticFeeConfig, 76 DynamicFeeConfig: genesis.LocalParams.DynamicFeeConfig, 77 SybilProtectionEnabled: true, 78 UpgradeConfig: upgrades, 79 }, 80 Ctx: ctx, 81 Clk: clock, 82 Fx: fx, 83 FlowChecker: utxo.NewVerifier( 84 ctx, 85 clock, 86 fx, 87 ), 88 Bootstrapped: utils.NewAtomic(true), 89 }, 90 } 91 } 92 93 func TestVerifierVisitProposalBlock(t *testing.T) { 94 require := require.New(t) 95 ctrl := gomock.NewController(t) 96 97 s := state.NewMockState(ctrl) 98 mempool := mempoolmock.NewMempool(ctrl) 99 parentID := ids.GenerateTestID() 100 parentStatelessBlk := block.NewMockBlock(ctrl) 101 parentOnAcceptState := state.NewMockDiff(ctrl) 102 timestamp := time.Now() 103 // One call for each of onCommitState and onAbortState. 104 parentOnAcceptState.EXPECT().GetTimestamp().Return(timestamp).Times(2) 105 parentOnAcceptState.EXPECT().GetFeeState().Return(gas.State{}).Times(2) 106 107 backend := &backend{ 108 lastAccepted: parentID, 109 blkIDToState: map[ids.ID]*blockState{ 110 parentID: { 111 statelessBlock: parentStatelessBlk, 112 onAcceptState: parentOnAcceptState, 113 }, 114 }, 115 Mempool: mempool, 116 state: s, 117 ctx: &snow.Context{ 118 Log: logging.NoLog{}, 119 }, 120 } 121 manager := &manager{ 122 txExecutorBackend: &executor.Backend{ 123 Config: &config.Config{ 124 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 125 }, 126 Clk: &mockable.Clock{}, 127 }, 128 backend: backend, 129 } 130 131 blkTx := txsmock.NewUnsignedTx(ctrl) 132 blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.ProposalTxExecutor{})).Return(nil).Times(1) 133 134 // We can't serialize [blkTx] because it isn't 135 // registered with the blocks.Codec. 136 // Serialize this block with a dummy tx 137 // and replace it after creation with the mock tx. 138 // TODO allow serialization of mock txs. 139 apricotBlk, err := block.NewApricotProposalBlock( 140 parentID, 141 2, 142 &txs.Tx{ 143 Unsigned: &txs.AdvanceTimeTx{}, 144 Creds: []verify.Verifiable{}, 145 }, 146 ) 147 require.NoError(err) 148 apricotBlk.Tx.Unsigned = blkTx 149 150 // Set expectations for dependencies. 151 tx := apricotBlk.Txs()[0] 152 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 153 mempool.EXPECT().Remove([]*txs.Tx{tx}).Times(1) 154 155 // Visit the block 156 blk := manager.NewBlock(apricotBlk) 157 require.NoError(blk.Verify(context.Background())) 158 require.Contains(manager.backend.blkIDToState, apricotBlk.ID()) 159 gotBlkState := manager.backend.blkIDToState[apricotBlk.ID()] 160 require.Equal(apricotBlk, gotBlkState.statelessBlock) 161 require.Equal(timestamp, gotBlkState.timestamp) 162 163 // Assert that the expected tx statuses are set. 164 _, gotStatus, err := gotBlkState.onCommitState.GetTx(tx.ID()) 165 require.NoError(err) 166 require.Equal(status.Committed, gotStatus) 167 168 _, gotStatus, err = gotBlkState.onAbortState.GetTx(tx.ID()) 169 require.NoError(err) 170 require.Equal(status.Aborted, gotStatus) 171 172 // Visiting again should return nil without using dependencies. 173 require.NoError(blk.Verify(context.Background())) 174 } 175 176 func TestVerifierVisitAtomicBlock(t *testing.T) { 177 require := require.New(t) 178 ctrl := gomock.NewController(t) 179 180 // Create mocked dependencies. 181 s := state.NewMockState(ctrl) 182 mempool := mempoolmock.NewMempool(ctrl) 183 parentID := ids.GenerateTestID() 184 parentStatelessBlk := block.NewMockBlock(ctrl) 185 grandparentID := ids.GenerateTestID() 186 parentState := state.NewMockDiff(ctrl) 187 188 backend := &backend{ 189 blkIDToState: map[ids.ID]*blockState{ 190 parentID: { 191 statelessBlock: parentStatelessBlk, 192 onAcceptState: parentState, 193 }, 194 }, 195 Mempool: mempool, 196 state: s, 197 ctx: &snow.Context{ 198 Log: logging.NoLog{}, 199 }, 200 } 201 manager := &manager{ 202 txExecutorBackend: &executor.Backend{ 203 Config: &config.Config{ 204 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 205 }, 206 Clk: &mockable.Clock{}, 207 }, 208 backend: backend, 209 } 210 211 onAccept := state.NewMockDiff(ctrl) 212 blkTx := txsmock.NewUnsignedTx(ctrl) 213 inputs := set.Of(ids.GenerateTestID()) 214 blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.AtomicTxExecutor{})).DoAndReturn( 215 func(e *executor.AtomicTxExecutor) error { 216 e.OnAccept = onAccept 217 e.Inputs = inputs 218 return nil 219 }, 220 ).Times(1) 221 222 // We can't serialize [blkTx] because it isn't registered with blocks.Codec. 223 // Serialize this block with a dummy tx and replace it after creation with 224 // the mock tx. 225 // TODO allow serialization of mock txs. 226 apricotBlk, err := block.NewApricotAtomicBlock( 227 parentID, 228 2, 229 &txs.Tx{ 230 Unsigned: &txs.AdvanceTimeTx{}, 231 Creds: []verify.Verifiable{}, 232 }, 233 ) 234 require.NoError(err) 235 apricotBlk.Tx.Unsigned = blkTx 236 237 // Set expectations for dependencies. 238 timestamp := time.Now() 239 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 240 parentStatelessBlk.EXPECT().Parent().Return(grandparentID).Times(1) 241 mempool.EXPECT().Remove([]*txs.Tx{apricotBlk.Tx}).Times(1) 242 onAccept.EXPECT().AddTx(apricotBlk.Tx, status.Committed).Times(1) 243 onAccept.EXPECT().GetTimestamp().Return(timestamp).Times(1) 244 245 blk := manager.NewBlock(apricotBlk) 246 require.NoError(blk.Verify(context.Background())) 247 248 require.Contains(manager.backend.blkIDToState, apricotBlk.ID()) 249 gotBlkState := manager.backend.blkIDToState[apricotBlk.ID()] 250 require.Equal(apricotBlk, gotBlkState.statelessBlock) 251 require.Equal(onAccept, gotBlkState.onAcceptState) 252 require.Equal(inputs, gotBlkState.inputs) 253 require.Equal(timestamp, gotBlkState.timestamp) 254 255 // Visiting again should return nil without using dependencies. 256 require.NoError(blk.Verify(context.Background())) 257 } 258 259 func TestVerifierVisitStandardBlock(t *testing.T) { 260 require := require.New(t) 261 ctrl := gomock.NewController(t) 262 263 // Create mocked dependencies. 264 s := state.NewMockState(ctrl) 265 mempool := mempoolmock.NewMempool(ctrl) 266 parentID := ids.GenerateTestID() 267 parentStatelessBlk := block.NewMockBlock(ctrl) 268 parentState := state.NewMockDiff(ctrl) 269 270 backend := &backend{ 271 blkIDToState: map[ids.ID]*blockState{ 272 parentID: { 273 statelessBlock: parentStatelessBlk, 274 onAcceptState: parentState, 275 }, 276 }, 277 Mempool: mempool, 278 state: s, 279 ctx: &snow.Context{ 280 Log: logging.NoLog{}, 281 }, 282 } 283 manager := &manager{ 284 txExecutorBackend: &executor.Backend{ 285 Config: &config.Config{ 286 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 287 }, 288 Clk: &mockable.Clock{}, 289 }, 290 backend: backend, 291 } 292 293 blkTx := txsmock.NewUnsignedTx(ctrl) 294 atomicRequests := map[ids.ID]*atomic.Requests{ 295 ids.GenerateTestID(): { 296 RemoveRequests: [][]byte{{1}, {2}}, 297 PutRequests: []*atomic.Element{ 298 { 299 Key: []byte{3}, 300 Value: []byte{4}, 301 Traits: [][]byte{{5}, {6}}, 302 }, 303 }, 304 }, 305 } 306 blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.StandardTxExecutor{})).DoAndReturn( 307 func(e *executor.StandardTxExecutor) error { 308 e.OnAccept = func() {} 309 e.Inputs = set.Set[ids.ID]{} 310 e.AtomicRequests = atomicRequests 311 return nil 312 }, 313 ).Times(1) 314 315 // We can't serialize [blkTx] because it isn't 316 // registered with the blocks.Codec. 317 // Serialize this block with a dummy tx 318 // and replace it after creation with the mock tx. 319 // TODO allow serialization of mock txs. 320 apricotBlk, err := block.NewApricotStandardBlock( 321 parentID, 322 2, /*height*/ 323 []*txs.Tx{ 324 { 325 Unsigned: &txs.AdvanceTimeTx{}, 326 Creds: []verify.Verifiable{}, 327 }, 328 }, 329 ) 330 require.NoError(err) 331 apricotBlk.Transactions[0].Unsigned = blkTx 332 333 // Set expectations for dependencies. 334 timestamp := time.Now() 335 parentState.EXPECT().GetTimestamp().Return(timestamp).Times(1) 336 parentState.EXPECT().GetFeeState().Return(gas.State{}).Times(1) 337 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 338 mempool.EXPECT().Remove(apricotBlk.Txs()).Times(1) 339 340 blk := manager.NewBlock(apricotBlk) 341 require.NoError(blk.Verify(context.Background())) 342 343 // Assert expected state. 344 require.Contains(manager.backend.blkIDToState, apricotBlk.ID()) 345 gotBlkState := manager.backend.blkIDToState[apricotBlk.ID()] 346 require.Equal(apricotBlk, gotBlkState.statelessBlock) 347 require.Equal(set.Set[ids.ID]{}, gotBlkState.inputs) 348 require.Equal(timestamp, gotBlkState.timestamp) 349 350 // Visiting again should return nil without using dependencies. 351 require.NoError(blk.Verify(context.Background())) 352 } 353 354 func TestVerifierVisitCommitBlock(t *testing.T) { 355 require := require.New(t) 356 ctrl := gomock.NewController(t) 357 358 // Create mocked dependencies. 359 s := state.NewMockState(ctrl) 360 mempool := mempoolmock.NewMempool(ctrl) 361 parentID := ids.GenerateTestID() 362 parentStatelessBlk := block.NewMockBlock(ctrl) 363 parentOnDecisionState := state.NewMockDiff(ctrl) 364 parentOnCommitState := state.NewMockDiff(ctrl) 365 parentOnAbortState := state.NewMockDiff(ctrl) 366 367 backend := &backend{ 368 blkIDToState: map[ids.ID]*blockState{ 369 parentID: { 370 statelessBlock: parentStatelessBlk, 371 proposalBlockState: proposalBlockState{ 372 onDecisionState: parentOnDecisionState, 373 onCommitState: parentOnCommitState, 374 onAbortState: parentOnAbortState, 375 }, 376 }, 377 }, 378 Mempool: mempool, 379 state: s, 380 ctx: &snow.Context{ 381 Log: logging.NoLog{}, 382 }, 383 } 384 manager := &manager{ 385 txExecutorBackend: &executor.Backend{ 386 Config: &config.Config{ 387 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 388 }, 389 Clk: &mockable.Clock{}, 390 }, 391 backend: backend, 392 } 393 394 apricotBlk, err := block.NewApricotCommitBlock( 395 parentID, 396 2, 397 ) 398 require.NoError(err) 399 400 // Set expectations for dependencies. 401 timestamp := time.Now() 402 gomock.InOrder( 403 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1), 404 parentOnCommitState.EXPECT().GetTimestamp().Return(timestamp).Times(1), 405 ) 406 407 // Verify the block. 408 blk := manager.NewBlock(apricotBlk) 409 require.NoError(blk.Verify(context.Background())) 410 411 // Assert expected state. 412 require.Contains(manager.backend.blkIDToState, apricotBlk.ID()) 413 gotBlkState := manager.backend.blkIDToState[apricotBlk.ID()] 414 require.Equal(parentOnAbortState, gotBlkState.onAcceptState) 415 require.Equal(timestamp, gotBlkState.timestamp) 416 417 // Visiting again should return nil without using dependencies. 418 require.NoError(blk.Verify(context.Background())) 419 } 420 421 func TestVerifierVisitAbortBlock(t *testing.T) { 422 require := require.New(t) 423 ctrl := gomock.NewController(t) 424 425 // Create mocked dependencies. 426 s := state.NewMockState(ctrl) 427 mempool := mempoolmock.NewMempool(ctrl) 428 parentID := ids.GenerateTestID() 429 parentStatelessBlk := block.NewMockBlock(ctrl) 430 parentOnDecisionState := state.NewMockDiff(ctrl) 431 parentOnCommitState := state.NewMockDiff(ctrl) 432 parentOnAbortState := state.NewMockDiff(ctrl) 433 434 backend := &backend{ 435 blkIDToState: map[ids.ID]*blockState{ 436 parentID: { 437 statelessBlock: parentStatelessBlk, 438 proposalBlockState: proposalBlockState{ 439 onDecisionState: parentOnDecisionState, 440 onCommitState: parentOnCommitState, 441 onAbortState: parentOnAbortState, 442 }, 443 }, 444 }, 445 Mempool: mempool, 446 state: s, 447 ctx: &snow.Context{ 448 Log: logging.NoLog{}, 449 }, 450 } 451 manager := &manager{ 452 txExecutorBackend: &executor.Backend{ 453 Config: &config.Config{ 454 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 455 }, 456 Clk: &mockable.Clock{}, 457 }, 458 backend: backend, 459 } 460 461 apricotBlk, err := block.NewApricotAbortBlock( 462 parentID, 463 2, 464 ) 465 require.NoError(err) 466 467 // Set expectations for dependencies. 468 timestamp := time.Now() 469 gomock.InOrder( 470 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1), 471 parentOnAbortState.EXPECT().GetTimestamp().Return(timestamp).Times(1), 472 ) 473 474 // Verify the block. 475 blk := manager.NewBlock(apricotBlk) 476 require.NoError(blk.Verify(context.Background())) 477 478 // Assert expected state. 479 require.Contains(manager.backend.blkIDToState, apricotBlk.ID()) 480 gotBlkState := manager.backend.blkIDToState[apricotBlk.ID()] 481 require.Equal(parentOnAbortState, gotBlkState.onAcceptState) 482 require.Equal(timestamp, gotBlkState.timestamp) 483 484 // Visiting again should return nil without using dependencies. 485 require.NoError(blk.Verify(context.Background())) 486 } 487 488 // Assert that a block with an unverified parent fails verification. 489 func TestVerifyUnverifiedParent(t *testing.T) { 490 require := require.New(t) 491 ctrl := gomock.NewController(t) 492 493 // Create mocked dependencies. 494 s := state.NewMockState(ctrl) 495 mempool := mempoolmock.NewMempool(ctrl) 496 parentID := ids.GenerateTestID() 497 498 backend := &backend{ 499 blkIDToState: map[ids.ID]*blockState{}, 500 Mempool: mempool, 501 state: s, 502 ctx: &snow.Context{ 503 Log: logging.NoLog{}, 504 }, 505 } 506 verifier := &verifier{ 507 txExecutorBackend: &executor.Backend{ 508 Config: &config.Config{ 509 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 510 }, 511 Clk: &mockable.Clock{}, 512 }, 513 backend: backend, 514 } 515 516 blk, err := block.NewApricotAbortBlock(parentID /*not in memory or persisted state*/, 2 /*height*/) 517 require.NoError(err) 518 519 // Set expectations for dependencies. 520 s.EXPECT().GetTimestamp().Return(time.Now()).Times(1) 521 s.EXPECT().GetStatelessBlock(parentID).Return(nil, database.ErrNotFound).Times(1) 522 523 // Verify the block. 524 err = blk.Visit(verifier) 525 require.ErrorIs(err, database.ErrNotFound) 526 } 527 528 func TestBanffAbortBlockTimestampChecks(t *testing.T) { 529 ctrl := gomock.NewController(t) 530 531 now := genesistest.DefaultValidatorStartTime.Add(time.Hour) 532 533 tests := []struct { 534 description string 535 parentTime time.Time 536 childTime time.Time 537 result error 538 }{ 539 { 540 description: "abort block timestamp matching parent's one", 541 parentTime: now, 542 childTime: now, 543 result: nil, 544 }, 545 { 546 description: "abort block timestamp before parent's one", 547 childTime: now.Add(-1 * time.Second), 548 parentTime: now, 549 result: errOptionBlockTimestampNotMatchingParent, 550 }, 551 { 552 description: "abort block timestamp after parent's one", 553 parentTime: now, 554 childTime: now.Add(time.Second), 555 result: errOptionBlockTimestampNotMatchingParent, 556 }, 557 } 558 559 for _, test := range tests { 560 t.Run(test.description, func(t *testing.T) { 561 require := require.New(t) 562 563 // Create mocked dependencies. 564 s := state.NewMockState(ctrl) 565 mempool := mempoolmock.NewMempool(ctrl) 566 parentID := ids.GenerateTestID() 567 parentStatelessBlk := block.NewMockBlock(ctrl) 568 parentHeight := uint64(1) 569 570 backend := &backend{ 571 blkIDToState: make(map[ids.ID]*blockState), 572 Mempool: mempool, 573 state: s, 574 ctx: &snow.Context{ 575 Log: logging.NoLog{}, 576 }, 577 } 578 verifier := &verifier{ 579 txExecutorBackend: &executor.Backend{ 580 Config: &config.Config{ 581 UpgradeConfig: upgradetest.GetConfig(upgradetest.Banff), 582 }, 583 Clk: &mockable.Clock{}, 584 }, 585 backend: backend, 586 } 587 588 // build and verify child block 589 childHeight := parentHeight + 1 590 statelessAbortBlk, err := block.NewBanffAbortBlock(test.childTime, parentID, childHeight) 591 require.NoError(err) 592 593 // setup parent state 594 parentTime := genesistest.DefaultValidatorStartTime 595 s.EXPECT().GetLastAccepted().Return(parentID).Times(3) 596 s.EXPECT().GetTimestamp().Return(parentTime).Times(3) 597 s.EXPECT().GetFeeState().Return(gas.State{}).Times(3) 598 599 onDecisionState, err := state.NewDiff(parentID, backend) 600 require.NoError(err) 601 onCommitState, err := state.NewDiff(parentID, backend) 602 require.NoError(err) 603 onAbortState, err := state.NewDiff(parentID, backend) 604 require.NoError(err) 605 backend.blkIDToState[parentID] = &blockState{ 606 timestamp: test.parentTime, 607 statelessBlock: parentStatelessBlk, 608 proposalBlockState: proposalBlockState{ 609 onDecisionState: onDecisionState, 610 onCommitState: onCommitState, 611 onAbortState: onAbortState, 612 }, 613 } 614 615 // Set expectations for dependencies. 616 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 617 618 err = statelessAbortBlk.Visit(verifier) 619 require.ErrorIs(err, test.result) 620 }) 621 } 622 } 623 624 // TODO combine with TestApricotCommitBlockTimestampChecks 625 func TestBanffCommitBlockTimestampChecks(t *testing.T) { 626 ctrl := gomock.NewController(t) 627 628 now := genesistest.DefaultValidatorStartTime.Add(time.Hour) 629 630 tests := []struct { 631 description string 632 parentTime time.Time 633 childTime time.Time 634 result error 635 }{ 636 { 637 description: "commit block timestamp matching parent's one", 638 parentTime: now, 639 childTime: now, 640 result: nil, 641 }, 642 { 643 description: "commit block timestamp before parent's one", 644 childTime: now.Add(-1 * time.Second), 645 parentTime: now, 646 result: errOptionBlockTimestampNotMatchingParent, 647 }, 648 { 649 description: "commit block timestamp after parent's one", 650 parentTime: now, 651 childTime: now.Add(time.Second), 652 result: errOptionBlockTimestampNotMatchingParent, 653 }, 654 } 655 656 for _, test := range tests { 657 t.Run(test.description, func(t *testing.T) { 658 require := require.New(t) 659 660 // Create mocked dependencies. 661 s := state.NewMockState(ctrl) 662 mempool := mempoolmock.NewMempool(ctrl) 663 parentID := ids.GenerateTestID() 664 parentStatelessBlk := block.NewMockBlock(ctrl) 665 parentHeight := uint64(1) 666 667 backend := &backend{ 668 blkIDToState: make(map[ids.ID]*blockState), 669 Mempool: mempool, 670 state: s, 671 ctx: &snow.Context{ 672 Log: logging.NoLog{}, 673 }, 674 } 675 verifier := &verifier{ 676 txExecutorBackend: &executor.Backend{ 677 Config: &config.Config{ 678 UpgradeConfig: upgradetest.GetConfig(upgradetest.Banff), 679 }, 680 Clk: &mockable.Clock{}, 681 }, 682 backend: backend, 683 } 684 685 // build and verify child block 686 childHeight := parentHeight + 1 687 statelessCommitBlk, err := block.NewBanffCommitBlock(test.childTime, parentID, childHeight) 688 require.NoError(err) 689 690 // setup parent state 691 parentTime := genesistest.DefaultValidatorStartTime 692 s.EXPECT().GetLastAccepted().Return(parentID).Times(3) 693 s.EXPECT().GetTimestamp().Return(parentTime).Times(3) 694 s.EXPECT().GetFeeState().Return(gas.State{}).Times(3) 695 696 onDecisionState, err := state.NewDiff(parentID, backend) 697 require.NoError(err) 698 onCommitState, err := state.NewDiff(parentID, backend) 699 require.NoError(err) 700 onAbortState, err := state.NewDiff(parentID, backend) 701 require.NoError(err) 702 backend.blkIDToState[parentID] = &blockState{ 703 timestamp: test.parentTime, 704 statelessBlock: parentStatelessBlk, 705 proposalBlockState: proposalBlockState{ 706 onDecisionState: onDecisionState, 707 onCommitState: onCommitState, 708 onAbortState: onAbortState, 709 }, 710 } 711 712 // Set expectations for dependencies. 713 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 714 715 err = statelessCommitBlk.Visit(verifier) 716 require.ErrorIs(err, test.result) 717 }) 718 } 719 } 720 721 func TestVerifierVisitStandardBlockWithDuplicateInputs(t *testing.T) { 722 require := require.New(t) 723 ctrl := gomock.NewController(t) 724 725 // Create mocked dependencies. 726 s := state.NewMockState(ctrl) 727 mempool := mempoolmock.NewMempool(ctrl) 728 729 grandParentID := ids.GenerateTestID() 730 grandParentStatelessBlk := block.NewMockBlock(ctrl) 731 grandParentState := state.NewMockDiff(ctrl) 732 parentID := ids.GenerateTestID() 733 parentStatelessBlk := block.NewMockBlock(ctrl) 734 parentState := state.NewMockDiff(ctrl) 735 atomicInputs := set.Of(ids.GenerateTestID()) 736 737 backend := &backend{ 738 blkIDToState: map[ids.ID]*blockState{ 739 grandParentID: { 740 statelessBlock: grandParentStatelessBlk, 741 onAcceptState: grandParentState, 742 inputs: atomicInputs, 743 }, 744 parentID: { 745 statelessBlock: parentStatelessBlk, 746 onAcceptState: parentState, 747 }, 748 }, 749 Mempool: mempool, 750 state: s, 751 ctx: &snow.Context{ 752 Log: logging.NoLog{}, 753 }, 754 } 755 verifier := &verifier{ 756 txExecutorBackend: &executor.Backend{ 757 Config: &config.Config{ 758 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 759 }, 760 Clk: &mockable.Clock{}, 761 }, 762 backend: backend, 763 } 764 765 blkTx := txsmock.NewUnsignedTx(ctrl) 766 atomicRequests := map[ids.ID]*atomic.Requests{ 767 ids.GenerateTestID(): { 768 RemoveRequests: [][]byte{{1}, {2}}, 769 PutRequests: []*atomic.Element{ 770 { 771 Key: []byte{3}, 772 Value: []byte{4}, 773 Traits: [][]byte{{5}, {6}}, 774 }, 775 }, 776 }, 777 } 778 blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.StandardTxExecutor{})).DoAndReturn( 779 func(e *executor.StandardTxExecutor) error { 780 e.OnAccept = func() {} 781 e.Inputs = atomicInputs 782 e.AtomicRequests = atomicRequests 783 return nil 784 }, 785 ).Times(1) 786 787 // We can't serialize [blkTx] because it isn't 788 // registered with the blocks.Codec. 789 // Serialize this block with a dummy tx 790 // and replace it after creation with the mock tx. 791 // TODO allow serialization of mock txs. 792 blk, err := block.NewApricotStandardBlock( 793 parentID, 794 2, 795 []*txs.Tx{ 796 { 797 Unsigned: &txs.AdvanceTimeTx{}, 798 Creds: []verify.Verifiable{}, 799 }, 800 }, 801 ) 802 require.NoError(err) 803 blk.Transactions[0].Unsigned = blkTx 804 805 // Set expectations for dependencies. 806 timestamp := time.Now() 807 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 808 parentState.EXPECT().GetTimestamp().Return(timestamp).Times(1) 809 parentState.EXPECT().GetFeeState().Return(gas.State{}).Times(1) 810 parentStatelessBlk.EXPECT().Parent().Return(grandParentID).Times(1) 811 812 err = verifier.ApricotStandardBlock(blk) 813 require.ErrorIs(err, errConflictingParentTxs) 814 } 815 816 func TestVerifierVisitApricotStandardBlockWithProposalBlockParent(t *testing.T) { 817 require := require.New(t) 818 ctrl := gomock.NewController(t) 819 820 // Create mocked dependencies. 821 s := state.NewMockState(ctrl) 822 mempool := mempoolmock.NewMempool(ctrl) 823 parentID := ids.GenerateTestID() 824 parentStatelessBlk := block.NewMockBlock(ctrl) 825 parentOnCommitState := state.NewMockDiff(ctrl) 826 parentOnAbortState := state.NewMockDiff(ctrl) 827 828 backend := &backend{ 829 blkIDToState: map[ids.ID]*blockState{ 830 parentID: { 831 statelessBlock: parentStatelessBlk, 832 proposalBlockState: proposalBlockState{ 833 onCommitState: parentOnCommitState, 834 onAbortState: parentOnAbortState, 835 }, 836 }, 837 }, 838 Mempool: mempool, 839 state: s, 840 ctx: &snow.Context{ 841 Log: logging.NoLog{}, 842 }, 843 } 844 verifier := &verifier{ 845 txExecutorBackend: &executor.Backend{ 846 Config: &config.Config{ 847 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 848 }, 849 Clk: &mockable.Clock{}, 850 }, 851 backend: backend, 852 } 853 854 blk, err := block.NewApricotStandardBlock( 855 parentID, 856 2, 857 []*txs.Tx{ 858 { 859 Unsigned: &txs.AdvanceTimeTx{}, 860 Creds: []verify.Verifiable{}, 861 }, 862 }, 863 ) 864 require.NoError(err) 865 866 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 867 868 err = verifier.ApricotStandardBlock(blk) 869 require.ErrorIs(err, state.ErrMissingParentState) 870 } 871 872 func TestVerifierVisitBanffStandardBlockWithProposalBlockParent(t *testing.T) { 873 require := require.New(t) 874 ctrl := gomock.NewController(t) 875 876 // Create mocked dependencies. 877 s := state.NewMockState(ctrl) 878 mempool := mempoolmock.NewMempool(ctrl) 879 parentID := ids.GenerateTestID() 880 parentStatelessBlk := block.NewMockBlock(ctrl) 881 parentTime := time.Now() 882 parentOnCommitState := state.NewMockDiff(ctrl) 883 parentOnAbortState := state.NewMockDiff(ctrl) 884 885 backend := &backend{ 886 blkIDToState: map[ids.ID]*blockState{ 887 parentID: { 888 statelessBlock: parentStatelessBlk, 889 proposalBlockState: proposalBlockState{ 890 onCommitState: parentOnCommitState, 891 onAbortState: parentOnAbortState, 892 }, 893 }, 894 }, 895 Mempool: mempool, 896 state: s, 897 ctx: &snow.Context{ 898 Log: logging.NoLog{}, 899 }, 900 } 901 verifier := &verifier{ 902 txExecutorBackend: &executor.Backend{ 903 Config: &config.Config{ 904 UpgradeConfig: upgradetest.GetConfig(upgradetest.Banff), 905 }, 906 Clk: &mockable.Clock{}, 907 }, 908 backend: backend, 909 } 910 911 blk, err := block.NewBanffStandardBlock( 912 parentTime.Add(time.Second), 913 parentID, 914 2, 915 []*txs.Tx{ 916 { 917 Unsigned: &txs.AdvanceTimeTx{}, 918 Creds: []verify.Verifiable{}, 919 }, 920 }, 921 ) 922 require.NoError(err) 923 924 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 925 926 err = verifier.BanffStandardBlock(blk) 927 require.ErrorIs(err, state.ErrMissingParentState) 928 } 929 930 func TestVerifierVisitApricotCommitBlockUnexpectedParentState(t *testing.T) { 931 require := require.New(t) 932 ctrl := gomock.NewController(t) 933 934 // Create mocked dependencies. 935 s := state.NewMockState(ctrl) 936 parentID := ids.GenerateTestID() 937 parentStatelessBlk := block.NewMockBlock(ctrl) 938 verifier := &verifier{ 939 txExecutorBackend: &executor.Backend{ 940 Config: &config.Config{ 941 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 942 }, 943 Clk: &mockable.Clock{}, 944 }, 945 backend: &backend{ 946 blkIDToState: map[ids.ID]*blockState{ 947 parentID: { 948 statelessBlock: parentStatelessBlk, 949 }, 950 }, 951 state: s, 952 ctx: &snow.Context{ 953 Log: logging.NoLog{}, 954 }, 955 }, 956 } 957 958 blk, err := block.NewApricotCommitBlock( 959 parentID, 960 2, 961 ) 962 require.NoError(err) 963 964 // Set expectations for dependencies. 965 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 966 967 // Verify the block. 968 err = verifier.ApricotCommitBlock(blk) 969 require.ErrorIs(err, state.ErrMissingParentState) 970 } 971 972 func TestVerifierVisitBanffCommitBlockUnexpectedParentState(t *testing.T) { 973 require := require.New(t) 974 ctrl := gomock.NewController(t) 975 976 // Create mocked dependencies. 977 s := state.NewMockState(ctrl) 978 parentID := ids.GenerateTestID() 979 parentStatelessBlk := block.NewMockBlock(ctrl) 980 timestamp := time.Unix(12345, 0) 981 verifier := &verifier{ 982 txExecutorBackend: &executor.Backend{ 983 Config: &config.Config{ 984 UpgradeConfig: upgradetest.GetConfig(upgradetest.Banff), 985 }, 986 Clk: &mockable.Clock{}, 987 }, 988 backend: &backend{ 989 blkIDToState: map[ids.ID]*blockState{ 990 parentID: { 991 statelessBlock: parentStatelessBlk, 992 timestamp: timestamp, 993 }, 994 }, 995 state: s, 996 ctx: &snow.Context{ 997 Log: logging.NoLog{}, 998 }, 999 }, 1000 } 1001 1002 blk, err := block.NewBanffCommitBlock( 1003 timestamp, 1004 parentID, 1005 2, 1006 ) 1007 require.NoError(err) 1008 1009 // Set expectations for dependencies. 1010 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 1011 1012 // Verify the block. 1013 err = verifier.BanffCommitBlock(blk) 1014 require.ErrorIs(err, state.ErrMissingParentState) 1015 } 1016 1017 func TestVerifierVisitApricotAbortBlockUnexpectedParentState(t *testing.T) { 1018 require := require.New(t) 1019 ctrl := gomock.NewController(t) 1020 1021 // Create mocked dependencies. 1022 s := state.NewMockState(ctrl) 1023 parentID := ids.GenerateTestID() 1024 parentStatelessBlk := block.NewMockBlock(ctrl) 1025 verifier := &verifier{ 1026 txExecutorBackend: &executor.Backend{ 1027 Config: &config.Config{ 1028 UpgradeConfig: upgradetest.GetConfig(upgradetest.ApricotPhasePost6), 1029 }, 1030 Clk: &mockable.Clock{}, 1031 }, 1032 backend: &backend{ 1033 blkIDToState: map[ids.ID]*blockState{ 1034 parentID: { 1035 statelessBlock: parentStatelessBlk, 1036 }, 1037 }, 1038 state: s, 1039 ctx: &snow.Context{ 1040 Log: logging.NoLog{}, 1041 }, 1042 }, 1043 } 1044 1045 blk, err := block.NewApricotAbortBlock( 1046 parentID, 1047 2, 1048 ) 1049 require.NoError(err) 1050 1051 // Set expectations for dependencies. 1052 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 1053 1054 // Verify the block. 1055 err = verifier.ApricotAbortBlock(blk) 1056 require.ErrorIs(err, state.ErrMissingParentState) 1057 } 1058 1059 func TestVerifierVisitBanffAbortBlockUnexpectedParentState(t *testing.T) { 1060 require := require.New(t) 1061 ctrl := gomock.NewController(t) 1062 1063 // Create mocked dependencies. 1064 s := state.NewMockState(ctrl) 1065 parentID := ids.GenerateTestID() 1066 parentStatelessBlk := block.NewMockBlock(ctrl) 1067 timestamp := time.Unix(12345, 0) 1068 verifier := &verifier{ 1069 txExecutorBackend: &executor.Backend{ 1070 Config: &config.Config{ 1071 UpgradeConfig: upgradetest.GetConfig(upgradetest.Banff), 1072 }, 1073 Clk: &mockable.Clock{}, 1074 }, 1075 backend: &backend{ 1076 blkIDToState: map[ids.ID]*blockState{ 1077 parentID: { 1078 statelessBlock: parentStatelessBlk, 1079 timestamp: timestamp, 1080 }, 1081 }, 1082 state: s, 1083 ctx: &snow.Context{ 1084 Log: logging.NoLog{}, 1085 }, 1086 }, 1087 } 1088 1089 blk, err := block.NewBanffAbortBlock( 1090 timestamp, 1091 parentID, 1092 2, 1093 ) 1094 require.NoError(err) 1095 1096 // Set expectations for dependencies. 1097 parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1) 1098 1099 // Verify the block. 1100 err = verifier.BanffAbortBlock(blk) 1101 require.ErrorIs(err, state.ErrMissingParentState) 1102 } 1103 1104 func TestBlockExecutionWithComplexity(t *testing.T) { 1105 s := statetest.New(t, statetest.Config{}) 1106 verifier := newTestVerifier(t, s) 1107 wallet := txstest.NewWallet( 1108 t, 1109 verifier.ctx, 1110 verifier.txExecutorBackend.Config, 1111 s, 1112 secp256k1fx.NewKeychain(genesis.EWOQKey), 1113 nil, // subnetIDs 1114 nil, // chainIDs 1115 ) 1116 1117 baseTx0, err := wallet.IssueBaseTx([]*avax.TransferableOutput{}) 1118 require.NoError(t, err) 1119 baseTx1, err := wallet.IssueBaseTx([]*avax.TransferableOutput{}) 1120 require.NoError(t, err) 1121 1122 blockComplexity, err := fee.TxComplexity(baseTx0.Unsigned, baseTx1.Unsigned) 1123 require.NoError(t, err) 1124 blockGas, err := blockComplexity.ToGas(verifier.txExecutorBackend.Config.DynamicFeeConfig.Weights) 1125 require.NoError(t, err) 1126 1127 tests := []struct { 1128 name string 1129 timestamp time.Time 1130 expectedErr error 1131 expectedFeeState gas.State 1132 }{ 1133 { 1134 name: "no capacity", 1135 timestamp: genesistest.DefaultValidatorStartTime, 1136 expectedErr: gas.ErrInsufficientCapacity, 1137 }, 1138 { 1139 name: "updates fee state", 1140 timestamp: genesistest.DefaultValidatorStartTime.Add(10 * time.Second), 1141 expectedFeeState: gas.State{ 1142 Capacity: gas.Gas(0).AddPerSecond(verifier.txExecutorBackend.Config.DynamicFeeConfig.MaxPerSecond, 10) - blockGas, 1143 Excess: blockGas, 1144 }, 1145 }, 1146 } 1147 for _, test := range tests { 1148 t.Run(test.name, func(t *testing.T) { 1149 require := require.New(t) 1150 1151 // Clear the state to prevent prior tests from impacting this test. 1152 clear(verifier.blkIDToState) 1153 1154 verifier.txExecutorBackend.Clk.Set(test.timestamp) 1155 timestamp, _, err := state.NextBlockTime(s, verifier.txExecutorBackend.Clk) 1156 require.NoError(err) 1157 1158 lastAcceptedID := s.GetLastAccepted() 1159 lastAccepted, err := s.GetStatelessBlock(lastAcceptedID) 1160 require.NoError(err) 1161 1162 blk, err := block.NewBanffStandardBlock( 1163 timestamp, 1164 lastAcceptedID, 1165 lastAccepted.Height()+1, 1166 []*txs.Tx{ 1167 baseTx0, 1168 baseTx1, 1169 }, 1170 ) 1171 require.NoError(err) 1172 1173 blkID := blk.ID() 1174 err = blk.Visit(verifier) 1175 require.ErrorIs(err, test.expectedErr) 1176 if err != nil { 1177 require.NotContains(verifier.blkIDToState, blkID) 1178 return 1179 } 1180 1181 require.Contains(verifier.blkIDToState, blkID) 1182 onAcceptState := verifier.blkIDToState[blkID].onAcceptState 1183 require.Equal(test.expectedFeeState, onAcceptState.GetFeeState()) 1184 }) 1185 } 1186 }