github.com/onflow/flow-go@v0.33.17/fvm/environment/system_contracts.go (about) 1 package environment 2 3 import ( 4 "github.com/onflow/cadence" 5 "github.com/onflow/cadence/runtime/common" 6 "github.com/onflow/cadence/runtime/sema" 7 "go.opentelemetry.io/otel/attribute" 8 9 "github.com/onflow/flow-go/fvm/systemcontracts" 10 "github.com/onflow/flow-go/fvm/tracing" 11 "github.com/onflow/flow-go/model/flow" 12 "github.com/onflow/flow-go/module/trace" 13 ) 14 15 // ContractFunctionSpec specify all the information, except the function's 16 // address and arguments, needed to invoke the contract function. 17 type ContractFunctionSpec struct { 18 AddressFromChain func(flow.Chain) flow.Address 19 LocationName string 20 FunctionName string 21 ArgumentTypes []sema.Type 22 } 23 24 // SystemContracts provides methods for invoking system contract functions as 25 // service account. 26 type SystemContracts struct { 27 chain flow.Chain 28 29 tracer tracing.TracerSpan 30 logger *ProgramLogger 31 runtime *Runtime 32 } 33 34 func NewSystemContracts( 35 chain flow.Chain, 36 tracer tracing.TracerSpan, 37 logger *ProgramLogger, 38 runtime *Runtime, 39 ) *SystemContracts { 40 return &SystemContracts{ 41 chain: chain, 42 tracer: tracer, 43 logger: logger, 44 runtime: runtime, 45 } 46 } 47 48 func (sys *SystemContracts) Invoke( 49 spec ContractFunctionSpec, 50 arguments []cadence.Value, 51 ) ( 52 cadence.Value, 53 error, 54 ) { 55 contractLocation := common.AddressLocation{ 56 Address: common.MustBytesToAddress( 57 spec.AddressFromChain(sys.chain).Bytes()), 58 Name: spec.LocationName, 59 } 60 61 span := sys.tracer.StartChildSpan(trace.FVMInvokeContractFunction) 62 span.SetAttributes( 63 attribute.String( 64 "transaction.ContractFunctionCall", 65 contractLocation.String()+"."+spec.FunctionName)) 66 defer span.End() 67 68 runtime := sys.runtime.BorrowCadenceRuntime() 69 defer sys.runtime.ReturnCadenceRuntime(runtime) 70 71 value, err := runtime.InvokeContractFunction( 72 contractLocation, 73 spec.FunctionName, 74 arguments, 75 spec.ArgumentTypes, 76 ) 77 if err != nil { 78 log := sys.logger.Logger() 79 log.Info(). 80 Err(err). 81 Str("contract", contractLocation.String()). 82 Str("function", spec.FunctionName). 83 Msg("Contract function call executed with error") 84 } 85 return value, err 86 } 87 88 func FlowFeesAddress(chain flow.Chain) flow.Address { 89 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 90 return sc.FlowFees.Address 91 } 92 93 func ServiceAddress(chain flow.Chain) flow.Address { 94 return chain.ServiceAddress() 95 } 96 97 var verifyPayersBalanceForTransactionExecutionSpec = ContractFunctionSpec{ 98 AddressFromChain: FlowFeesAddress, 99 LocationName: systemcontracts.ContractNameFlowFees, 100 FunctionName: systemcontracts.ContractServiceAccountFunction_verifyPayersBalanceForTransactionExecution, 101 ArgumentTypes: []sema.Type{ 102 sema.AuthAccountType, 103 sema.UInt64Type, 104 sema.UInt64Type, 105 }, 106 } 107 108 // CheckPayerBalanceAndGetMaxTxFees executes the verifyPayersBalanceForTransactionExecution 109 // on the FlowFees account. 110 // It checks whether the given payer has enough balance to cover inclusion fee and max execution 111 // fee. 112 // It returns (maxTransactionFee, ErrCodeInsufficientPayerBalance) if the payer doesn't have enough balance 113 // It returns (maxTransactionFee, nil) if the payer has enough balance 114 func (sys *SystemContracts) CheckPayerBalanceAndGetMaxTxFees( 115 payer flow.Address, 116 inclusionEffort uint64, 117 maxExecutionEffort uint64, 118 ) (cadence.Value, error) { 119 return sys.Invoke( 120 verifyPayersBalanceForTransactionExecutionSpec, 121 []cadence.Value{ 122 cadence.BytesToAddress(payer.Bytes()), 123 cadence.UFix64(inclusionEffort), 124 cadence.UFix64(maxExecutionEffort), 125 }, 126 ) 127 } 128 129 var deductTransactionFeeSpec = ContractFunctionSpec{ 130 AddressFromChain: FlowFeesAddress, 131 LocationName: systemcontracts.ContractNameFlowFees, 132 FunctionName: systemcontracts.ContractServiceAccountFunction_deductTransactionFee, 133 ArgumentTypes: []sema.Type{ 134 sema.AuthAccountType, 135 sema.UInt64Type, 136 sema.UInt64Type, 137 }, 138 } 139 140 // DeductTransactionFees executes the fee deduction function 141 // on the FlowFees account. 142 func (sys *SystemContracts) DeductTransactionFees( 143 payer flow.Address, 144 inclusionEffort uint64, 145 executionEffort uint64, 146 ) (cadence.Value, error) { 147 return sys.Invoke( 148 deductTransactionFeeSpec, 149 []cadence.Value{ 150 cadence.BytesToAddress(payer.Bytes()), 151 cadence.UFix64(inclusionEffort), 152 cadence.UFix64(executionEffort), 153 }, 154 ) 155 } 156 157 // uses `FlowServiceAccount.setupNewAccount` from https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowServiceAccount.cdc 158 var setupNewAccountSpec = ContractFunctionSpec{ 159 AddressFromChain: ServiceAddress, 160 LocationName: systemcontracts.ContractNameServiceAccount, 161 FunctionName: systemcontracts.ContractServiceAccountFunction_setupNewAccount, 162 ArgumentTypes: []sema.Type{ 163 sema.AuthAccountType, 164 sema.AuthAccountType, 165 }, 166 } 167 168 // SetupNewAccount executes the new account setup contract on the service 169 // account. 170 func (sys *SystemContracts) SetupNewAccount( 171 flowAddress flow.Address, 172 payer flow.Address, 173 ) (cadence.Value, error) { 174 return sys.Invoke( 175 setupNewAccountSpec, 176 []cadence.Value{ 177 cadence.BytesToAddress(flowAddress.Bytes()), 178 cadence.BytesToAddress(payer.Bytes()), 179 }, 180 ) 181 } 182 183 var accountAvailableBalanceSpec = ContractFunctionSpec{ 184 AddressFromChain: ServiceAddress, 185 LocationName: systemcontracts.ContractNameStorageFees, 186 FunctionName: systemcontracts.ContractStorageFeesFunction_defaultTokenAvailableBalance, 187 ArgumentTypes: []sema.Type{ 188 &sema.AddressType{}, 189 }, 190 } 191 192 // AccountAvailableBalance executes the get available balance contract on the 193 // storage fees contract. 194 func (sys *SystemContracts) AccountAvailableBalance( 195 address flow.Address, 196 ) (cadence.Value, error) { 197 return sys.Invoke( 198 accountAvailableBalanceSpec, 199 []cadence.Value{ 200 cadence.BytesToAddress(address.Bytes()), 201 }, 202 ) 203 } 204 205 var accountBalanceInvocationSpec = ContractFunctionSpec{ 206 AddressFromChain: ServiceAddress, 207 LocationName: systemcontracts.ContractNameServiceAccount, 208 FunctionName: systemcontracts.ContractServiceAccountFunction_defaultTokenBalance, 209 ArgumentTypes: []sema.Type{ 210 sema.PublicAccountType, 211 }, 212 } 213 214 // AccountBalance executes the get available balance contract on the service 215 // account. 216 func (sys *SystemContracts) AccountBalance( 217 address flow.Address, 218 ) (cadence.Value, error) { 219 return sys.Invoke( 220 accountBalanceInvocationSpec, 221 []cadence.Value{ 222 cadence.BytesToAddress(address.Bytes()), 223 }, 224 ) 225 } 226 227 var accountStorageCapacitySpec = ContractFunctionSpec{ 228 AddressFromChain: ServiceAddress, 229 LocationName: systemcontracts.ContractNameStorageFees, 230 FunctionName: systemcontracts.ContractStorageFeesFunction_calculateAccountCapacity, 231 ArgumentTypes: []sema.Type{ 232 &sema.AddressType{}, 233 }, 234 } 235 236 // AccountStorageCapacity executes the get storage capacity contract on the 237 // service account. 238 func (sys *SystemContracts) AccountStorageCapacity( 239 address flow.Address, 240 ) (cadence.Value, error) { 241 return sys.Invoke( 242 accountStorageCapacitySpec, 243 []cadence.Value{ 244 cadence.BytesToAddress(address.Bytes()), 245 }, 246 ) 247 } 248 249 // AccountsStorageCapacity gets storage capacity for multiple accounts at once. 250 func (sys *SystemContracts) AccountsStorageCapacity( 251 addresses []flow.Address, 252 payer flow.Address, 253 maxTxFees uint64, 254 ) (cadence.Value, error) { 255 arrayValues := make([]cadence.Value, len(addresses)) 256 for i, address := range addresses { 257 arrayValues[i] = cadence.BytesToAddress(address.Bytes()) 258 } 259 260 return sys.Invoke( 261 ContractFunctionSpec{ 262 AddressFromChain: ServiceAddress, 263 LocationName: systemcontracts.ContractNameStorageFees, 264 FunctionName: systemcontracts.ContractStorageFeesFunction_getAccountsCapacityForTransactionStorageCheck, 265 ArgumentTypes: []sema.Type{ 266 sema.NewConstantSizedType( 267 nil, 268 &sema.AddressType{}, 269 int64(len(arrayValues)), 270 ), 271 &sema.AddressType{}, 272 sema.UFix64Type, 273 }, 274 }, 275 []cadence.Value{ 276 cadence.NewArray(arrayValues), 277 cadence.BytesToAddress(payer.Bytes()), 278 cadence.UFix64(maxTxFees), 279 }, 280 ) 281 }