github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/executor/standard_tx_executor.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 "fmt" 10 "time" 11 12 "go.uber.org/zap" 13 14 "github.com/MetalBlockchain/metalgo/chains/atomic" 15 "github.com/MetalBlockchain/metalgo/ids" 16 "github.com/MetalBlockchain/metalgo/utils/constants" 17 "github.com/MetalBlockchain/metalgo/utils/set" 18 "github.com/MetalBlockchain/metalgo/vms/components/avax" 19 "github.com/MetalBlockchain/metalgo/vms/components/verify" 20 "github.com/MetalBlockchain/metalgo/vms/platformvm/state" 21 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs" 22 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/fee" 23 ) 24 25 var ( 26 _ txs.Visitor = (*StandardTxExecutor)(nil) 27 28 errEmptyNodeID = errors.New("validator nodeID cannot be empty") 29 errMaxStakeDurationTooLarge = errors.New("max stake duration must be less than or equal to the global max stake duration") 30 errMissingStartTimePreDurango = errors.New("staker transactions must have a StartTime pre-Durango") 31 ) 32 33 type StandardTxExecutor struct { 34 // inputs, to be filled before visitor methods are called 35 *Backend 36 State state.Diff // state is expected to be modified 37 Tx *txs.Tx 38 39 // outputs of visitor execution 40 OnAccept func() // may be nil 41 Inputs set.Set[ids.ID] 42 AtomicRequests map[ids.ID]*atomic.Requests // may be nil 43 } 44 45 func (*StandardTxExecutor) AdvanceTimeTx(*txs.AdvanceTimeTx) error { 46 return ErrWrongTxType 47 } 48 49 func (*StandardTxExecutor) RewardValidatorTx(*txs.RewardValidatorTx) error { 50 return ErrWrongTxType 51 } 52 53 func (e *StandardTxExecutor) CreateChainTx(tx *txs.CreateChainTx) error { 54 if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { 55 return err 56 } 57 58 var ( 59 currentTimestamp = e.State.GetTimestamp() 60 isDurangoActive = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 61 ) 62 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 63 return err 64 } 65 66 baseTxCreds, err := verifyPoASubnetAuthorization(e.Backend, e.State, e.Tx, tx.SubnetID, tx.SubnetAuth) 67 if err != nil { 68 return err 69 } 70 71 // Verify the flowcheck 72 feeCalculator := fee.NewStaticCalculator(e.Backend.Config.StaticFeeConfig, e.Backend.Config.UpgradeConfig) 73 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 74 75 if err := e.FlowChecker.VerifySpend( 76 tx, 77 e.State, 78 tx.Ins, 79 tx.Outs, 80 baseTxCreds, 81 map[ids.ID]uint64{ 82 e.Ctx.AVAXAssetID: fee, 83 }, 84 ); err != nil { 85 return err 86 } 87 88 txID := e.Tx.ID() 89 90 // Consume the UTXOS 91 avax.Consume(e.State, tx.Ins) 92 // Produce the UTXOS 93 avax.Produce(e.State, txID, tx.Outs) 94 // Add the new chain to the database 95 e.State.AddChain(e.Tx) 96 97 // If this proposal is committed and this node is a member of the subnet 98 // that validates the blockchain, create the blockchain 99 e.OnAccept = func() { 100 e.Config.CreateChain(txID, tx) 101 } 102 return nil 103 } 104 105 func (e *StandardTxExecutor) CreateSubnetTx(tx *txs.CreateSubnetTx) error { 106 // Make sure this transaction is well formed. 107 if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { 108 return err 109 } 110 111 var ( 112 currentTimestamp = e.State.GetTimestamp() 113 isDurangoActive = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 114 ) 115 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 116 return err 117 } 118 119 // Verify the flowcheck 120 feeCalculator := fee.NewStaticCalculator(e.Backend.Config.StaticFeeConfig, e.Backend.Config.UpgradeConfig) 121 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 122 123 if err := e.FlowChecker.VerifySpend( 124 tx, 125 e.State, 126 tx.Ins, 127 tx.Outs, 128 e.Tx.Creds, 129 map[ids.ID]uint64{ 130 e.Ctx.AVAXAssetID: fee, 131 }, 132 ); err != nil { 133 return err 134 } 135 136 txID := e.Tx.ID() 137 138 // Consume the UTXOS 139 avax.Consume(e.State, tx.Ins) 140 // Produce the UTXOS 141 avax.Produce(e.State, txID, tx.Outs) 142 // Add the new subnet to the database 143 e.State.AddSubnet(txID) 144 e.State.SetSubnetOwner(txID, tx.Owner) 145 return nil 146 } 147 148 func (e *StandardTxExecutor) ImportTx(tx *txs.ImportTx) error { 149 if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { 150 return err 151 } 152 153 var ( 154 currentTimestamp = e.State.GetTimestamp() 155 isDurangoActive = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 156 ) 157 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 158 return err 159 } 160 161 e.Inputs = set.NewSet[ids.ID](len(tx.ImportedInputs)) 162 utxoIDs := make([][]byte, len(tx.ImportedInputs)) 163 for i, in := range tx.ImportedInputs { 164 utxoID := in.UTXOID.InputID() 165 166 e.Inputs.Add(utxoID) 167 utxoIDs[i] = utxoID[:] 168 } 169 170 // Skip verification of the shared memory inputs if the other primary 171 // network chains are not guaranteed to be up-to-date. 172 if e.Bootstrapped.Get() && !e.Config.PartialSyncPrimaryNetwork { 173 if err := verify.SameSubnet(context.TODO(), e.Ctx, tx.SourceChain); err != nil { 174 return err 175 } 176 177 allUTXOBytes, err := e.Ctx.SharedMemory.Get(tx.SourceChain, utxoIDs) 178 if err != nil { 179 return fmt.Errorf("failed to get shared memory: %w", err) 180 } 181 182 utxos := make([]*avax.UTXO, len(tx.Ins)+len(tx.ImportedInputs)) 183 for index, input := range tx.Ins { 184 utxo, err := e.State.GetUTXO(input.InputID()) 185 if err != nil { 186 return fmt.Errorf("failed to get UTXO %s: %w", &input.UTXOID, err) 187 } 188 utxos[index] = utxo 189 } 190 for i, utxoBytes := range allUTXOBytes { 191 utxo := &avax.UTXO{} 192 if _, err := txs.Codec.Unmarshal(utxoBytes, utxo); err != nil { 193 return fmt.Errorf("failed to unmarshal UTXO: %w", err) 194 } 195 utxos[i+len(tx.Ins)] = utxo 196 } 197 198 ins := make([]*avax.TransferableInput, len(tx.Ins)+len(tx.ImportedInputs)) 199 copy(ins, tx.Ins) 200 copy(ins[len(tx.Ins):], tx.ImportedInputs) 201 202 // Verify the flowcheck 203 feeCalculator := fee.NewStaticCalculator(e.Backend.Config.StaticFeeConfig, e.Backend.Config.UpgradeConfig) 204 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 205 206 if err := e.FlowChecker.VerifySpendUTXOs( 207 tx, 208 utxos, 209 ins, 210 tx.Outs, 211 e.Tx.Creds, 212 map[ids.ID]uint64{ 213 e.Ctx.AVAXAssetID: fee, 214 }, 215 ); err != nil { 216 return err 217 } 218 } 219 220 txID := e.Tx.ID() 221 222 // Consume the UTXOS 223 avax.Consume(e.State, tx.Ins) 224 // Produce the UTXOS 225 avax.Produce(e.State, txID, tx.Outs) 226 227 // Note: We apply atomic requests even if we are not verifying atomic 228 // requests to ensure the shared state will be correct if we later start 229 // verifying the requests. 230 e.AtomicRequests = map[ids.ID]*atomic.Requests{ 231 tx.SourceChain: { 232 RemoveRequests: utxoIDs, 233 }, 234 } 235 return nil 236 } 237 238 func (e *StandardTxExecutor) ExportTx(tx *txs.ExportTx) error { 239 if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { 240 return err 241 } 242 243 var ( 244 currentTimestamp = e.State.GetTimestamp() 245 isDurangoActive = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 246 ) 247 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 248 return err 249 } 250 251 outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.ExportedOutputs)) 252 copy(outs, tx.Outs) 253 copy(outs[len(tx.Outs):], tx.ExportedOutputs) 254 255 if e.Bootstrapped.Get() { 256 if err := verify.SameSubnet(context.TODO(), e.Ctx, tx.DestinationChain); err != nil { 257 return err 258 } 259 } 260 261 // Verify the flowcheck 262 feeCalculator := fee.NewStaticCalculator(e.Backend.Config.StaticFeeConfig, e.Backend.Config.UpgradeConfig) 263 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 264 265 if err := e.FlowChecker.VerifySpend( 266 tx, 267 e.State, 268 tx.Ins, 269 outs, 270 e.Tx.Creds, 271 map[ids.ID]uint64{ 272 e.Ctx.AVAXAssetID: fee, 273 }, 274 ); err != nil { 275 return fmt.Errorf("failed verifySpend: %w", err) 276 } 277 278 txID := e.Tx.ID() 279 280 // Consume the UTXOS 281 avax.Consume(e.State, tx.Ins) 282 // Produce the UTXOS 283 avax.Produce(e.State, txID, tx.Outs) 284 285 // Note: We apply atomic requests even if we are not verifying atomic 286 // requests to ensure the shared state will be correct if we later start 287 // verifying the requests. 288 elems := make([]*atomic.Element, len(tx.ExportedOutputs)) 289 for i, out := range tx.ExportedOutputs { 290 utxo := &avax.UTXO{ 291 UTXOID: avax.UTXOID{ 292 TxID: txID, 293 OutputIndex: uint32(len(tx.Outs) + i), 294 }, 295 Asset: avax.Asset{ID: out.AssetID()}, 296 Out: out.Out, 297 } 298 299 utxoBytes, err := txs.Codec.Marshal(txs.CodecVersion, utxo) 300 if err != nil { 301 return fmt.Errorf("failed to marshal UTXO: %w", err) 302 } 303 utxoID := utxo.InputID() 304 elem := &atomic.Element{ 305 Key: utxoID[:], 306 Value: utxoBytes, 307 } 308 if out, ok := utxo.Out.(avax.Addressable); ok { 309 elem.Traits = out.Addresses() 310 } 311 312 elems[i] = elem 313 } 314 e.AtomicRequests = map[ids.ID]*atomic.Requests{ 315 tx.DestinationChain: { 316 PutRequests: elems, 317 }, 318 } 319 return nil 320 } 321 322 func (e *StandardTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error { 323 if tx.Validator.NodeID == ids.EmptyNodeID { 324 return errEmptyNodeID 325 } 326 327 if _, err := verifyAddValidatorTx( 328 e.Backend, 329 e.State, 330 e.Tx, 331 tx, 332 ); err != nil { 333 return err 334 } 335 336 if err := e.putStaker(tx); err != nil { 337 return err 338 } 339 340 txID := e.Tx.ID() 341 avax.Consume(e.State, tx.Ins) 342 avax.Produce(e.State, txID, tx.Outs) 343 344 if e.Config.PartialSyncPrimaryNetwork && tx.Validator.NodeID == e.Ctx.NodeID { 345 e.Ctx.Log.Warn("verified transaction that would cause this node to become unhealthy", 346 zap.String("reason", "primary network is not being fully synced"), 347 zap.Stringer("txID", txID), 348 zap.String("txType", "addValidator"), 349 zap.Stringer("nodeID", tx.Validator.NodeID), 350 ) 351 } 352 return nil 353 } 354 355 func (e *StandardTxExecutor) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error { 356 if err := verifyAddSubnetValidatorTx( 357 e.Backend, 358 e.State, 359 e.Tx, 360 tx, 361 ); err != nil { 362 return err 363 } 364 365 if err := e.putStaker(tx); err != nil { 366 return err 367 } 368 369 txID := e.Tx.ID() 370 avax.Consume(e.State, tx.Ins) 371 avax.Produce(e.State, txID, tx.Outs) 372 return nil 373 } 374 375 func (e *StandardTxExecutor) AddDelegatorTx(tx *txs.AddDelegatorTx) error { 376 if _, err := verifyAddDelegatorTx( 377 e.Backend, 378 e.State, 379 e.Tx, 380 tx, 381 ); err != nil { 382 return err 383 } 384 385 if err := e.putStaker(tx); err != nil { 386 return err 387 } 388 389 txID := e.Tx.ID() 390 avax.Consume(e.State, tx.Ins) 391 avax.Produce(e.State, txID, tx.Outs) 392 return nil 393 } 394 395 // Verifies a [*txs.RemoveSubnetValidatorTx] and, if it passes, executes it on 396 // [e.State]. For verification rules, see [verifyRemoveSubnetValidatorTx]. This 397 // transaction will result in [tx.NodeID] being removed as a validator of 398 // [tx.SubnetID]. 399 // Note: [tx.NodeID] may be either a current or pending validator. 400 func (e *StandardTxExecutor) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { 401 staker, isCurrentValidator, err := verifyRemoveSubnetValidatorTx( 402 e.Backend, 403 e.State, 404 e.Tx, 405 tx, 406 ) 407 if err != nil { 408 return err 409 } 410 411 if isCurrentValidator { 412 e.State.DeleteCurrentValidator(staker) 413 } else { 414 e.State.DeletePendingValidator(staker) 415 } 416 417 // Invariant: There are no permissioned subnet delegators to remove. 418 419 txID := e.Tx.ID() 420 avax.Consume(e.State, tx.Ins) 421 avax.Produce(e.State, txID, tx.Outs) 422 423 return nil 424 } 425 426 func (e *StandardTxExecutor) TransformSubnetTx(tx *txs.TransformSubnetTx) error { 427 if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { 428 return err 429 } 430 431 var ( 432 currentTimestamp = e.State.GetTimestamp() 433 isDurangoActive = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp) 434 ) 435 if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil { 436 return err 437 } 438 439 // Note: math.MaxInt32 * time.Second < math.MaxInt64 - so this can never 440 // overflow. 441 if time.Duration(tx.MaxStakeDuration)*time.Second > e.Backend.Config.MaxStakeDuration { 442 return errMaxStakeDurationTooLarge 443 } 444 445 baseTxCreds, err := verifyPoASubnetAuthorization(e.Backend, e.State, e.Tx, tx.Subnet, tx.SubnetAuth) 446 if err != nil { 447 return err 448 } 449 450 // Verify the flowcheck 451 feeCalculator := fee.NewStaticCalculator(e.Backend.Config.StaticFeeConfig, e.Backend.Config.UpgradeConfig) 452 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 453 454 totalRewardAmount := tx.MaximumSupply - tx.InitialSupply 455 if err := e.Backend.FlowChecker.VerifySpend( 456 tx, 457 e.State, 458 tx.Ins, 459 tx.Outs, 460 baseTxCreds, 461 // Invariant: [tx.AssetID != e.Ctx.AVAXAssetID]. This prevents the first 462 // entry in this map literal from being overwritten by the 463 // second entry. 464 map[ids.ID]uint64{ 465 e.Ctx.AVAXAssetID: fee, 466 tx.AssetID: totalRewardAmount, 467 }, 468 ); err != nil { 469 return err 470 } 471 472 txID := e.Tx.ID() 473 474 // Consume the UTXOS 475 avax.Consume(e.State, tx.Ins) 476 // Produce the UTXOS 477 avax.Produce(e.State, txID, tx.Outs) 478 // Transform the new subnet in the database 479 e.State.AddSubnetTransformation(e.Tx) 480 e.State.SetCurrentSupply(tx.Subnet, tx.InitialSupply) 481 return nil 482 } 483 484 func (e *StandardTxExecutor) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { 485 if err := verifyAddPermissionlessValidatorTx( 486 e.Backend, 487 e.State, 488 e.Tx, 489 tx, 490 ); err != nil { 491 return err 492 } 493 494 if err := e.putStaker(tx); err != nil { 495 return err 496 } 497 498 txID := e.Tx.ID() 499 avax.Consume(e.State, tx.Ins) 500 avax.Produce(e.State, txID, tx.Outs) 501 502 if e.Config.PartialSyncPrimaryNetwork && 503 tx.Subnet == constants.PrimaryNetworkID && 504 tx.Validator.NodeID == e.Ctx.NodeID { 505 e.Ctx.Log.Warn("verified transaction that would cause this node to become unhealthy", 506 zap.String("reason", "primary network is not being fully synced"), 507 zap.Stringer("txID", txID), 508 zap.String("txType", "addPermissionlessValidator"), 509 zap.Stringer("nodeID", tx.Validator.NodeID), 510 ) 511 } 512 513 return nil 514 } 515 516 func (e *StandardTxExecutor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { 517 if err := verifyAddPermissionlessDelegatorTx( 518 e.Backend, 519 e.State, 520 e.Tx, 521 tx, 522 ); err != nil { 523 return err 524 } 525 526 if err := e.putStaker(tx); err != nil { 527 return err 528 } 529 530 txID := e.Tx.ID() 531 avax.Consume(e.State, tx.Ins) 532 avax.Produce(e.State, txID, tx.Outs) 533 return nil 534 } 535 536 // Verifies a [*txs.TransferSubnetOwnershipTx] and, if it passes, executes it on 537 // [e.State]. For verification rules, see [verifyTransferSubnetOwnershipTx]. 538 // This transaction will result in the ownership of [tx.Subnet] being transferred 539 // to [tx.Owner]. 540 func (e *StandardTxExecutor) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error { 541 err := verifyTransferSubnetOwnershipTx( 542 e.Backend, 543 e.State, 544 e.Tx, 545 tx, 546 ) 547 if err != nil { 548 return err 549 } 550 551 e.State.SetSubnetOwner(tx.Subnet, tx.Owner) 552 553 txID := e.Tx.ID() 554 avax.Consume(e.State, tx.Ins) 555 avax.Produce(e.State, txID, tx.Outs) 556 return nil 557 } 558 559 func (e *StandardTxExecutor) BaseTx(tx *txs.BaseTx) error { 560 if !e.Backend.Config.UpgradeConfig.IsDurangoActivated(e.State.GetTimestamp()) { 561 return ErrDurangoUpgradeNotActive 562 } 563 564 // Verify the tx is well-formed 565 if err := e.Tx.SyntacticVerify(e.Ctx); err != nil { 566 return err 567 } 568 569 if err := avax.VerifyMemoFieldLength(tx.Memo, true /*=isDurangoActive*/); err != nil { 570 return err 571 } 572 573 // Verify the flowcheck 574 currentTimestamp := e.State.GetTimestamp() 575 feeCalculator := fee.NewStaticCalculator(e.Backend.Config.StaticFeeConfig, e.Backend.Config.UpgradeConfig) 576 fee := feeCalculator.CalculateFee(tx, currentTimestamp) 577 578 if err := e.FlowChecker.VerifySpend( 579 tx, 580 e.State, 581 tx.Ins, 582 tx.Outs, 583 e.Tx.Creds, 584 map[ids.ID]uint64{ 585 e.Ctx.AVAXAssetID: fee, 586 }, 587 ); err != nil { 588 return err 589 } 590 591 txID := e.Tx.ID() 592 // Consume the UTXOS 593 avax.Consume(e.State, tx.Ins) 594 // Produce the UTXOS 595 avax.Produce(e.State, txID, tx.Outs) 596 return nil 597 } 598 599 // Creates the staker as defined in [stakerTx] and adds it to [e.State]. 600 func (e *StandardTxExecutor) putStaker(stakerTx txs.Staker) error { 601 var ( 602 chainTime = e.State.GetTimestamp() 603 txID = e.Tx.ID() 604 staker *state.Staker 605 err error 606 ) 607 608 if !e.Config.UpgradeConfig.IsDurangoActivated(chainTime) { 609 // Pre-Durango, stakers set a future [StartTime] and are added to the 610 // pending staker set. They are promoted to the current staker set once 611 // the chain time reaches [StartTime]. 612 scheduledStakerTx, ok := stakerTx.(txs.ScheduledStaker) 613 if !ok { 614 return fmt.Errorf("%w: %T", errMissingStartTimePreDurango, stakerTx) 615 } 616 staker, err = state.NewPendingStaker(txID, scheduledStakerTx) 617 } else { 618 // Only calculate the potentialReward for permissionless stakers. 619 // Recall that we only need to check if this is a permissioned 620 // validator as there are no permissioned delegators 621 var potentialReward uint64 622 if !stakerTx.CurrentPriority().IsPermissionedValidator() { 623 subnetID := stakerTx.SubnetID() 624 currentSupply, err := e.State.GetCurrentSupply(subnetID) 625 if err != nil { 626 return err 627 } 628 629 rewards, err := GetRewardsCalculator(e.Backend, e.State, subnetID) 630 if err != nil { 631 return err 632 } 633 634 // Post-Durango, stakers are immediately added to the current staker 635 // set. Their [StartTime] is the current chain time. 636 stakeDuration := stakerTx.EndTime().Sub(chainTime) 637 potentialReward = rewards.Calculate( 638 stakeDuration, 639 stakerTx.Weight(), 640 currentSupply, 641 ) 642 643 e.State.SetCurrentSupply(subnetID, currentSupply+potentialReward) 644 } 645 646 staker, err = state.NewCurrentStaker(txID, stakerTx, chainTime, potentialReward) 647 } 648 if err != nil { 649 return err 650 } 651 652 switch priority := staker.Priority; { 653 case priority.IsCurrentValidator(): 654 e.State.PutCurrentValidator(staker) 655 case priority.IsCurrentDelegator(): 656 e.State.PutCurrentDelegator(staker) 657 case priority.IsPendingValidator(): 658 e.State.PutPendingValidator(staker) 659 case priority.IsPendingDelegator(): 660 e.State.PutPendingDelegator(staker) 661 default: 662 return fmt.Errorf("staker %s, unexpected priority %d", staker.TxID, priority) 663 } 664 return nil 665 }