github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/transactionPayerBalanceChecker.go (about) 1 package fvm 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/cadence" 7 8 "github.com/onflow/flow-go/fvm/environment" 9 "github.com/onflow/flow-go/fvm/errors" 10 "github.com/onflow/flow-go/fvm/storage" 11 ) 12 13 type TransactionPayerBalanceChecker struct{} 14 15 const VerifyPayerBalanceResultTypeCanExecuteTransactionFieldName = "canExecuteTransaction" 16 const VerifyPayerBalanceResultTypeRequiredBalanceFieldName = "requiredBalance" 17 const VerifyPayerBalanceResultTypeMaximumTransactionFeesFieldName = "maximumTransactionFees" 18 19 var VerifyPayerBalanceResultType = &cadence.StructType{ 20 Fields: []cadence.Field{ 21 { 22 Identifier: VerifyPayerBalanceResultTypeCanExecuteTransactionFieldName, 23 Type: cadence.BoolType, 24 }, 25 { 26 Identifier: VerifyPayerBalanceResultTypeRequiredBalanceFieldName, 27 Type: cadence.UFix64Type, 28 }, 29 { 30 Identifier: VerifyPayerBalanceResultTypeMaximumTransactionFeesFieldName, 31 Type: cadence.UFix64Type, 32 }, 33 }, 34 } 35 36 // decodeVerifyPayerBalanceResult decodes the VerifyPayerBalanceResult struct 37 // https://github.com/onflow/flow-core-contracts/blob/7c70c6a1d33c2879b60c78e363fa68fc6fce13b9/contracts/FlowFees.cdc#L75 38 func decodeVerifyPayerBalanceResult(resultValue cadence.Value) ( 39 canExecuteTransaction cadence.Bool, 40 requiredBalance cadence.UFix64, 41 maximumTransactionFees cadence.UFix64, 42 err error, 43 ) { 44 result, ok := resultValue.(cadence.Struct) 45 if !ok { 46 return false, 0, 0, fmt.Errorf("invalid VerifyPayerBalanceResult value: not a struct") 47 } 48 49 fields := cadence.FieldsMappedByName(result) 50 51 canExecuteTransaction, ok = fields[VerifyPayerBalanceResultTypeCanExecuteTransactionFieldName].(cadence.Bool) 52 if !ok { 53 return false, 0, 0, fmt.Errorf( 54 "invalid VerifyPayerBalanceResult field: %s", 55 VerifyPayerBalanceResultTypeCanExecuteTransactionFieldName, 56 ) 57 } 58 59 requiredBalance, ok = fields[VerifyPayerBalanceResultTypeRequiredBalanceFieldName].(cadence.UFix64) 60 if !ok { 61 return false, 0, 0, fmt.Errorf( 62 "invalid VerifyPayerBalanceResult field: %s", 63 VerifyPayerBalanceResultTypeRequiredBalanceFieldName, 64 ) 65 } 66 67 maximumTransactionFees, ok = fields[VerifyPayerBalanceResultTypeMaximumTransactionFeesFieldName].(cadence.UFix64) 68 if !ok { 69 return false, 0, 0, fmt.Errorf( 70 "invalid VerifyPayerBalanceResult field: %s", 71 VerifyPayerBalanceResultTypeMaximumTransactionFeesFieldName, 72 ) 73 } 74 75 return canExecuteTransaction, requiredBalance, maximumTransactionFees, nil 76 } 77 78 func (_ TransactionPayerBalanceChecker) CheckPayerBalanceAndReturnMaxFees( 79 proc *TransactionProcedure, 80 txnState storage.TransactionPreparer, 81 env environment.Environment, 82 ) (uint64, error) { 83 if !env.TransactionFeesEnabled() { 84 // if the transaction fees are not enabled, 85 // the payer can pay for the transaction, 86 // and the max transaction fees are 0. 87 // This is also the condition that gets hit during bootstrapping. 88 return 0, nil 89 } 90 91 var resultValue cadence.Value 92 var err error 93 txnState.RunWithAllLimitsDisabled(func() { 94 // Don't meter the payer balance check. 95 // It has a static cost, and its cost should be part of the inclusion fees, not part of execution fees. 96 resultValue, err = env.CheckPayerBalanceAndGetMaxTxFees( 97 proc.Transaction.Payer, 98 proc.Transaction.InclusionEffort(), 99 uint64(txnState.TotalComputationLimit()), 100 ) 101 }) 102 if err != nil { 103 return 0, errors.NewPayerBalanceCheckFailure(proc.Transaction.Payer, err) 104 } 105 106 payerCanPay, requiredBalance, maxFees, err := decodeVerifyPayerBalanceResult(resultValue) 107 if err != nil { 108 return 0, errors.NewPayerBalanceCheckFailure(proc.Transaction.Payer, err) 109 } 110 111 if !payerCanPay { 112 return 0, errors.NewInsufficientPayerBalanceError(proc.Transaction.Payer, requiredBalance) 113 } 114 115 return uint64(maxFees), nil 116 }