github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/executor/staker_tx_verification.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 "errors" 8 "fmt" 9 "math" 10 "time" 11 12 "github.com/MetalBlockchain/metalgo/database" 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/utils/constants" 15 "github.com/MetalBlockchain/metalgo/vms/components/avax" 16 "github.com/MetalBlockchain/metalgo/vms/platformvm/state" 17 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs" 18 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/fee" 19 20 safemath "github.com/MetalBlockchain/metalgo/utils/math" 21 ) 22 23 var ( 24 ErrWeightTooSmall = errors.New("weight of this validator is too low") 25 ErrWeightTooLarge = errors.New("weight of this validator is too large") 26 ErrInsufficientDelegationFee = errors.New("staker charges an insufficient delegation fee") 27 ErrStakeTooShort = errors.New("staking period is too short") 28 ErrStakeTooLong = errors.New("staking period is too long") 29 ErrFlowCheckFailed = errors.New("flow check failed") 30 ErrNotValidator = errors.New("isn't a current or pending validator") 31 ErrRemovePermissionlessValidator = errors.New("attempting to remove permissionless validator") 32 ErrStakeOverflow = errors.New("validator stake exceeds limit") 33 ErrPeriodMismatch = errors.New("proposed staking period is not inside dependant staking period") 34 ErrOverDelegated = errors.New("validator would be over delegated") 35 ErrIsNotTransformSubnetTx = errors.New("is not a transform subnet tx") 36 ErrTimestampNotBeforeStartTime = errors.New("chain timestamp not before start time") 37 ErrAlreadyValidator = errors.New("already a validator") 38 ErrDuplicateValidator = errors.New("duplicate validator") 39 ErrDelegateToPermissionedValidator = errors.New("delegation to permissioned validator") 40 ErrWrongStakedAssetID = errors.New("incorrect staked assetID") 41 ErrDurangoUpgradeNotActive = errors.New("attempting to use a Durango-upgrade feature prior to activation") 42 ErrAddValidatorTxPostDurango = errors.New("AddValidatorTx is not permitted post-Durango") 43 ErrAddDelegatorTxPostDurango = errors.New("AddDelegatorTx is not permitted post-Durango") 44 ) 45 46 // verifySubnetValidatorPrimaryNetworkRequirements verifies the primary 47 // network requirements for [subnetValidator]. An error is returned if they 48 // are not fulfilled. 49 func verifySubnetValidatorPrimaryNetworkRequirements( 50 isDurangoActive bool, 51 chainState state.Chain, 52 subnetValidator txs.Validator, 53 ) error { 54 primaryNetworkValidator, err := GetValidator(chainState, constants.PrimaryNetworkID, subnetValidator.NodeID) 55 if err == database.ErrNotFound { 56 return fmt.Errorf( 57 "%s %w of the primary network", 58 subnetValidator.NodeID, 59 ErrNotValidator, 60 ) 61 } 62 if err != nil { 63 return fmt.Errorf( 64 "failed to fetch the primary network validator for %s: %w", 65 subnetValidator.NodeID, 66 err, 67 ) 68 } 69 70 // Ensure that the period this validator validates the specified subnet 71 // is a subset of the time they validate the primary network. 72 startTime := chainState.GetTimestamp() 73 if !isDurangoActive { 74 startTime = subnetValidator.StartTime() 75 } 76 if !txs.BoundedBy( 77 startTime, 78 subnetValidator.EndTime(), 79 primaryNetworkValidator.StartTime, 80 primaryNetworkValidator.EndTime, 81 ) { 82 return ErrPeriodMismatch 83 } 84 85 return nil 86 } 87 88 // verifyAddValidatorTx carries out the validation for an AddValidatorTx. 89 // It returns the tx outputs that should be returned if this validator is not 90 // added to the staking set. 91 func verifyAddValidatorTx( 92 backend *Backend, 93 chainState state.Chain, 94 sTx *txs.Tx, 95 tx *txs.AddValidatorTx, 96 ) ( 97 []*avax.TransferableOutput, 98 error, 99 ) { 100 currentTimestamp := chainState.GetTimestamp() 101 if backend.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) { 102 return nil, ErrAddValidatorTxPostDurango 103 } 104 105 // Verify the tx is well-formed 106 if err := sTx.SyntacticVerify(backend.Ctx); err != nil { 107 return nil, err 108 } 109 110 if err := avax.VerifyMemoFieldLength(tx.Memo, false /*=isDurangoActive*/); err != nil { 111 return nil, err 112 } 113 114 startTime := tx.StartTime() 115 duration := tx.EndTime().Sub(startTime) 116 switch { 117 case tx.Validator.Wght < backend.Config.MinValidatorStake: 118 // Ensure validator is staking at least the minimum amount 119 return nil, ErrWeightTooSmall 120 121 case tx.Validator.Wght > backend.Config.MaxValidatorStake: 122 // Ensure validator isn't staking too much 123 return nil, ErrWeightTooLarge 124 125 case tx.DelegationShares < backend.Config.MinDelegationFee: 126 // Ensure the validator fee is at least the minimum amount 127 return nil, ErrInsufficientDelegationFee 128 129 case duration < backend.Config.MinStakeDuration: 130 // Ensure staking length is not too short 131 return nil, ErrStakeTooShort 132 133 case duration > backend.Config.MaxStakeDuration: 134 // Ensure staking length is not too long 135 return nil, ErrStakeTooLong 136 } 137 138 outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) 139 copy(outs, tx.Outs) 140 copy(outs[len(tx.Outs):], tx.StakeOuts) 141 142 if !backend.Bootstrapped.Get() { 143 return outs, nil 144 } 145 146 if err := verifyStakerStartTime(false /*=isDurangoActive*/, currentTimestamp, startTime); err != nil { 147 return nil, err 148 } 149 150 _, err := GetValidator(chainState, constants.PrimaryNetworkID, tx.Validator.NodeID) 151 if err == nil { 152 return nil, fmt.Errorf( 153 "%s is %w of the primary network", 154 tx.Validator.NodeID, 155 ErrAlreadyValidator, 156 ) 157 } 158 if err != database.ErrNotFound { 159 return nil, fmt.Errorf( 160 "failed to find whether %s is a primary network validator: %w", 161 tx.Validator.NodeID, 162 err, 163 ) 164 } 165 166 // Verify the flowcheck 167 feeCalculator := fee.NewStaticCalculator(backend.Config.StaticFeeConfig, backend.Config.UpgradeConfig) 168 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 169 170 if err := backend.FlowChecker.VerifySpend( 171 tx, 172 chainState, 173 tx.Ins, 174 outs, 175 sTx.Creds, 176 map[ids.ID]uint64{ 177 backend.Ctx.AVAXAssetID: fee, 178 }, 179 ); err != nil { 180 return nil, fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) 181 } 182 183 return outs, nil 184 } 185 186 // verifyAddSubnetValidatorTx carries out the validation for an 187 // AddSubnetValidatorTx. 188 func verifyAddSubnetValidatorTx( 189 backend *Backend, 190 chainState state.Chain, 191 sTx *txs.Tx, 192 tx *txs.AddSubnetValidatorTx, 193 ) error { 194 // Verify the tx is well-formed 195 if err := sTx.SyntacticVerify(backend.Ctx); err != nil { 196 return err 197 } 198 199 var ( 200 currentTimestamp = chainState.GetTimestamp() 201 isDurangoActive = backend.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 202 ) 203 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 204 return err 205 } 206 207 startTime := currentTimestamp 208 if !isDurangoActive { 209 startTime = tx.StartTime() 210 } 211 duration := tx.EndTime().Sub(startTime) 212 213 switch { 214 case duration < backend.Config.MinStakeDuration: 215 // Ensure staking length is not too short 216 return ErrStakeTooShort 217 218 case duration > backend.Config.MaxStakeDuration: 219 // Ensure staking length is not too long 220 return ErrStakeTooLong 221 } 222 223 if !backend.Bootstrapped.Get() { 224 return nil 225 } 226 227 if err := verifyStakerStartTime(isDurangoActive, currentTimestamp, startTime); err != nil { 228 return err 229 } 230 231 _, err := GetValidator(chainState, tx.SubnetValidator.Subnet, tx.Validator.NodeID) 232 if err == nil { 233 return fmt.Errorf( 234 "attempted to issue %w for %s on subnet %s", 235 ErrDuplicateValidator, 236 tx.Validator.NodeID, 237 tx.SubnetValidator.Subnet, 238 ) 239 } 240 if err != database.ErrNotFound { 241 return fmt.Errorf( 242 "failed to find whether %s is a subnet validator: %w", 243 tx.Validator.NodeID, 244 err, 245 ) 246 } 247 248 if err := verifySubnetValidatorPrimaryNetworkRequirements(isDurangoActive, chainState, tx.Validator); err != nil { 249 return err 250 } 251 252 baseTxCreds, err := verifyPoASubnetAuthorization(backend, chainState, sTx, tx.SubnetValidator.Subnet, tx.SubnetAuth) 253 if err != nil { 254 return err 255 } 256 257 // Verify the flowcheck 258 feeCalculator := fee.NewStaticCalculator(backend.Config.StaticFeeConfig, backend.Config.UpgradeConfig) 259 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 260 261 if err := backend.FlowChecker.VerifySpend( 262 tx, 263 chainState, 264 tx.Ins, 265 tx.Outs, 266 baseTxCreds, 267 map[ids.ID]uint64{ 268 backend.Ctx.AVAXAssetID: fee, 269 }, 270 ); err != nil { 271 return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) 272 } 273 274 return nil 275 } 276 277 // Returns the representation of [tx.NodeID] validating [tx.Subnet]. 278 // Returns true if [tx.NodeID] is a current validator of [tx.Subnet]. 279 // Returns an error if the given tx is invalid. 280 // The transaction is valid if: 281 // * [tx.NodeID] is a current/pending PoA validator of [tx.Subnet]. 282 // * [sTx]'s creds authorize it to spend the stated inputs. 283 // * [sTx]'s creds authorize it to remove a validator from [tx.Subnet]. 284 // * The flow checker passes. 285 func verifyRemoveSubnetValidatorTx( 286 backend *Backend, 287 chainState state.Chain, 288 sTx *txs.Tx, 289 tx *txs.RemoveSubnetValidatorTx, 290 ) (*state.Staker, bool, error) { 291 // Verify the tx is well-formed 292 if err := sTx.SyntacticVerify(backend.Ctx); err != nil { 293 return nil, false, err 294 } 295 296 var ( 297 currentTimestamp = chainState.GetTimestamp() 298 isDurangoActive = backend.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 299 ) 300 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 301 return nil, false, err 302 } 303 304 isCurrentValidator := true 305 vdr, err := chainState.GetCurrentValidator(tx.Subnet, tx.NodeID) 306 if err == database.ErrNotFound { 307 vdr, err = chainState.GetPendingValidator(tx.Subnet, tx.NodeID) 308 isCurrentValidator = false 309 } 310 if err != nil { 311 // It isn't a current or pending validator. 312 return nil, false, fmt.Errorf( 313 "%s %w of %s: %w", 314 tx.NodeID, 315 ErrNotValidator, 316 tx.Subnet, 317 err, 318 ) 319 } 320 321 if !vdr.Priority.IsPermissionedValidator() { 322 return nil, false, ErrRemovePermissionlessValidator 323 } 324 325 if !backend.Bootstrapped.Get() { 326 // Not bootstrapped yet -- don't need to do full verification. 327 return vdr, isCurrentValidator, nil 328 } 329 330 baseTxCreds, err := verifySubnetAuthorization(backend, chainState, sTx, tx.Subnet, tx.SubnetAuth) 331 if err != nil { 332 return nil, false, err 333 } 334 335 // Verify the flowcheck 336 feeCalculator := fee.NewStaticCalculator(backend.Config.StaticFeeConfig, backend.Config.UpgradeConfig) 337 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 338 339 if err := backend.FlowChecker.VerifySpend( 340 tx, 341 chainState, 342 tx.Ins, 343 tx.Outs, 344 baseTxCreds, 345 map[ids.ID]uint64{ 346 backend.Ctx.AVAXAssetID: fee, 347 }, 348 ); err != nil { 349 return nil, false, fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) 350 } 351 352 return vdr, isCurrentValidator, nil 353 } 354 355 // verifyAddDelegatorTx carries out the validation for an AddDelegatorTx. 356 // It returns the tx outputs that should be returned if this delegator is not 357 // added to the staking set. 358 func verifyAddDelegatorTx( 359 backend *Backend, 360 chainState state.Chain, 361 sTx *txs.Tx, 362 tx *txs.AddDelegatorTx, 363 ) ( 364 []*avax.TransferableOutput, 365 error, 366 ) { 367 currentTimestamp := chainState.GetTimestamp() 368 if backend.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) { 369 return nil, ErrAddDelegatorTxPostDurango 370 } 371 372 // Verify the tx is well-formed 373 if err := sTx.SyntacticVerify(backend.Ctx); err != nil { 374 return nil, err 375 } 376 377 if err := avax.VerifyMemoFieldLength(tx.Memo, false /*=isDurangoActive*/); err != nil { 378 return nil, err 379 } 380 381 var ( 382 endTime = tx.EndTime() 383 startTime = tx.StartTime() 384 duration = endTime.Sub(startTime) 385 ) 386 switch { 387 case duration < backend.Config.MinStakeDuration: 388 // Ensure staking length is not too short 389 return nil, ErrStakeTooShort 390 391 case duration > backend.Config.MaxStakeDuration: 392 // Ensure staking length is not too long 393 return nil, ErrStakeTooLong 394 395 case tx.Validator.Wght < backend.Config.MinDelegatorStake: 396 // Ensure validator is staking at least the minimum amount 397 return nil, ErrWeightTooSmall 398 } 399 400 outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) 401 copy(outs, tx.Outs) 402 copy(outs[len(tx.Outs):], tx.StakeOuts) 403 404 if !backend.Bootstrapped.Get() { 405 return outs, nil 406 } 407 408 if err := verifyStakerStartTime(false /*=isDurangoActive*/, currentTimestamp, startTime); err != nil { 409 return nil, err 410 } 411 412 primaryNetworkValidator, err := GetValidator(chainState, constants.PrimaryNetworkID, tx.Validator.NodeID) 413 if err != nil { 414 return nil, fmt.Errorf( 415 "failed to fetch the primary network validator for %s: %w", 416 tx.Validator.NodeID, 417 err, 418 ) 419 } 420 421 maximumWeight, err := safemath.Mul64(MaxValidatorWeightFactor, primaryNetworkValidator.Weight) 422 if err != nil { 423 return nil, ErrStakeOverflow 424 } 425 426 if backend.Config.UpgradeConfig.IsApricotPhase3Activated(currentTimestamp) { 427 maximumWeight = min(maximumWeight, backend.Config.MaxValidatorStake) 428 } 429 430 if !txs.BoundedBy( 431 startTime, 432 endTime, 433 primaryNetworkValidator.StartTime, 434 primaryNetworkValidator.EndTime, 435 ) { 436 return nil, ErrPeriodMismatch 437 } 438 overDelegated, err := overDelegated( 439 chainState, 440 primaryNetworkValidator, 441 maximumWeight, 442 tx.Validator.Wght, 443 startTime, 444 endTime, 445 ) 446 if err != nil { 447 return nil, err 448 } 449 if overDelegated { 450 return nil, ErrOverDelegated 451 } 452 453 // Verify the flowcheck 454 feeCalculator := fee.NewStaticCalculator(backend.Config.StaticFeeConfig, backend.Config.UpgradeConfig) 455 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 456 457 if err := backend.FlowChecker.VerifySpend( 458 tx, 459 chainState, 460 tx.Ins, 461 outs, 462 sTx.Creds, 463 map[ids.ID]uint64{ 464 backend.Ctx.AVAXAssetID: fee, 465 }, 466 ); err != nil { 467 return nil, fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) 468 } 469 470 return outs, nil 471 } 472 473 // verifyAddPermissionlessValidatorTx carries out the validation for an 474 // AddPermissionlessValidatorTx. 475 func verifyAddPermissionlessValidatorTx( 476 backend *Backend, 477 chainState state.Chain, 478 sTx *txs.Tx, 479 tx *txs.AddPermissionlessValidatorTx, 480 ) error { 481 // Verify the tx is well-formed 482 if err := sTx.SyntacticVerify(backend.Ctx); err != nil { 483 return err 484 } 485 486 var ( 487 currentTimestamp = chainState.GetTimestamp() 488 isDurangoActive = backend.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 489 ) 490 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 491 return err 492 } 493 494 if !backend.Bootstrapped.Get() { 495 return nil 496 } 497 498 startTime := currentTimestamp 499 if !isDurangoActive { 500 startTime = tx.StartTime() 501 } 502 duration := tx.EndTime().Sub(startTime) 503 504 if err := verifyStakerStartTime(isDurangoActive, currentTimestamp, startTime); err != nil { 505 return err 506 } 507 508 validatorRules, err := getValidatorRules(backend, chainState, tx.Subnet) 509 if err != nil { 510 return err 511 } 512 513 stakedAssetID := tx.StakeOuts[0].AssetID() 514 switch { 515 case tx.Validator.Wght < validatorRules.minValidatorStake: 516 // Ensure validator is staking at least the minimum amount 517 return ErrWeightTooSmall 518 519 case tx.Validator.Wght > validatorRules.maxValidatorStake: 520 // Ensure validator isn't staking too much 521 return ErrWeightTooLarge 522 523 case tx.DelegationShares < validatorRules.minDelegationFee: 524 // Ensure the validator fee is at least the minimum amount 525 return ErrInsufficientDelegationFee 526 527 case duration < validatorRules.minStakeDuration: 528 // Ensure staking length is not too short 529 return ErrStakeTooShort 530 531 case duration > validatorRules.maxStakeDuration: 532 // Ensure staking length is not too long 533 return ErrStakeTooLong 534 535 case stakedAssetID != validatorRules.assetID: 536 // Wrong assetID used 537 return fmt.Errorf( 538 "%w: %s != %s", 539 ErrWrongStakedAssetID, 540 validatorRules.assetID, 541 stakedAssetID, 542 ) 543 } 544 545 _, err = GetValidator(chainState, tx.Subnet, tx.Validator.NodeID) 546 if err == nil { 547 return fmt.Errorf( 548 "%w: %s on %s", 549 ErrDuplicateValidator, 550 tx.Validator.NodeID, 551 tx.Subnet, 552 ) 553 } 554 if err != database.ErrNotFound { 555 return fmt.Errorf( 556 "failed to find whether %s is a validator on %s: %w", 557 tx.Validator.NodeID, 558 tx.Subnet, 559 err, 560 ) 561 } 562 563 if tx.Subnet != constants.PrimaryNetworkID { 564 if err := verifySubnetValidatorPrimaryNetworkRequirements(isDurangoActive, chainState, tx.Validator); err != nil { 565 return err 566 } 567 } 568 569 outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) 570 copy(outs, tx.Outs) 571 copy(outs[len(tx.Outs):], tx.StakeOuts) 572 573 // Verify the flowcheck 574 feeCalculator := fee.NewStaticCalculator(backend.Config.StaticFeeConfig, backend.Config.UpgradeConfig) 575 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 576 577 if err := backend.FlowChecker.VerifySpend( 578 tx, 579 chainState, 580 tx.Ins, 581 outs, 582 sTx.Creds, 583 map[ids.ID]uint64{ 584 backend.Ctx.AVAXAssetID: fee, 585 }, 586 ); err != nil { 587 return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) 588 } 589 590 return nil 591 } 592 593 // verifyAddPermissionlessDelegatorTx carries out the validation for an 594 // AddPermissionlessDelegatorTx. 595 func verifyAddPermissionlessDelegatorTx( 596 backend *Backend, 597 chainState state.Chain, 598 sTx *txs.Tx, 599 tx *txs.AddPermissionlessDelegatorTx, 600 ) error { 601 // Verify the tx is well-formed 602 if err := sTx.SyntacticVerify(backend.Ctx); err != nil { 603 return err 604 } 605 606 var ( 607 currentTimestamp = chainState.GetTimestamp() 608 isDurangoActive = backend.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 609 ) 610 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 611 return err 612 } 613 614 if !backend.Bootstrapped.Get() { 615 return nil 616 } 617 618 var ( 619 endTime = tx.EndTime() 620 startTime = currentTimestamp 621 ) 622 if !isDurangoActive { 623 startTime = tx.StartTime() 624 } 625 duration := endTime.Sub(startTime) 626 627 if err := verifyStakerStartTime(isDurangoActive, currentTimestamp, startTime); err != nil { 628 return err 629 } 630 631 delegatorRules, err := getDelegatorRules(backend, chainState, tx.Subnet) 632 if err != nil { 633 return err 634 } 635 636 stakedAssetID := tx.StakeOuts[0].AssetID() 637 switch { 638 case tx.Validator.Wght < delegatorRules.minDelegatorStake: 639 // Ensure delegator is staking at least the minimum amount 640 return ErrWeightTooSmall 641 642 case duration < delegatorRules.minStakeDuration: 643 // Ensure staking length is not too short 644 return ErrStakeTooShort 645 646 case duration > delegatorRules.maxStakeDuration: 647 // Ensure staking length is not too long 648 return ErrStakeTooLong 649 650 case stakedAssetID != delegatorRules.assetID: 651 // Wrong assetID used 652 return fmt.Errorf( 653 "%w: %s != %s", 654 ErrWrongStakedAssetID, 655 delegatorRules.assetID, 656 stakedAssetID, 657 ) 658 } 659 660 validator, err := GetValidator(chainState, tx.Subnet, tx.Validator.NodeID) 661 if err != nil { 662 return fmt.Errorf( 663 "failed to fetch the validator for %s on %s: %w", 664 tx.Validator.NodeID, 665 tx.Subnet, 666 err, 667 ) 668 } 669 670 maximumWeight, err := safemath.Mul64( 671 uint64(delegatorRules.maxValidatorWeightFactor), 672 validator.Weight, 673 ) 674 if err != nil { 675 maximumWeight = math.MaxUint64 676 } 677 maximumWeight = min(maximumWeight, delegatorRules.maxValidatorStake) 678 679 if !txs.BoundedBy( 680 startTime, 681 endTime, 682 validator.StartTime, 683 validator.EndTime, 684 ) { 685 return ErrPeriodMismatch 686 } 687 overDelegated, err := overDelegated( 688 chainState, 689 validator, 690 maximumWeight, 691 tx.Validator.Wght, 692 startTime, 693 endTime, 694 ) 695 if err != nil { 696 return err 697 } 698 if overDelegated { 699 return ErrOverDelegated 700 } 701 702 outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) 703 copy(outs, tx.Outs) 704 copy(outs[len(tx.Outs):], tx.StakeOuts) 705 706 if tx.Subnet != constants.PrimaryNetworkID { 707 // Invariant: Delegators must only be able to reference validator 708 // transactions that implement [txs.ValidatorTx]. All 709 // validator transactions implement this interface except the 710 // AddSubnetValidatorTx. AddSubnetValidatorTx is the only 711 // permissioned validator, so we verify this delegator is 712 // pointing to a permissionless validator. 713 if validator.Priority.IsPermissionedValidator() { 714 return ErrDelegateToPermissionedValidator 715 } 716 } 717 718 // Verify the flowcheck 719 feeCalculator := fee.NewStaticCalculator(backend.Config.StaticFeeConfig, backend.Config.UpgradeConfig) 720 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 721 722 if err := backend.FlowChecker.VerifySpend( 723 tx, 724 chainState, 725 tx.Ins, 726 outs, 727 sTx.Creds, 728 map[ids.ID]uint64{ 729 backend.Ctx.AVAXAssetID: fee, 730 }, 731 ); err != nil { 732 return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) 733 } 734 735 return nil 736 } 737 738 // Returns an error if the given tx is invalid. 739 // The transaction is valid if: 740 // * [sTx]'s creds authorize it to spend the stated inputs. 741 // * [sTx]'s creds authorize it to transfer ownership of [tx.Subnet]. 742 // * The flow checker passes. 743 func verifyTransferSubnetOwnershipTx( 744 backend *Backend, 745 chainState state.Chain, 746 sTx *txs.Tx, 747 tx *txs.TransferSubnetOwnershipTx, 748 ) error { 749 if !backend.Config.UpgradeConfig.IsDurangoActivated(chainState.GetTimestamp()) { 750 return ErrDurangoUpgradeNotActive 751 } 752 753 // Verify the tx is well-formed 754 if err := sTx.SyntacticVerify(backend.Ctx); err != nil { 755 return err 756 } 757 758 if err := avax.VerifyMemoFieldLength(tx.Memo, true /*=isDurangoActive*/); err != nil { 759 return err 760 } 761 762 if !backend.Bootstrapped.Get() { 763 // Not bootstrapped yet -- don't need to do full verification. 764 return nil 765 } 766 767 baseTxCreds, err := verifySubnetAuthorization(backend, chainState, sTx, tx.Subnet, tx.SubnetAuth) 768 if err != nil { 769 return err 770 } 771 772 // Verify the flowcheck 773 currentTimestamp := chainState.GetTimestamp() 774 feeCalculator := fee.NewStaticCalculator(backend.Config.StaticFeeConfig, backend.Config.UpgradeConfig) 775 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 776 777 if err := backend.FlowChecker.VerifySpend( 778 tx, 779 chainState, 780 tx.Ins, 781 tx.Outs, 782 baseTxCreds, 783 map[ids.ID]uint64{ 784 backend.Ctx.AVAXAssetID: fee, 785 }, 786 ); err != nil { 787 return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) 788 } 789 790 return nil 791 } 792 793 // Ensure the proposed validator starts after the current time 794 func verifyStakerStartTime(isDurangoActive bool, chainTime, stakerTime time.Time) error { 795 // Pre Durango activation, start time must be after current chain time. 796 // Post Durango activation, start time is not validated 797 if isDurangoActive { 798 return nil 799 } 800 801 if !chainTime.Before(stakerTime) { 802 return fmt.Errorf( 803 "%w: %s >= %s", 804 ErrTimestampNotBeforeStartTime, 805 chainTime, 806 stakerTime, 807 ) 808 } 809 return nil 810 }