github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/executor/standard_tx_executor_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 "errors" 9 "math" 10 "math/rand" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/require" 15 "go.uber.org/mock/gomock" 16 17 "github.com/MetalBlockchain/metalgo/database" 18 "github.com/MetalBlockchain/metalgo/ids" 19 "github.com/MetalBlockchain/metalgo/snow" 20 "github.com/MetalBlockchain/metalgo/utils" 21 "github.com/MetalBlockchain/metalgo/utils/constants" 22 "github.com/MetalBlockchain/metalgo/utils/crypto/bls" 23 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 24 "github.com/MetalBlockchain/metalgo/utils/hashing" 25 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 26 "github.com/MetalBlockchain/metalgo/utils/units" 27 "github.com/MetalBlockchain/metalgo/vms/components/avax" 28 "github.com/MetalBlockchain/metalgo/vms/components/verify" 29 "github.com/MetalBlockchain/metalgo/vms/platformvm/config" 30 "github.com/MetalBlockchain/metalgo/vms/platformvm/fx" 31 "github.com/MetalBlockchain/metalgo/vms/platformvm/reward" 32 "github.com/MetalBlockchain/metalgo/vms/platformvm/signer" 33 "github.com/MetalBlockchain/metalgo/vms/platformvm/state" 34 "github.com/MetalBlockchain/metalgo/vms/platformvm/status" 35 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs" 36 "github.com/MetalBlockchain/metalgo/vms/platformvm/upgrade" 37 "github.com/MetalBlockchain/metalgo/vms/platformvm/utxo" 38 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 39 "github.com/MetalBlockchain/metalgo/wallet/subnet/primary/common" 40 41 walletsigner "github.com/MetalBlockchain/metalgo/wallet/chain/p/signer" 42 ) 43 44 // This tests that the math performed during TransformSubnetTx execution can 45 // never overflow 46 const _ time.Duration = math.MaxUint32 * time.Second 47 48 var errTest = errors.New("non-nil error") 49 50 func TestStandardTxExecutorAddValidatorTxEmptyID(t *testing.T) { 51 require := require.New(t) 52 env := newEnvironment(t, apricotPhase5) 53 env.ctx.Lock.Lock() 54 defer env.ctx.Lock.Unlock() 55 56 chainTime := env.state.GetTimestamp() 57 startTime := defaultValidateStartTime.Add(1 * time.Second) 58 59 tests := []struct { 60 banffTime time.Time 61 expectedError error 62 }{ 63 { // Case: Before banff 64 banffTime: chainTime.Add(1), 65 expectedError: errEmptyNodeID, 66 }, 67 { // Case: At banff 68 banffTime: chainTime, 69 expectedError: errEmptyNodeID, 70 }, 71 { // Case: After banff 72 banffTime: chainTime.Add(-1), 73 expectedError: errEmptyNodeID, 74 }, 75 } 76 for _, test := range tests { 77 // Case: Empty validator node ID after banff 78 env.config.UpgradeConfig.BanffTime = test.banffTime 79 80 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 81 utx, err := builder.NewAddValidatorTx( 82 &txs.Validator{ 83 NodeID: ids.EmptyNodeID, 84 Start: uint64(startTime.Unix()), 85 End: uint64(defaultValidateEndTime.Unix()), 86 Wght: env.config.MinValidatorStake, 87 }, 88 &secp256k1fx.OutputOwners{ 89 Threshold: 1, 90 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 91 }, 92 reward.PercentDenominator, 93 ) 94 require.NoError(err) 95 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 96 require.NoError(err) 97 98 stateDiff, err := state.NewDiff(lastAcceptedID, env) 99 require.NoError(err) 100 101 executor := StandardTxExecutor{ 102 Backend: &env.backend, 103 State: stateDiff, 104 Tx: tx, 105 } 106 err = tx.Unsigned.Visit(&executor) 107 require.ErrorIs(err, test.expectedError) 108 } 109 } 110 111 func TestStandardTxExecutorAddDelegator(t *testing.T) { 112 dummyHeight := uint64(1) 113 rewardAddress := preFundedKeys[0].PublicKey().Address() 114 nodeID := genesisNodeIDs[0] 115 116 newValidatorID := ids.GenerateTestNodeID() 117 newValidatorStartTime := defaultValidateStartTime.Add(5 * time.Second) 118 newValidatorEndTime := defaultValidateEndTime.Add(-5 * time.Second) 119 120 // [addMinStakeValidator] adds a new validator to the primary network's 121 // pending validator set with the minimum staking amount 122 addMinStakeValidator := func(env *environment) { 123 require := require.New(t) 124 125 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 126 utx, err := builder.NewAddValidatorTx( 127 &txs.Validator{ 128 NodeID: newValidatorID, 129 Start: uint64(newValidatorStartTime.Unix()), 130 End: uint64(newValidatorEndTime.Unix()), 131 Wght: env.config.MinValidatorStake, 132 }, 133 &secp256k1fx.OutputOwners{ 134 Threshold: 1, 135 Addrs: []ids.ShortID{rewardAddress}, 136 }, 137 reward.PercentDenominator, 138 ) 139 require.NoError(err) 140 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 141 require.NoError(err) 142 143 addValTx := tx.Unsigned.(*txs.AddValidatorTx) 144 staker, err := state.NewCurrentStaker( 145 tx.ID(), 146 addValTx, 147 newValidatorStartTime, 148 0, 149 ) 150 require.NoError(err) 151 152 env.state.PutCurrentValidator(staker) 153 env.state.AddTx(tx, status.Committed) 154 env.state.SetHeight(dummyHeight) 155 require.NoError(env.state.Commit()) 156 } 157 158 // [addMaxStakeValidator] adds a new validator to the primary network's 159 // pending validator set with the maximum staking amount 160 addMaxStakeValidator := func(env *environment) { 161 require := require.New(t) 162 163 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 164 utx, err := builder.NewAddValidatorTx( 165 &txs.Validator{ 166 NodeID: newValidatorID, 167 Start: uint64(newValidatorStartTime.Unix()), 168 End: uint64(newValidatorEndTime.Unix()), 169 Wght: env.config.MaxValidatorStake, 170 }, 171 &secp256k1fx.OutputOwners{ 172 Threshold: 1, 173 Addrs: []ids.ShortID{rewardAddress}, 174 }, 175 reward.PercentDenominator, 176 ) 177 require.NoError(err) 178 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 179 require.NoError(err) 180 181 addValTx := tx.Unsigned.(*txs.AddValidatorTx) 182 staker, err := state.NewCurrentStaker( 183 tx.ID(), 184 addValTx, 185 newValidatorStartTime, 186 0, 187 ) 188 require.NoError(err) 189 190 env.state.PutCurrentValidator(staker) 191 env.state.AddTx(tx, status.Committed) 192 env.state.SetHeight(dummyHeight) 193 require.NoError(env.state.Commit()) 194 } 195 196 env := newEnvironment(t, apricotPhase5) 197 currentTimestamp := env.state.GetTimestamp() 198 199 type test struct { 200 description string 201 stakeAmount uint64 202 startTime time.Time 203 endTime time.Time 204 nodeID ids.NodeID 205 feeKeys []*secp256k1.PrivateKey 206 setup func(*environment) 207 AP3Time time.Time 208 expectedExecutionErr error 209 } 210 211 tests := []test{ 212 { 213 description: "validator stops validating earlier than delegator", 214 stakeAmount: env.config.MinDelegatorStake, 215 startTime: defaultValidateStartTime.Add(time.Second), 216 endTime: defaultValidateEndTime.Add(time.Second), 217 nodeID: nodeID, 218 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, 219 setup: nil, 220 AP3Time: defaultGenesisTime, 221 expectedExecutionErr: ErrPeriodMismatch, 222 }, 223 { 224 description: "validator not in the current or pending validator sets", 225 stakeAmount: env.config.MinDelegatorStake, 226 startTime: defaultValidateStartTime.Add(5 * time.Second), 227 endTime: defaultValidateEndTime.Add(-5 * time.Second), 228 nodeID: newValidatorID, 229 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, 230 setup: nil, 231 AP3Time: defaultGenesisTime, 232 expectedExecutionErr: database.ErrNotFound, 233 }, 234 { 235 description: "delegator starts before validator", 236 stakeAmount: env.config.MinDelegatorStake, 237 startTime: newValidatorStartTime.Add(-1 * time.Second), // start validating subnet before primary network 238 endTime: newValidatorEndTime, 239 nodeID: newValidatorID, 240 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, 241 setup: addMinStakeValidator, 242 AP3Time: defaultGenesisTime, 243 expectedExecutionErr: ErrPeriodMismatch, 244 }, 245 { 246 description: "delegator stops before validator", 247 stakeAmount: env.config.MinDelegatorStake, 248 startTime: newValidatorStartTime, 249 endTime: newValidatorEndTime.Add(time.Second), // stop validating subnet after stopping validating primary network 250 nodeID: newValidatorID, 251 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, 252 setup: addMinStakeValidator, 253 AP3Time: defaultGenesisTime, 254 expectedExecutionErr: ErrPeriodMismatch, 255 }, 256 { 257 description: "valid", 258 stakeAmount: env.config.MinDelegatorStake, 259 startTime: newValidatorStartTime, // same start time as for primary network 260 endTime: newValidatorEndTime, // same end time as for primary network 261 nodeID: newValidatorID, 262 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, 263 setup: addMinStakeValidator, 264 AP3Time: defaultGenesisTime, 265 expectedExecutionErr: nil, 266 }, 267 { 268 description: "starts delegating at current timestamp", 269 stakeAmount: env.config.MinDelegatorStake, // weight 270 startTime: currentTimestamp, // start time 271 endTime: defaultValidateEndTime, // end time 272 nodeID: nodeID, // node ID 273 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, // tx fee payer 274 setup: nil, 275 AP3Time: defaultGenesisTime, 276 expectedExecutionErr: ErrTimestampNotBeforeStartTime, 277 }, 278 { 279 description: "tx fee paying key has no funds", 280 stakeAmount: env.config.MinDelegatorStake, // weight 281 startTime: defaultValidateStartTime.Add(time.Second), // start time 282 endTime: defaultValidateEndTime, // end time 283 nodeID: nodeID, // node ID 284 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[1]}, // tx fee payer 285 setup: func(env *environment) { // Remove all UTXOs owned by keys[1] 286 utxoIDs, err := env.state.UTXOIDs( 287 preFundedKeys[1].PublicKey().Address().Bytes(), 288 ids.Empty, 289 math.MaxInt32) 290 require.NoError(t, err) 291 292 for _, utxoID := range utxoIDs { 293 env.state.DeleteUTXO(utxoID) 294 } 295 env.state.SetHeight(dummyHeight) 296 require.NoError(t, env.state.Commit()) 297 }, 298 AP3Time: defaultGenesisTime, 299 expectedExecutionErr: ErrFlowCheckFailed, 300 }, 301 { 302 description: "over delegation before AP3", 303 stakeAmount: env.config.MinDelegatorStake, 304 startTime: newValidatorStartTime, // same start time as for primary network 305 endTime: newValidatorEndTime, // same end time as for primary network 306 nodeID: newValidatorID, 307 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, 308 setup: addMaxStakeValidator, 309 AP3Time: defaultValidateEndTime, 310 expectedExecutionErr: nil, 311 }, 312 { 313 description: "over delegation after AP3", 314 stakeAmount: env.config.MinDelegatorStake, 315 startTime: newValidatorStartTime, // same start time as for primary network 316 endTime: newValidatorEndTime, // same end time as for primary network 317 nodeID: newValidatorID, 318 feeKeys: []*secp256k1.PrivateKey{preFundedKeys[0]}, 319 setup: addMaxStakeValidator, 320 AP3Time: defaultGenesisTime, 321 expectedExecutionErr: ErrOverDelegated, 322 }, 323 } 324 325 for _, tt := range tests { 326 t.Run(tt.description, func(t *testing.T) { 327 require := require.New(t) 328 env := newEnvironment(t, apricotPhase5) 329 env.config.UpgradeConfig.ApricotPhase3Time = tt.AP3Time 330 331 builder, signer := env.factory.NewWallet(tt.feeKeys...) 332 utx, err := builder.NewAddDelegatorTx( 333 &txs.Validator{ 334 NodeID: tt.nodeID, 335 Start: uint64(tt.startTime.Unix()), 336 End: uint64(tt.endTime.Unix()), 337 Wght: tt.stakeAmount, 338 }, 339 &secp256k1fx.OutputOwners{ 340 Threshold: 1, 341 Addrs: []ids.ShortID{rewardAddress}, 342 }, 343 ) 344 require.NoError(err) 345 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 346 require.NoError(err) 347 348 if tt.setup != nil { 349 tt.setup(env) 350 } 351 352 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 353 require.NoError(err) 354 355 env.config.UpgradeConfig.BanffTime = onAcceptState.GetTimestamp() 356 357 executor := StandardTxExecutor{ 358 Backend: &env.backend, 359 State: onAcceptState, 360 Tx: tx, 361 } 362 err = tx.Unsigned.Visit(&executor) 363 require.ErrorIs(err, tt.expectedExecutionErr) 364 }) 365 } 366 } 367 368 func TestApricotStandardTxExecutorAddSubnetValidator(t *testing.T) { 369 require := require.New(t) 370 env := newEnvironment(t, apricotPhase5) 371 env.ctx.Lock.Lock() 372 defer env.ctx.Lock.Unlock() 373 374 nodeID := genesisNodeIDs[0] 375 376 { 377 // Case: Proposed validator currently validating primary network 378 // but stops validating subnet after stops validating primary network 379 // (note that keys[0] is a genesis validator) 380 startTime := defaultValidateStartTime.Add(time.Second) 381 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 382 utx, err := builder.NewAddSubnetValidatorTx( 383 &txs.SubnetValidator{ 384 Validator: txs.Validator{ 385 NodeID: nodeID, 386 Start: uint64(startTime.Unix()), 387 End: uint64(defaultValidateEndTime.Unix()) + 1, 388 Wght: defaultWeight, 389 }, 390 Subnet: testSubnet1.ID(), 391 }, 392 ) 393 require.NoError(err) 394 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 395 require.NoError(err) 396 397 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 398 require.NoError(err) 399 400 executor := StandardTxExecutor{ 401 Backend: &env.backend, 402 State: onAcceptState, 403 Tx: tx, 404 } 405 err = tx.Unsigned.Visit(&executor) 406 require.ErrorIs(err, ErrPeriodMismatch) 407 } 408 409 { 410 // Case: Proposed validator currently validating primary network 411 // and proposed subnet validation period is subset of 412 // primary network validation period 413 // (note that keys[0] is a genesis validator) 414 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 415 utx, err := builder.NewAddSubnetValidatorTx( 416 &txs.SubnetValidator{ 417 Validator: txs.Validator{ 418 NodeID: nodeID, 419 Start: uint64(defaultValidateStartTime.Unix() + 1), 420 End: uint64(defaultValidateEndTime.Unix()), 421 Wght: defaultWeight, 422 }, 423 Subnet: testSubnet1.ID(), 424 }, 425 ) 426 require.NoError(err) 427 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 428 require.NoError(err) 429 430 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 431 require.NoError(err) 432 433 executor := StandardTxExecutor{ 434 Backend: &env.backend, 435 State: onAcceptState, 436 Tx: tx, 437 } 438 require.NoError(tx.Unsigned.Visit(&executor)) 439 } 440 441 // Add a validator to pending validator set of primary network 442 // Starts validating primary network 10 seconds after genesis 443 pendingDSValidatorID := ids.GenerateTestNodeID() 444 dsStartTime := defaultGenesisTime.Add(10 * time.Second) 445 dsEndTime := dsStartTime.Add(5 * defaultMinStakingDuration) 446 447 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 448 utx, err := builder.NewAddValidatorTx( 449 &txs.Validator{ 450 NodeID: pendingDSValidatorID, 451 Start: uint64(dsStartTime.Unix()), 452 End: uint64(dsEndTime.Unix()), 453 Wght: env.config.MinValidatorStake, 454 }, 455 &secp256k1fx.OutputOwners{ 456 Threshold: 1, 457 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 458 }, 459 reward.PercentDenominator, 460 ) 461 require.NoError(err) 462 addDSTx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 463 require.NoError(err) 464 465 { 466 // Case: Proposed validator isn't in pending or current validator sets 467 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 468 utx, err := builder.NewAddSubnetValidatorTx( 469 &txs.SubnetValidator{ 470 Validator: txs.Validator{ 471 NodeID: pendingDSValidatorID, 472 Start: uint64(dsStartTime.Unix()), // start validating subnet before primary network 473 End: uint64(dsEndTime.Unix()), 474 Wght: defaultWeight, 475 }, 476 Subnet: testSubnet1.ID(), 477 }, 478 ) 479 require.NoError(err) 480 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 481 require.NoError(err) 482 483 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 484 require.NoError(err) 485 486 executor := StandardTxExecutor{ 487 Backend: &env.backend, 488 State: onAcceptState, 489 Tx: tx, 490 } 491 err = tx.Unsigned.Visit(&executor) 492 require.ErrorIs(err, ErrNotValidator) 493 } 494 495 addValTx := addDSTx.Unsigned.(*txs.AddValidatorTx) 496 staker, err := state.NewCurrentStaker( 497 addDSTx.ID(), 498 addValTx, 499 dsStartTime, 500 0, 501 ) 502 require.NoError(err) 503 504 env.state.PutCurrentValidator(staker) 505 env.state.AddTx(addDSTx, status.Committed) 506 dummyHeight := uint64(1) 507 env.state.SetHeight(dummyHeight) 508 require.NoError(env.state.Commit()) 509 510 // Node with ID key.PublicKey().Address() now a pending validator for primary network 511 512 { 513 // Case: Proposed validator is pending validator of primary network 514 // but starts validating subnet before primary network 515 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 516 utx, err := builder.NewAddSubnetValidatorTx( 517 &txs.SubnetValidator{ 518 Validator: txs.Validator{ 519 NodeID: pendingDSValidatorID, 520 Start: uint64(dsStartTime.Unix()) - 1, // start validating subnet before primary network 521 End: uint64(dsEndTime.Unix()), 522 Wght: defaultWeight, 523 }, 524 Subnet: testSubnet1.ID(), 525 }, 526 ) 527 require.NoError(err) 528 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 529 require.NoError(err) 530 531 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 532 require.NoError(err) 533 534 executor := StandardTxExecutor{ 535 Backend: &env.backend, 536 State: onAcceptState, 537 Tx: tx, 538 } 539 err = tx.Unsigned.Visit(&executor) 540 require.ErrorIs(err, ErrPeriodMismatch) 541 } 542 543 { 544 // Case: Proposed validator is pending validator of primary network 545 // but stops validating subnet after primary network 546 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 547 utx, err := builder.NewAddSubnetValidatorTx( 548 &txs.SubnetValidator{ 549 Validator: txs.Validator{ 550 NodeID: pendingDSValidatorID, 551 Start: uint64(dsStartTime.Unix()), 552 End: uint64(dsEndTime.Unix()) + 1, // stop validating subnet after stopping validating primary network 553 Wght: defaultWeight, 554 }, 555 Subnet: testSubnet1.ID(), 556 }, 557 ) 558 require.NoError(err) 559 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 560 require.NoError(err) 561 562 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 563 require.NoError(err) 564 565 executor := StandardTxExecutor{ 566 Backend: &env.backend, 567 State: onAcceptState, 568 Tx: tx, 569 } 570 err = tx.Unsigned.Visit(&executor) 571 require.ErrorIs(err, ErrPeriodMismatch) 572 } 573 574 { 575 // Case: Proposed validator is pending validator of primary network and 576 // period validating subnet is subset of time validating primary network 577 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 578 utx, err := builder.NewAddSubnetValidatorTx( 579 &txs.SubnetValidator{ 580 Validator: txs.Validator{ 581 NodeID: pendingDSValidatorID, 582 Start: uint64(dsStartTime.Unix()), // same start time as for primary network 583 End: uint64(dsEndTime.Unix()), // same end time as for primary network 584 Wght: defaultWeight, 585 }, 586 Subnet: testSubnet1.ID(), 587 }, 588 ) 589 require.NoError(err) 590 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 591 require.NoError(err) 592 593 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 594 require.NoError(err) 595 executor := StandardTxExecutor{ 596 Backend: &env.backend, 597 State: onAcceptState, 598 Tx: tx, 599 } 600 require.NoError(tx.Unsigned.Visit(&executor)) 601 } 602 603 // Case: Proposed validator start validating at/before current timestamp 604 // First, advance the timestamp 605 newTimestamp := defaultGenesisTime.Add(2 * time.Second) 606 env.state.SetTimestamp(newTimestamp) 607 608 { 609 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 610 utx, err := builder.NewAddSubnetValidatorTx( 611 &txs.SubnetValidator{ 612 Validator: txs.Validator{ 613 NodeID: nodeID, 614 Start: uint64(newTimestamp.Unix()), 615 End: uint64(newTimestamp.Add(defaultMinStakingDuration).Unix()), 616 Wght: defaultWeight, 617 }, 618 Subnet: testSubnet1.ID(), 619 }, 620 ) 621 require.NoError(err) 622 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 623 require.NoError(err) 624 625 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 626 require.NoError(err) 627 628 executor := StandardTxExecutor{ 629 Backend: &env.backend, 630 State: onAcceptState, 631 Tx: tx, 632 } 633 err = tx.Unsigned.Visit(&executor) 634 require.ErrorIs(err, ErrTimestampNotBeforeStartTime) 635 } 636 637 // reset the timestamp 638 env.state.SetTimestamp(defaultGenesisTime) 639 640 // Case: Proposed validator already validating the subnet 641 // First, add validator as validator of subnet 642 builder, signer = env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 643 uSubnetTx, err := builder.NewAddSubnetValidatorTx( 644 &txs.SubnetValidator{ 645 Validator: txs.Validator{ 646 NodeID: nodeID, 647 Start: uint64(defaultValidateStartTime.Unix()), 648 End: uint64(defaultValidateEndTime.Unix()), 649 Wght: defaultWeight, 650 }, 651 Subnet: testSubnet1.ID(), 652 }, 653 ) 654 require.NoError(err) 655 subnetTx, err := walletsigner.SignUnsigned(context.Background(), signer, uSubnetTx) 656 require.NoError(err) 657 658 addSubnetValTx := subnetTx.Unsigned.(*txs.AddSubnetValidatorTx) 659 staker, err = state.NewCurrentStaker( 660 subnetTx.ID(), 661 addSubnetValTx, 662 defaultValidateStartTime, 663 0, 664 ) 665 require.NoError(err) 666 667 env.state.PutCurrentValidator(staker) 668 env.state.AddTx(subnetTx, status.Committed) 669 env.state.SetHeight(dummyHeight) 670 require.NoError(env.state.Commit()) 671 672 { 673 // Node with ID nodeIDKey.PublicKey().Address() now validating subnet with ID testSubnet1.ID 674 startTime := defaultValidateStartTime.Add(time.Second) 675 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 676 utx, err := builder.NewAddSubnetValidatorTx( 677 &txs.SubnetValidator{ 678 Validator: txs.Validator{ 679 NodeID: nodeID, 680 Start: uint64(startTime.Unix()), 681 End: uint64(defaultValidateEndTime.Unix()), 682 Wght: defaultWeight, 683 }, 684 Subnet: testSubnet1.ID(), 685 }, 686 ) 687 require.NoError(err) 688 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 689 require.NoError(err) 690 691 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 692 require.NoError(err) 693 694 executor := StandardTxExecutor{ 695 Backend: &env.backend, 696 State: onAcceptState, 697 Tx: tx, 698 } 699 err = tx.Unsigned.Visit(&executor) 700 require.ErrorIs(err, ErrDuplicateValidator) 701 } 702 703 env.state.DeleteCurrentValidator(staker) 704 env.state.SetHeight(dummyHeight) 705 require.NoError(env.state.Commit()) 706 707 { 708 // Case: Duplicate signatures 709 startTime := defaultValidateStartTime.Add(time.Second) 710 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1], testSubnet1ControlKeys[2]) 711 utx, err := builder.NewAddSubnetValidatorTx( 712 &txs.SubnetValidator{ 713 Validator: txs.Validator{ 714 NodeID: nodeID, 715 Start: uint64(startTime.Unix()), 716 End: uint64(startTime.Add(defaultMinStakingDuration).Unix()) + 1, 717 Wght: defaultWeight, 718 }, 719 Subnet: testSubnet1.ID(), 720 }, 721 ) 722 require.NoError(err) 723 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 724 require.NoError(err) 725 726 // Duplicate a signature 727 addSubnetValidatorTx := tx.Unsigned.(*txs.AddSubnetValidatorTx) 728 input := addSubnetValidatorTx.SubnetAuth.(*secp256k1fx.Input) 729 input.SigIndices = append(input.SigIndices, input.SigIndices[0]) 730 // This tx was syntactically verified when it was created...pretend it wasn't so we don't use cache 731 addSubnetValidatorTx.SyntacticallyVerified = false 732 733 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 734 require.NoError(err) 735 736 executor := StandardTxExecutor{ 737 Backend: &env.backend, 738 State: onAcceptState, 739 Tx: tx, 740 } 741 err = tx.Unsigned.Visit(&executor) 742 require.ErrorIs(err, secp256k1fx.ErrInputIndicesNotSortedUnique) 743 } 744 745 { 746 // Case: Too few signatures 747 startTime := defaultValidateStartTime.Add(time.Second) 748 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[2]) 749 utx, err := builder.NewAddSubnetValidatorTx( 750 &txs.SubnetValidator{ 751 Validator: txs.Validator{ 752 NodeID: nodeID, 753 Start: uint64(startTime.Unix()), 754 End: uint64(startTime.Add(defaultMinStakingDuration).Unix()), 755 Wght: defaultWeight, 756 }, 757 Subnet: testSubnet1.ID(), 758 }, 759 ) 760 require.NoError(err) 761 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 762 require.NoError(err) 763 764 // Remove a signature 765 addSubnetValidatorTx := tx.Unsigned.(*txs.AddSubnetValidatorTx) 766 input := addSubnetValidatorTx.SubnetAuth.(*secp256k1fx.Input) 767 input.SigIndices = input.SigIndices[1:] 768 // This tx was syntactically verified when it was created...pretend it wasn't so we don't use cache 769 addSubnetValidatorTx.SyntacticallyVerified = false 770 771 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 772 require.NoError(err) 773 774 executor := StandardTxExecutor{ 775 Backend: &env.backend, 776 State: onAcceptState, 777 Tx: tx, 778 } 779 err = tx.Unsigned.Visit(&executor) 780 require.ErrorIs(err, errUnauthorizedSubnetModification) 781 } 782 783 { 784 // Case: Control Signature from invalid key (keys[3] is not a control key) 785 startTime := defaultValidateStartTime.Add(time.Second) 786 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], preFundedKeys[1]) 787 utx, err := builder.NewAddSubnetValidatorTx( 788 &txs.SubnetValidator{ 789 Validator: txs.Validator{ 790 NodeID: nodeID, 791 Start: uint64(startTime.Unix()), 792 End: uint64(startTime.Add(defaultMinStakingDuration).Unix()), 793 Wght: defaultWeight, 794 }, 795 Subnet: testSubnet1.ID(), 796 }, 797 ) 798 require.NoError(err) 799 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 800 require.NoError(err) 801 802 // Replace a valid signature with one from keys[3] 803 sig, err := preFundedKeys[3].SignHash(hashing.ComputeHash256(tx.Unsigned.Bytes())) 804 require.NoError(err) 805 copy(tx.Creds[0].(*secp256k1fx.Credential).Sigs[0][:], sig) 806 807 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 808 require.NoError(err) 809 810 executor := StandardTxExecutor{ 811 Backend: &env.backend, 812 State: onAcceptState, 813 Tx: tx, 814 } 815 err = tx.Unsigned.Visit(&executor) 816 require.ErrorIs(err, errUnauthorizedSubnetModification) 817 } 818 819 { 820 // Case: Proposed validator in pending validator set for subnet 821 // First, add validator to pending validator set of subnet 822 startTime := defaultValidateStartTime.Add(time.Second) 823 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 824 utx, err := builder.NewAddSubnetValidatorTx( 825 &txs.SubnetValidator{ 826 Validator: txs.Validator{ 827 NodeID: nodeID, 828 Start: uint64(startTime.Unix()) + 1, 829 End: uint64(startTime.Add(defaultMinStakingDuration).Unix()) + 1, 830 Wght: defaultWeight, 831 }, 832 Subnet: testSubnet1.ID(), 833 }, 834 ) 835 require.NoError(err) 836 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 837 require.NoError(err) 838 839 addSubnetValTx := subnetTx.Unsigned.(*txs.AddSubnetValidatorTx) 840 staker, err = state.NewCurrentStaker( 841 subnetTx.ID(), 842 addSubnetValTx, 843 defaultValidateStartTime, 844 0, 845 ) 846 require.NoError(err) 847 848 env.state.PutCurrentValidator(staker) 849 env.state.AddTx(tx, status.Committed) 850 env.state.SetHeight(dummyHeight) 851 require.NoError(env.state.Commit()) 852 853 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 854 require.NoError(err) 855 856 executor := StandardTxExecutor{ 857 Backend: &env.backend, 858 State: onAcceptState, 859 Tx: tx, 860 } 861 err = tx.Unsigned.Visit(&executor) 862 require.ErrorIs(err, ErrDuplicateValidator) 863 } 864 } 865 866 func TestBanffStandardTxExecutorAddValidator(t *testing.T) { 867 require := require.New(t) 868 env := newEnvironment(t, banff) 869 env.ctx.Lock.Lock() 870 defer env.ctx.Lock.Unlock() 871 872 nodeID := ids.GenerateTestNodeID() 873 874 { 875 // Case: Validator's start time too early 876 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 877 utx, err := builder.NewAddValidatorTx( 878 &txs.Validator{ 879 NodeID: nodeID, 880 Start: uint64(defaultValidateStartTime.Unix()) - 1, 881 End: uint64(defaultValidateEndTime.Unix()), 882 Wght: env.config.MinValidatorStake, 883 }, 884 &secp256k1fx.OutputOwners{ 885 Threshold: 1, 886 Addrs: []ids.ShortID{ids.ShortEmpty}, 887 }, 888 reward.PercentDenominator, 889 ) 890 require.NoError(err) 891 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 892 require.NoError(err) 893 894 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 895 require.NoError(err) 896 897 executor := StandardTxExecutor{ 898 Backend: &env.backend, 899 State: onAcceptState, 900 Tx: tx, 901 } 902 err = tx.Unsigned.Visit(&executor) 903 require.ErrorIs(err, ErrTimestampNotBeforeStartTime) 904 } 905 906 { 907 // Case: Validator in current validator set of primary network 908 startTime := defaultValidateStartTime.Add(1 * time.Second) 909 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 910 utx, err := builder.NewAddValidatorTx( 911 &txs.Validator{ 912 NodeID: nodeID, 913 Start: uint64(startTime.Unix()), 914 End: uint64(startTime.Add(defaultMinStakingDuration).Unix()), 915 Wght: env.config.MinValidatorStake, 916 }, 917 &secp256k1fx.OutputOwners{ 918 Threshold: 1, 919 Addrs: []ids.ShortID{ids.ShortEmpty}, 920 }, 921 reward.PercentDenominator, 922 ) 923 require.NoError(err) 924 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 925 require.NoError(err) 926 927 addValTx := tx.Unsigned.(*txs.AddValidatorTx) 928 staker, err := state.NewCurrentStaker( 929 tx.ID(), 930 addValTx, 931 startTime, 932 0, 933 ) 934 require.NoError(err) 935 936 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 937 require.NoError(err) 938 939 onAcceptState.PutCurrentValidator(staker) 940 onAcceptState.AddTx(tx, status.Committed) 941 942 executor := StandardTxExecutor{ 943 Backend: &env.backend, 944 State: onAcceptState, 945 Tx: tx, 946 } 947 err = tx.Unsigned.Visit(&executor) 948 require.ErrorIs(err, ErrAlreadyValidator) 949 } 950 951 { 952 // Case: Validator in pending validator set of primary network 953 startTime := defaultValidateStartTime.Add(1 * time.Second) 954 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 955 utx, err := builder.NewAddValidatorTx( 956 &txs.Validator{ 957 NodeID: nodeID, 958 Start: uint64(startTime.Unix()), 959 End: uint64(startTime.Add(defaultMinStakingDuration).Unix()), 960 Wght: env.config.MinValidatorStake, 961 }, 962 &secp256k1fx.OutputOwners{ 963 Threshold: 1, 964 Addrs: []ids.ShortID{ids.ShortEmpty}, 965 }, 966 reward.PercentDenominator, 967 ) 968 require.NoError(err) 969 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 970 require.NoError(err) 971 972 staker, err := state.NewPendingStaker( 973 tx.ID(), 974 tx.Unsigned.(*txs.AddValidatorTx), 975 ) 976 require.NoError(err) 977 978 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 979 require.NoError(err) 980 981 onAcceptState.PutPendingValidator(staker) 982 onAcceptState.AddTx(tx, status.Committed) 983 984 executor := StandardTxExecutor{ 985 Backend: &env.backend, 986 State: onAcceptState, 987 Tx: tx, 988 } 989 err = tx.Unsigned.Visit(&executor) 990 require.ErrorIs(err, ErrAlreadyValidator) 991 } 992 993 { 994 // Case: Validator doesn't have enough tokens to cover stake amount 995 startTime := defaultValidateStartTime.Add(1 * time.Second) 996 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 997 utx, err := builder.NewAddValidatorTx( 998 &txs.Validator{ 999 NodeID: nodeID, 1000 Start: uint64(startTime.Unix()), 1001 End: uint64(startTime.Add(defaultMinStakingDuration).Unix()), 1002 Wght: env.config.MinValidatorStake, 1003 }, 1004 &secp256k1fx.OutputOwners{ 1005 Threshold: 1, 1006 Addrs: []ids.ShortID{ids.ShortEmpty}, 1007 }, 1008 reward.PercentDenominator, 1009 ) 1010 require.NoError(err) 1011 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1012 require.NoError(err) 1013 1014 // Remove all UTXOs owned by preFundedKeys[0] 1015 utxoIDs, err := env.state.UTXOIDs(preFundedKeys[0].PublicKey().Address().Bytes(), ids.Empty, math.MaxInt32) 1016 require.NoError(err) 1017 1018 onAcceptState, err := state.NewDiff(lastAcceptedID, env) 1019 require.NoError(err) 1020 1021 for _, utxoID := range utxoIDs { 1022 onAcceptState.DeleteUTXO(utxoID) 1023 } 1024 1025 executor := StandardTxExecutor{ 1026 Backend: &env.backend, 1027 State: onAcceptState, 1028 Tx: tx, 1029 } 1030 err = tx.Unsigned.Visit(&executor) 1031 require.ErrorIs(err, ErrFlowCheckFailed) 1032 } 1033 } 1034 1035 // Verifies that [AddValidatorTx] and [AddDelegatorTx] are disabled post-Durango 1036 func TestDurangoDisabledTransactions(t *testing.T) { 1037 type test struct { 1038 name string 1039 buildTx func(*environment) *txs.Tx 1040 expectedErr error 1041 } 1042 1043 tests := []test{ 1044 { 1045 name: "AddValidatorTx", 1046 buildTx: func(env *environment) *txs.Tx { 1047 var ( 1048 nodeID = ids.GenerateTestNodeID() 1049 chainTime = env.state.GetTimestamp() 1050 endTime = chainTime.Add(defaultMaxStakingDuration) 1051 ) 1052 1053 builder, signer := env.factory.NewWallet(preFundedKeys...) 1054 utx, err := builder.NewAddValidatorTx( 1055 &txs.Validator{ 1056 NodeID: nodeID, 1057 Start: 0, 1058 End: uint64(endTime.Unix()), 1059 Wght: defaultMinValidatorStake, 1060 }, 1061 &secp256k1fx.OutputOwners{ 1062 Threshold: 1, 1063 Addrs: []ids.ShortID{ids.ShortEmpty}, 1064 }, 1065 reward.PercentDenominator, 1066 ) 1067 require.NoError(t, err) 1068 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1069 require.NoError(t, err) 1070 1071 return tx 1072 }, 1073 expectedErr: ErrAddValidatorTxPostDurango, 1074 }, 1075 { 1076 name: "AddDelegatorTx", 1077 buildTx: func(env *environment) *txs.Tx { 1078 var primaryValidator *state.Staker 1079 it, err := env.state.GetCurrentStakerIterator() 1080 require.NoError(t, err) 1081 for it.Next() { 1082 staker := it.Value() 1083 if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority { 1084 continue 1085 } 1086 primaryValidator = staker 1087 break 1088 } 1089 it.Release() 1090 1091 builder, signer := env.factory.NewWallet(preFundedKeys...) 1092 utx, err := builder.NewAddDelegatorTx( 1093 &txs.Validator{ 1094 NodeID: primaryValidator.NodeID, 1095 Start: 0, 1096 End: uint64(primaryValidator.EndTime.Unix()), 1097 Wght: defaultMinValidatorStake, 1098 }, 1099 &secp256k1fx.OutputOwners{ 1100 Threshold: 1, 1101 Addrs: []ids.ShortID{ids.ShortEmpty}, 1102 }, 1103 ) 1104 require.NoError(t, err) 1105 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1106 require.NoError(t, err) 1107 1108 return tx 1109 }, 1110 expectedErr: ErrAddDelegatorTxPostDurango, 1111 }, 1112 } 1113 1114 for _, tt := range tests { 1115 t.Run(tt.name, func(t *testing.T) { 1116 require := require.New(t) 1117 1118 env := newEnvironment(t, durango) 1119 env.ctx.Lock.Lock() 1120 defer env.ctx.Lock.Unlock() 1121 1122 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1123 require.NoError(err) 1124 1125 tx := tt.buildTx(env) 1126 1127 err = tx.Unsigned.Visit(&StandardTxExecutor{ 1128 Backend: &env.backend, 1129 State: onAcceptState, 1130 Tx: tx, 1131 }) 1132 require.ErrorIs(err, tt.expectedErr) 1133 }) 1134 } 1135 } 1136 1137 // Verifies that the Memo field is required to be empty post-Durango 1138 func TestDurangoMemoField(t *testing.T) { 1139 type test struct { 1140 name string 1141 setupTest func(env *environment, memoField []byte) (*txs.Tx, state.Diff) 1142 } 1143 1144 tests := []test{ 1145 { 1146 name: "AddSubnetValidatorTx", 1147 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1148 var primaryValidator *state.Staker 1149 it, err := env.state.GetCurrentStakerIterator() 1150 require.NoError(t, err) 1151 for it.Next() { 1152 staker := it.Value() 1153 if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority { 1154 continue 1155 } 1156 primaryValidator = staker 1157 break 1158 } 1159 it.Release() 1160 1161 builder, signer := env.factory.NewWallet(preFundedKeys...) 1162 utx, err := builder.NewAddSubnetValidatorTx( 1163 &txs.SubnetValidator{ 1164 Validator: txs.Validator{ 1165 NodeID: primaryValidator.NodeID, 1166 Start: 0, 1167 End: uint64(primaryValidator.EndTime.Unix()), 1168 Wght: defaultMinValidatorStake, 1169 }, 1170 Subnet: testSubnet1.TxID, 1171 }, 1172 common.WithMemo(memoField), 1173 ) 1174 require.NoError(t, err) 1175 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1176 require.NoError(t, err) 1177 1178 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1179 require.NoError(t, err) 1180 return tx, onAcceptState 1181 }, 1182 }, 1183 { 1184 name: "CreateChainTx", 1185 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1186 builder, signer := env.factory.NewWallet(preFundedKeys...) 1187 utx, err := builder.NewCreateChainTx( 1188 testSubnet1.TxID, 1189 []byte{}, 1190 ids.GenerateTestID(), 1191 []ids.ID{}, 1192 "aaa", 1193 common.WithMemo(memoField), 1194 ) 1195 require.NoError(t, err) 1196 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1197 require.NoError(t, err) 1198 1199 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1200 require.NoError(t, err) 1201 1202 return tx, onAcceptState 1203 }, 1204 }, 1205 { 1206 name: "CreateSubnetTx", 1207 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1208 builder, signer := env.factory.NewWallet(preFundedKeys...) 1209 utx, err := builder.NewCreateSubnetTx( 1210 &secp256k1fx.OutputOwners{ 1211 Threshold: 1, 1212 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1213 }, 1214 common.WithMemo(memoField), 1215 ) 1216 require.NoError(t, err) 1217 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1218 require.NoError(t, err) 1219 1220 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1221 require.NoError(t, err) 1222 1223 return tx, onAcceptState 1224 }, 1225 }, 1226 { 1227 name: "ImportTx", 1228 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1229 // Skip shared memory checks 1230 env.backend.Bootstrapped.Set(false) 1231 1232 var ( 1233 sourceChain = env.ctx.XChainID 1234 sourceKey = preFundedKeys[1] 1235 sourceAmount = 10 * units.Avax 1236 ) 1237 1238 sharedMemory := fundedSharedMemory( 1239 t, 1240 env, 1241 sourceKey, 1242 sourceChain, 1243 map[ids.ID]uint64{ 1244 env.ctx.AVAXAssetID: sourceAmount, 1245 }, 1246 rand.NewSource(0), 1247 ) 1248 env.msm.SharedMemory = sharedMemory 1249 1250 builder, signer := env.factory.NewWallet(preFundedKeys...) 1251 utx, err := builder.NewImportTx( 1252 sourceChain, 1253 &secp256k1fx.OutputOwners{ 1254 Locktime: 0, 1255 Threshold: 1, 1256 Addrs: []ids.ShortID{sourceKey.PublicKey().Address()}, 1257 }, 1258 common.WithMemo(memoField), 1259 ) 1260 require.NoError(t, err) 1261 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1262 require.NoError(t, err) 1263 1264 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1265 require.NoError(t, err) 1266 1267 return tx, onAcceptState 1268 }, 1269 }, 1270 { 1271 name: "ExportTx", 1272 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1273 builder, signer := env.factory.NewWallet(preFundedKeys...) 1274 utx, err := builder.NewExportTx( 1275 env.ctx.XChainID, 1276 []*avax.TransferableOutput{{ 1277 Asset: avax.Asset{ID: env.ctx.AVAXAssetID}, 1278 Out: &secp256k1fx.TransferOutput{ 1279 Amt: units.Avax, 1280 OutputOwners: secp256k1fx.OutputOwners{ 1281 Locktime: 0, 1282 Threshold: 1, 1283 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1284 }, 1285 }, 1286 }}, 1287 common.WithMemo(memoField), 1288 ) 1289 require.NoError(t, err) 1290 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1291 require.NoError(t, err) 1292 1293 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1294 require.NoError(t, err) 1295 1296 return tx, onAcceptState 1297 }, 1298 }, 1299 { 1300 name: "RemoveSubnetValidatorTx", 1301 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1302 var primaryValidator *state.Staker 1303 it, err := env.state.GetCurrentStakerIterator() 1304 require.NoError(t, err) 1305 for it.Next() { 1306 staker := it.Value() 1307 if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority { 1308 continue 1309 } 1310 primaryValidator = staker 1311 break 1312 } 1313 it.Release() 1314 1315 endTime := primaryValidator.EndTime 1316 builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]) 1317 utx, err := builder.NewAddSubnetValidatorTx( 1318 &txs.SubnetValidator{ 1319 Validator: txs.Validator{ 1320 NodeID: primaryValidator.NodeID, 1321 Start: 0, 1322 End: uint64(endTime.Unix()), 1323 Wght: defaultWeight, 1324 }, 1325 Subnet: testSubnet1.ID(), 1326 }, 1327 ) 1328 require.NoError(t, err) 1329 subnetValTx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1330 require.NoError(t, err) 1331 1332 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1333 require.NoError(t, err) 1334 1335 require.NoError(t, subnetValTx.Unsigned.Visit(&StandardTxExecutor{ 1336 Backend: &env.backend, 1337 State: onAcceptState, 1338 Tx: subnetValTx, 1339 })) 1340 1341 builder, signer = env.factory.NewWallet(preFundedKeys...) 1342 utx2, err := builder.NewRemoveSubnetValidatorTx( 1343 primaryValidator.NodeID, 1344 testSubnet1.ID(), 1345 common.WithMemo(memoField), 1346 ) 1347 require.NoError(t, err) 1348 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx2) 1349 require.NoError(t, err) 1350 1351 return tx, onAcceptState 1352 }, 1353 }, 1354 { 1355 name: "TransformSubnetTx", 1356 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1357 builder, signer := env.factory.NewWallet(preFundedKeys...) 1358 utx, err := builder.NewTransformSubnetTx( 1359 testSubnet1.TxID, // subnetID 1360 ids.GenerateTestID(), // assetID 1361 10, // initial supply 1362 10, // max supply 1363 0, // min consumption rate 1364 reward.PercentDenominator, // max consumption rate 1365 2, // min validator stake 1366 10, // max validator stake 1367 time.Minute, // min stake duration 1368 time.Hour, // max stake duration 1369 1, // min delegation fees 1370 10, // min delegator stake 1371 1, // max validator weight factor 1372 80, // uptime requirement 1373 common.WithMemo(memoField), 1374 ) 1375 require.NoError(t, err) 1376 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1377 require.NoError(t, err) 1378 1379 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1380 require.NoError(t, err) 1381 1382 return tx, onAcceptState 1383 }, 1384 }, 1385 { 1386 name: "AddPermissionlessValidatorTx", 1387 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1388 var ( 1389 nodeID = ids.GenerateTestNodeID() 1390 chainTime = env.state.GetTimestamp() 1391 endTime = chainTime.Add(defaultMaxStakingDuration) 1392 ) 1393 sk, err := bls.NewSecretKey() 1394 require.NoError(t, err) 1395 1396 builder, txSigner := env.factory.NewWallet(preFundedKeys...) 1397 utx, err := builder.NewAddPermissionlessValidatorTx( 1398 &txs.SubnetValidator{ 1399 Validator: txs.Validator{ 1400 NodeID: nodeID, 1401 Start: 0, 1402 End: uint64(endTime.Unix()), 1403 Wght: env.config.MinValidatorStake, 1404 }, 1405 Subnet: constants.PrimaryNetworkID, 1406 }, 1407 signer.NewProofOfPossession(sk), 1408 env.ctx.AVAXAssetID, 1409 &secp256k1fx.OutputOwners{ 1410 Threshold: 1, 1411 Addrs: []ids.ShortID{ids.ShortEmpty}, 1412 }, 1413 &secp256k1fx.OutputOwners{ 1414 Threshold: 1, 1415 Addrs: []ids.ShortID{ids.ShortEmpty}, 1416 }, 1417 reward.PercentDenominator, 1418 common.WithMemo(memoField), 1419 ) 1420 require.NoError(t, err) 1421 tx, err := walletsigner.SignUnsigned(context.Background(), txSigner, utx) 1422 require.NoError(t, err) 1423 1424 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1425 require.NoError(t, err) 1426 1427 return tx, onAcceptState 1428 }, 1429 }, 1430 { 1431 name: "AddPermissionlessDelegatorTx", 1432 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1433 var primaryValidator *state.Staker 1434 it, err := env.state.GetCurrentStakerIterator() 1435 require.NoError(t, err) 1436 for it.Next() { 1437 staker := it.Value() 1438 if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority { 1439 continue 1440 } 1441 primaryValidator = staker 1442 break 1443 } 1444 it.Release() 1445 1446 builder, signer := env.factory.NewWallet(preFundedKeys...) 1447 utx, err := builder.NewAddPermissionlessDelegatorTx( 1448 &txs.SubnetValidator{ 1449 Validator: txs.Validator{ 1450 NodeID: primaryValidator.NodeID, 1451 Start: 0, 1452 End: uint64(primaryValidator.EndTime.Unix()), 1453 Wght: defaultMinValidatorStake, 1454 }, 1455 Subnet: constants.PrimaryNetworkID, 1456 }, 1457 env.ctx.AVAXAssetID, 1458 &secp256k1fx.OutputOwners{ 1459 Threshold: 1, 1460 Addrs: []ids.ShortID{ids.ShortEmpty}, 1461 }, 1462 common.WithMemo(memoField), 1463 ) 1464 require.NoError(t, err) 1465 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1466 require.NoError(t, err) 1467 1468 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1469 require.NoError(t, err) 1470 1471 return tx, onAcceptState 1472 }, 1473 }, 1474 { 1475 name: "TransferSubnetOwnershipTx", 1476 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1477 builder, signer := env.factory.NewWallet(preFundedKeys...) 1478 utx, err := builder.NewTransferSubnetOwnershipTx( 1479 testSubnet1.TxID, 1480 &secp256k1fx.OutputOwners{ 1481 Threshold: 1, 1482 Addrs: []ids.ShortID{ids.ShortEmpty}, 1483 }, 1484 common.WithMemo(memoField), 1485 ) 1486 require.NoError(t, err) 1487 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1488 require.NoError(t, err) 1489 1490 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1491 require.NoError(t, err) 1492 1493 return tx, onAcceptState 1494 }, 1495 }, 1496 { 1497 name: "BaseTx", 1498 setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) { 1499 builder, signer := env.factory.NewWallet(preFundedKeys...) 1500 utx, err := builder.NewBaseTx( 1501 []*avax.TransferableOutput{ 1502 { 1503 Asset: avax.Asset{ID: env.ctx.AVAXAssetID}, 1504 Out: &secp256k1fx.TransferOutput{ 1505 Amt: 1, 1506 OutputOwners: secp256k1fx.OutputOwners{ 1507 Threshold: 1, 1508 Addrs: []ids.ShortID{ids.ShortEmpty}, 1509 }, 1510 }, 1511 }, 1512 }, 1513 common.WithMemo(memoField), 1514 ) 1515 require.NoError(t, err) 1516 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 1517 require.NoError(t, err) 1518 1519 onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env) 1520 require.NoError(t, err) 1521 1522 return tx, onAcceptState 1523 }, 1524 }, 1525 } 1526 1527 for _, tt := range tests { 1528 t.Run(tt.name, func(t *testing.T) { 1529 require := require.New(t) 1530 1531 env := newEnvironment(t, durango) 1532 env.ctx.Lock.Lock() 1533 defer env.ctx.Lock.Unlock() 1534 1535 // Populated memo field should error 1536 tx, onAcceptState := tt.setupTest(env, []byte{'m', 'e', 'm', 'o'}) 1537 err := tx.Unsigned.Visit(&StandardTxExecutor{ 1538 Backend: &env.backend, 1539 State: onAcceptState, 1540 Tx: tx, 1541 }) 1542 require.ErrorIs(err, avax.ErrMemoTooLarge) 1543 1544 // Empty memo field should not error 1545 tx, onAcceptState = tt.setupTest(env, []byte{}) 1546 require.NoError(tx.Unsigned.Visit(&StandardTxExecutor{ 1547 Backend: &env.backend, 1548 State: onAcceptState, 1549 Tx: tx, 1550 })) 1551 }) 1552 } 1553 } 1554 1555 // Returns a RemoveSubnetValidatorTx that passes syntactic verification. 1556 // Memo field is empty as required post Durango activation 1557 func newRemoveSubnetValidatorTx(t *testing.T) (*txs.RemoveSubnetValidatorTx, *txs.Tx) { 1558 t.Helper() 1559 1560 creds := []verify.Verifiable{ 1561 &secp256k1fx.Credential{ 1562 Sigs: make([][65]byte, 1), 1563 }, 1564 &secp256k1fx.Credential{ 1565 Sigs: make([][65]byte, 1), 1566 }, 1567 } 1568 unsignedTx := &txs.RemoveSubnetValidatorTx{ 1569 BaseTx: txs.BaseTx{ 1570 BaseTx: avax.BaseTx{ 1571 Ins: []*avax.TransferableInput{{ 1572 UTXOID: avax.UTXOID{ 1573 TxID: ids.GenerateTestID(), 1574 }, 1575 Asset: avax.Asset{ 1576 ID: ids.GenerateTestID(), 1577 }, 1578 In: &secp256k1fx.TransferInput{ 1579 Amt: 1, 1580 Input: secp256k1fx.Input{ 1581 SigIndices: []uint32{0, 1}, 1582 }, 1583 }, 1584 }}, 1585 Outs: []*avax.TransferableOutput{ 1586 { 1587 Asset: avax.Asset{ 1588 ID: ids.GenerateTestID(), 1589 }, 1590 Out: &secp256k1fx.TransferOutput{ 1591 Amt: 1, 1592 OutputOwners: secp256k1fx.OutputOwners{ 1593 Threshold: 1, 1594 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1595 }, 1596 }, 1597 }, 1598 }, 1599 }, 1600 }, 1601 Subnet: ids.GenerateTestID(), 1602 NodeID: ids.GenerateTestNodeID(), 1603 SubnetAuth: &secp256k1fx.Credential{ 1604 Sigs: make([][65]byte, 1), 1605 }, 1606 } 1607 tx := &txs.Tx{ 1608 Unsigned: unsignedTx, 1609 Creds: creds, 1610 } 1611 require.NoError(t, tx.Initialize(txs.Codec)) 1612 return unsignedTx, tx 1613 } 1614 1615 // mock implementations that can be used in tests 1616 // for verifying RemoveSubnetValidatorTx. 1617 type removeSubnetValidatorTxVerifyEnv struct { 1618 latestForkTime time.Time 1619 fx *fx.MockFx 1620 flowChecker *utxo.MockVerifier 1621 unsignedTx *txs.RemoveSubnetValidatorTx 1622 tx *txs.Tx 1623 state *state.MockDiff 1624 staker *state.Staker 1625 } 1626 1627 // Returns mock implementations that can be used in tests 1628 // for verifying RemoveSubnetValidatorTx. 1629 func newValidRemoveSubnetValidatorTxVerifyEnv(t *testing.T, ctrl *gomock.Controller) removeSubnetValidatorTxVerifyEnv { 1630 t.Helper() 1631 1632 now := time.Now() 1633 mockFx := fx.NewMockFx(ctrl) 1634 mockFlowChecker := utxo.NewMockVerifier(ctrl) 1635 unsignedTx, tx := newRemoveSubnetValidatorTx(t) 1636 mockState := state.NewMockDiff(ctrl) 1637 return removeSubnetValidatorTxVerifyEnv{ 1638 latestForkTime: now, 1639 fx: mockFx, 1640 flowChecker: mockFlowChecker, 1641 unsignedTx: unsignedTx, 1642 tx: tx, 1643 state: mockState, 1644 staker: &state.Staker{ 1645 TxID: ids.GenerateTestID(), 1646 NodeID: ids.GenerateTestNodeID(), 1647 Priority: txs.SubnetPermissionedValidatorCurrentPriority, 1648 }, 1649 } 1650 } 1651 1652 func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { 1653 type test struct { 1654 name string 1655 newExecutor func(*gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) 1656 expectedErr error 1657 } 1658 1659 tests := []test{ 1660 { 1661 name: "valid tx", 1662 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1663 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1664 1665 // Set dependency expectations. 1666 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 1667 env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil).Times(1) 1668 subnetOwner := fx.NewMockOwner(ctrl) 1669 env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1) 1670 env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1) 1671 env.flowChecker.EXPECT().VerifySpend( 1672 env.unsignedTx, env.state, env.unsignedTx.Ins, env.unsignedTx.Outs, env.tx.Creds[:len(env.tx.Creds)-1], gomock.Any(), 1673 ).Return(nil).Times(1) 1674 env.state.EXPECT().DeleteCurrentValidator(env.staker) 1675 env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins)) 1676 env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs)) 1677 e := &StandardTxExecutor{ 1678 Backend: &Backend{ 1679 Config: defaultTestConfig(t, durango, env.latestForkTime), 1680 Bootstrapped: &utils.Atomic[bool]{}, 1681 Fx: env.fx, 1682 FlowChecker: env.flowChecker, 1683 Ctx: &snow.Context{}, 1684 }, 1685 Tx: env.tx, 1686 State: env.state, 1687 } 1688 e.Bootstrapped.Set(true) 1689 return env.unsignedTx, e 1690 }, 1691 expectedErr: nil, 1692 }, 1693 { 1694 name: "tx fails syntactic verification", 1695 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1696 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1697 // Setting the subnet ID to the Primary Network ID makes the tx fail syntactic verification 1698 env.tx.Unsigned.(*txs.RemoveSubnetValidatorTx).Subnet = constants.PrimaryNetworkID 1699 env.state = state.NewMockDiff(ctrl) 1700 e := &StandardTxExecutor{ 1701 Backend: &Backend{ 1702 Config: defaultTestConfig(t, durango, env.latestForkTime), 1703 Bootstrapped: &utils.Atomic[bool]{}, 1704 Fx: env.fx, 1705 FlowChecker: env.flowChecker, 1706 Ctx: &snow.Context{}, 1707 }, 1708 Tx: env.tx, 1709 State: env.state, 1710 } 1711 e.Bootstrapped.Set(true) 1712 return env.unsignedTx, e 1713 }, 1714 expectedErr: txs.ErrRemovePrimaryNetworkValidator, 1715 }, 1716 { 1717 name: "node isn't a validator of the subnet", 1718 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1719 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1720 env.state = state.NewMockDiff(ctrl) 1721 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 1722 env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound) 1723 env.state.EXPECT().GetPendingValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound) 1724 e := &StandardTxExecutor{ 1725 Backend: &Backend{ 1726 Config: defaultTestConfig(t, durango, env.latestForkTime), 1727 Bootstrapped: &utils.Atomic[bool]{}, 1728 Fx: env.fx, 1729 FlowChecker: env.flowChecker, 1730 Ctx: &snow.Context{}, 1731 }, 1732 Tx: env.tx, 1733 State: env.state, 1734 } 1735 e.Bootstrapped.Set(true) 1736 return env.unsignedTx, e 1737 }, 1738 expectedErr: ErrNotValidator, 1739 }, 1740 { 1741 name: "validator is permissionless", 1742 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1743 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1744 1745 staker := *env.staker 1746 staker.Priority = txs.SubnetPermissionlessValidatorCurrentPriority 1747 1748 // Set dependency expectations. 1749 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 1750 env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(&staker, nil).Times(1) 1751 e := &StandardTxExecutor{ 1752 Backend: &Backend{ 1753 Config: defaultTestConfig(t, durango, env.latestForkTime), 1754 Bootstrapped: &utils.Atomic[bool]{}, 1755 Fx: env.fx, 1756 FlowChecker: env.flowChecker, 1757 Ctx: &snow.Context{}, 1758 }, 1759 Tx: env.tx, 1760 State: env.state, 1761 } 1762 e.Bootstrapped.Set(true) 1763 return env.unsignedTx, e 1764 }, 1765 expectedErr: ErrRemovePermissionlessValidator, 1766 }, 1767 { 1768 name: "tx has no credentials", 1769 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1770 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1771 // Remove credentials 1772 env.tx.Creds = nil 1773 env.state = state.NewMockDiff(ctrl) 1774 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 1775 env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) 1776 e := &StandardTxExecutor{ 1777 Backend: &Backend{ 1778 Config: defaultTestConfig(t, durango, env.latestForkTime), 1779 Bootstrapped: &utils.Atomic[bool]{}, 1780 Fx: env.fx, 1781 FlowChecker: env.flowChecker, 1782 Ctx: &snow.Context{}, 1783 }, 1784 Tx: env.tx, 1785 State: env.state, 1786 } 1787 e.Bootstrapped.Set(true) 1788 return env.unsignedTx, e 1789 }, 1790 expectedErr: errWrongNumberOfCredentials, 1791 }, 1792 { 1793 name: "can't find subnet", 1794 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1795 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1796 env.state = state.NewMockDiff(ctrl) 1797 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 1798 env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) 1799 env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound) 1800 e := &StandardTxExecutor{ 1801 Backend: &Backend{ 1802 Config: defaultTestConfig(t, durango, env.latestForkTime), 1803 Bootstrapped: &utils.Atomic[bool]{}, 1804 Fx: env.fx, 1805 FlowChecker: env.flowChecker, 1806 Ctx: &snow.Context{}, 1807 }, 1808 Tx: env.tx, 1809 State: env.state, 1810 } 1811 e.Bootstrapped.Set(true) 1812 return env.unsignedTx, e 1813 }, 1814 expectedErr: database.ErrNotFound, 1815 }, 1816 { 1817 name: "no permission to remove validator", 1818 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1819 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1820 env.state = state.NewMockDiff(ctrl) 1821 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 1822 env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) 1823 subnetOwner := fx.NewMockOwner(ctrl) 1824 env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil) 1825 env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(errTest) 1826 e := &StandardTxExecutor{ 1827 Backend: &Backend{ 1828 Config: defaultTestConfig(t, durango, env.latestForkTime), 1829 Bootstrapped: &utils.Atomic[bool]{}, 1830 Fx: env.fx, 1831 FlowChecker: env.flowChecker, 1832 Ctx: &snow.Context{}, 1833 }, 1834 Tx: env.tx, 1835 State: env.state, 1836 } 1837 e.Bootstrapped.Set(true) 1838 return env.unsignedTx, e 1839 }, 1840 expectedErr: errUnauthorizedSubnetModification, 1841 }, 1842 { 1843 name: "flow checker failed", 1844 newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { 1845 env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) 1846 env.state = state.NewMockDiff(ctrl) 1847 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 1848 env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) 1849 subnetOwner := fx.NewMockOwner(ctrl) 1850 env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil) 1851 env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil) 1852 env.flowChecker.EXPECT().VerifySpend( 1853 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), 1854 ).Return(errTest) 1855 e := &StandardTxExecutor{ 1856 Backend: &Backend{ 1857 Config: defaultTestConfig(t, durango, env.latestForkTime), 1858 Bootstrapped: &utils.Atomic[bool]{}, 1859 Fx: env.fx, 1860 FlowChecker: env.flowChecker, 1861 Ctx: &snow.Context{}, 1862 }, 1863 Tx: env.tx, 1864 State: env.state, 1865 } 1866 e.Bootstrapped.Set(true) 1867 return env.unsignedTx, e 1868 }, 1869 expectedErr: ErrFlowCheckFailed, 1870 }, 1871 } 1872 1873 for _, tt := range tests { 1874 t.Run(tt.name, func(t *testing.T) { 1875 require := require.New(t) 1876 ctrl := gomock.NewController(t) 1877 1878 unsignedTx, executor := tt.newExecutor(ctrl) 1879 err := executor.RemoveSubnetValidatorTx(unsignedTx) 1880 require.ErrorIs(err, tt.expectedErr) 1881 }) 1882 } 1883 } 1884 1885 // Returns a TransformSubnetTx that passes syntactic verification. 1886 // Memo field is empty as required post Durango activation 1887 func newTransformSubnetTx(t *testing.T) (*txs.TransformSubnetTx, *txs.Tx) { 1888 t.Helper() 1889 1890 creds := []verify.Verifiable{ 1891 &secp256k1fx.Credential{ 1892 Sigs: make([][65]byte, 1), 1893 }, 1894 &secp256k1fx.Credential{ 1895 Sigs: make([][65]byte, 1), 1896 }, 1897 } 1898 unsignedTx := &txs.TransformSubnetTx{ 1899 BaseTx: txs.BaseTx{ 1900 BaseTx: avax.BaseTx{ 1901 Ins: []*avax.TransferableInput{{ 1902 UTXOID: avax.UTXOID{ 1903 TxID: ids.GenerateTestID(), 1904 }, 1905 Asset: avax.Asset{ 1906 ID: ids.GenerateTestID(), 1907 }, 1908 In: &secp256k1fx.TransferInput{ 1909 Amt: 1, 1910 Input: secp256k1fx.Input{ 1911 SigIndices: []uint32{0, 1}, 1912 }, 1913 }, 1914 }}, 1915 Outs: []*avax.TransferableOutput{ 1916 { 1917 Asset: avax.Asset{ 1918 ID: ids.GenerateTestID(), 1919 }, 1920 Out: &secp256k1fx.TransferOutput{ 1921 Amt: 1, 1922 OutputOwners: secp256k1fx.OutputOwners{ 1923 Threshold: 1, 1924 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1925 }, 1926 }, 1927 }, 1928 }, 1929 }, 1930 }, 1931 Subnet: ids.GenerateTestID(), 1932 AssetID: ids.GenerateTestID(), 1933 InitialSupply: 10, 1934 MaximumSupply: 10, 1935 MinConsumptionRate: 0, 1936 MaxConsumptionRate: reward.PercentDenominator, 1937 MinValidatorStake: 2, 1938 MaxValidatorStake: 10, 1939 MinStakeDuration: 1, 1940 MaxStakeDuration: 2, 1941 MinDelegationFee: reward.PercentDenominator, 1942 MinDelegatorStake: 1, 1943 MaxValidatorWeightFactor: 1, 1944 UptimeRequirement: reward.PercentDenominator, 1945 SubnetAuth: &secp256k1fx.Credential{ 1946 Sigs: make([][65]byte, 1), 1947 }, 1948 } 1949 tx := &txs.Tx{ 1950 Unsigned: unsignedTx, 1951 Creds: creds, 1952 } 1953 require.NoError(t, tx.Initialize(txs.Codec)) 1954 return unsignedTx, tx 1955 } 1956 1957 // mock implementations that can be used in tests 1958 // for verifying TransformSubnetTx. 1959 type transformSubnetTxVerifyEnv struct { 1960 latestForkTime time.Time 1961 fx *fx.MockFx 1962 flowChecker *utxo.MockVerifier 1963 unsignedTx *txs.TransformSubnetTx 1964 tx *txs.Tx 1965 state *state.MockDiff 1966 staker *state.Staker 1967 } 1968 1969 // Returns mock implementations that can be used in tests 1970 // for verifying TransformSubnetTx. 1971 func newValidTransformSubnetTxVerifyEnv(t *testing.T, ctrl *gomock.Controller) transformSubnetTxVerifyEnv { 1972 t.Helper() 1973 1974 now := time.Now() 1975 mockFx := fx.NewMockFx(ctrl) 1976 mockFlowChecker := utxo.NewMockVerifier(ctrl) 1977 unsignedTx, tx := newTransformSubnetTx(t) 1978 mockState := state.NewMockDiff(ctrl) 1979 return transformSubnetTxVerifyEnv{ 1980 latestForkTime: now, 1981 fx: mockFx, 1982 flowChecker: mockFlowChecker, 1983 unsignedTx: unsignedTx, 1984 tx: tx, 1985 state: mockState, 1986 staker: &state.Staker{ 1987 TxID: ids.GenerateTestID(), 1988 NodeID: ids.GenerateTestNodeID(), 1989 }, 1990 } 1991 } 1992 1993 func TestStandardExecutorTransformSubnetTx(t *testing.T) { 1994 type test struct { 1995 name string 1996 newExecutor func(*gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) 1997 err error 1998 } 1999 2000 tests := []test{ 2001 { 2002 name: "tx fails syntactic verification", 2003 newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { 2004 env := newValidTransformSubnetTxVerifyEnv(t, ctrl) 2005 // Setting the tx to nil makes the tx fail syntactic verification 2006 env.tx.Unsigned = (*txs.TransformSubnetTx)(nil) 2007 env.state = state.NewMockDiff(ctrl) 2008 e := &StandardTxExecutor{ 2009 Backend: &Backend{ 2010 Config: defaultTestConfig(t, durango, env.latestForkTime), 2011 Bootstrapped: &utils.Atomic[bool]{}, 2012 Fx: env.fx, 2013 FlowChecker: env.flowChecker, 2014 Ctx: &snow.Context{}, 2015 }, 2016 Tx: env.tx, 2017 State: env.state, 2018 } 2019 e.Bootstrapped.Set(true) 2020 return env.unsignedTx, e 2021 }, 2022 err: txs.ErrNilTx, 2023 }, 2024 { 2025 name: "max stake duration too large", 2026 newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { 2027 env := newValidTransformSubnetTxVerifyEnv(t, ctrl) 2028 env.unsignedTx.MaxStakeDuration = math.MaxUint32 2029 env.state = state.NewMockDiff(ctrl) 2030 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 2031 e := &StandardTxExecutor{ 2032 Backend: &Backend{ 2033 Config: defaultTestConfig(t, durango, env.latestForkTime), 2034 Bootstrapped: &utils.Atomic[bool]{}, 2035 Fx: env.fx, 2036 FlowChecker: env.flowChecker, 2037 Ctx: &snow.Context{}, 2038 }, 2039 Tx: env.tx, 2040 State: env.state, 2041 } 2042 e.Bootstrapped.Set(true) 2043 return env.unsignedTx, e 2044 }, 2045 err: errMaxStakeDurationTooLarge, 2046 }, 2047 { 2048 name: "fail subnet authorization", 2049 newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { 2050 env := newValidTransformSubnetTxVerifyEnv(t, ctrl) 2051 // Remove credentials 2052 env.tx.Creds = nil 2053 env.state = state.NewMockDiff(ctrl) 2054 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 2055 2056 cfg := defaultTestConfig(t, durango, env.latestForkTime) 2057 cfg.MaxStakeDuration = math.MaxInt64 2058 2059 e := &StandardTxExecutor{ 2060 Backend: &Backend{ 2061 Config: cfg, 2062 Bootstrapped: &utils.Atomic[bool]{}, 2063 Fx: env.fx, 2064 FlowChecker: env.flowChecker, 2065 Ctx: &snow.Context{}, 2066 }, 2067 Tx: env.tx, 2068 State: env.state, 2069 } 2070 e.Bootstrapped.Set(true) 2071 return env.unsignedTx, e 2072 }, 2073 err: errWrongNumberOfCredentials, 2074 }, 2075 { 2076 name: "flow checker failed", 2077 newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { 2078 env := newValidTransformSubnetTxVerifyEnv(t, ctrl) 2079 env.state = state.NewMockDiff(ctrl) 2080 subnetOwner := fx.NewMockOwner(ctrl) 2081 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 2082 env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil) 2083 env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1) 2084 env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil) 2085 env.flowChecker.EXPECT().VerifySpend( 2086 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), 2087 ).Return(ErrFlowCheckFailed) 2088 2089 cfg := defaultTestConfig(t, durango, env.latestForkTime) 2090 cfg.MaxStakeDuration = math.MaxInt64 2091 2092 e := &StandardTxExecutor{ 2093 Backend: &Backend{ 2094 Config: cfg, 2095 Bootstrapped: &utils.Atomic[bool]{}, 2096 Fx: env.fx, 2097 FlowChecker: env.flowChecker, 2098 Ctx: &snow.Context{}, 2099 }, 2100 Tx: env.tx, 2101 State: env.state, 2102 } 2103 e.Bootstrapped.Set(true) 2104 return env.unsignedTx, e 2105 }, 2106 err: ErrFlowCheckFailed, 2107 }, 2108 { 2109 name: "valid tx", 2110 newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) { 2111 env := newValidTransformSubnetTxVerifyEnv(t, ctrl) 2112 2113 // Set dependency expectations. 2114 subnetOwner := fx.NewMockOwner(ctrl) 2115 env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) 2116 env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1) 2117 env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1) 2118 env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1) 2119 env.flowChecker.EXPECT().VerifySpend( 2120 env.unsignedTx, env.state, env.unsignedTx.Ins, env.unsignedTx.Outs, env.tx.Creds[:len(env.tx.Creds)-1], gomock.Any(), 2121 ).Return(nil).Times(1) 2122 env.state.EXPECT().AddSubnetTransformation(env.tx) 2123 env.state.EXPECT().SetCurrentSupply(env.unsignedTx.Subnet, env.unsignedTx.InitialSupply) 2124 env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins)) 2125 env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs)) 2126 2127 cfg := defaultTestConfig(t, durango, env.latestForkTime) 2128 cfg.MaxStakeDuration = math.MaxInt64 2129 2130 e := &StandardTxExecutor{ 2131 Backend: &Backend{ 2132 Config: cfg, 2133 Bootstrapped: &utils.Atomic[bool]{}, 2134 Fx: env.fx, 2135 FlowChecker: env.flowChecker, 2136 Ctx: &snow.Context{}, 2137 }, 2138 Tx: env.tx, 2139 State: env.state, 2140 } 2141 e.Bootstrapped.Set(true) 2142 return env.unsignedTx, e 2143 }, 2144 err: nil, 2145 }, 2146 } 2147 2148 for _, tt := range tests { 2149 t.Run(tt.name, func(t *testing.T) { 2150 ctrl := gomock.NewController(t) 2151 2152 unsignedTx, executor := tt.newExecutor(ctrl) 2153 err := executor.TransformSubnetTx(unsignedTx) 2154 require.ErrorIs(t, err, tt.err) 2155 }) 2156 } 2157 } 2158 2159 func defaultTestConfig(t *testing.T, f fork, tm time.Time) *config.Config { 2160 c := &config.Config{ 2161 UpgradeConfig: upgrade.Config{ 2162 ApricotPhase3Time: mockable.MaxTime, 2163 ApricotPhase5Time: mockable.MaxTime, 2164 BanffTime: mockable.MaxTime, 2165 CortinaTime: mockable.MaxTime, 2166 DurangoTime: mockable.MaxTime, 2167 EUpgradeTime: mockable.MaxTime, 2168 }, 2169 } 2170 2171 switch f { 2172 case eUpgrade: 2173 c.UpgradeConfig.EUpgradeTime = tm 2174 fallthrough 2175 case durango: 2176 c.UpgradeConfig.DurangoTime = tm 2177 fallthrough 2178 case cortina: 2179 c.UpgradeConfig.CortinaTime = tm 2180 fallthrough 2181 case banff: 2182 c.UpgradeConfig.BanffTime = tm 2183 fallthrough 2184 case apricotPhase5: 2185 c.UpgradeConfig.ApricotPhase5Time = tm 2186 fallthrough 2187 case apricotPhase3: 2188 c.UpgradeConfig.ApricotPhase3Time = tm 2189 default: 2190 require.FailNow(t, "unhandled fork", f) 2191 } 2192 2193 return c 2194 }