github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/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 // SystemContracts provides methods for invoking system contract functions as 16 // service account. 17 type SystemContracts struct { 18 chain flow.Chain 19 20 tracer tracing.TracerSpan 21 logger *ProgramLogger 22 runtime *Runtime 23 } 24 25 func NewSystemContracts( 26 chain flow.Chain, 27 tracer tracing.TracerSpan, 28 logger *ProgramLogger, 29 runtime *Runtime, 30 ) *SystemContracts { 31 return &SystemContracts{ 32 chain: chain, 33 tracer: tracer, 34 logger: logger, 35 runtime: runtime, 36 } 37 } 38 39 func (sys *SystemContracts) Invoke( 40 spec ContractFunctionSpec, 41 arguments []cadence.Value, 42 ) ( 43 cadence.Value, 44 error, 45 ) { 46 contractLocation := common.AddressLocation{ 47 Address: common.MustBytesToAddress( 48 spec.AddressFromChain(sys.chain).Bytes()), 49 Name: spec.LocationName, 50 } 51 52 span := sys.tracer.StartChildSpan(trace.FVMInvokeContractFunction) 53 span.SetAttributes( 54 attribute.String( 55 "transaction.ContractFunctionCall", 56 contractLocation.String()+"."+spec.FunctionName)) 57 defer span.End() 58 59 runtime := sys.runtime.BorrowCadenceRuntime() 60 defer sys.runtime.ReturnCadenceRuntime(runtime) 61 62 value, err := runtime.InvokeContractFunction( 63 contractLocation, 64 spec.FunctionName, 65 arguments, 66 spec.ArgumentTypes, 67 ) 68 if err != nil { 69 log := sys.logger.Logger() 70 log.Info(). 71 Err(err). 72 Str("contract", contractLocation.String()). 73 Str("function", spec.FunctionName). 74 Msg("Contract function call executed with error") 75 } 76 return value, err 77 } 78 79 func FlowFeesAddress(chain flow.Chain) flow.Address { 80 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 81 return sc.FlowFees.Address 82 } 83 84 func ServiceAddress(chain flow.Chain) flow.Address { 85 return chain.ServiceAddress() 86 } 87 88 var verifyPayersBalanceForTransactionExecutionSpec = ContractFunctionSpec{ 89 AddressFromChain: FlowFeesAddress, 90 LocationName: systemcontracts.ContractNameFlowFees, 91 FunctionName: systemcontracts.ContractServiceAccountFunction_verifyPayersBalanceForTransactionExecution, 92 ArgumentTypes: []sema.Type{ 93 sema.NewReferenceType( 94 nil, 95 sema.NewEntitlementSetAccess( 96 []*sema.EntitlementType{ 97 sema.BorrowValueType, 98 }, 99 sema.Conjunction, 100 ), 101 sema.AccountType, 102 ), 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.NewReferenceType( 135 nil, 136 sema.NewEntitlementSetAccess( 137 []*sema.EntitlementType{ 138 sema.BorrowValueType, 139 }, 140 sema.Conjunction, 141 ), 142 sema.AccountType, 143 ), 144 sema.UInt64Type, 145 sema.UInt64Type, 146 }, 147 } 148 149 // DeductTransactionFees executes the fee deduction function 150 // on the FlowFees account. 151 func (sys *SystemContracts) DeductTransactionFees( 152 payer flow.Address, 153 inclusionEffort uint64, 154 executionEffort uint64, 155 ) (cadence.Value, error) { 156 return sys.Invoke( 157 deductTransactionFeeSpec, 158 []cadence.Value{ 159 cadence.BytesToAddress(payer.Bytes()), 160 cadence.UFix64(inclusionEffort), 161 cadence.UFix64(executionEffort), 162 }, 163 ) 164 } 165 166 // uses `FlowServiceAccount.setupNewAccount` from https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowServiceAccount.cdc 167 var setupNewAccountSpec = ContractFunctionSpec{ 168 AddressFromChain: ServiceAddress, 169 LocationName: systemcontracts.ContractNameServiceAccount, 170 FunctionName: systemcontracts.ContractServiceAccountFunction_setupNewAccount, 171 ArgumentTypes: []sema.Type{ 172 sema.NewReferenceType( 173 nil, 174 sema.NewEntitlementSetAccess( 175 []*sema.EntitlementType{ 176 sema.SaveValueType, 177 sema.BorrowValueType, 178 sema.CapabilitiesType, 179 }, 180 sema.Conjunction, 181 ), 182 sema.AccountType, 183 ), 184 sema.NewReferenceType( 185 nil, 186 sema.NewEntitlementSetAccess( 187 []*sema.EntitlementType{ 188 sema.BorrowValueType, 189 }, 190 sema.Conjunction, 191 ), 192 sema.AccountType, 193 ), 194 }, 195 } 196 197 // SetupNewAccount executes the new account setup contract on the service 198 // account. 199 func (sys *SystemContracts) SetupNewAccount( 200 flowAddress flow.Address, 201 payer flow.Address, 202 ) (cadence.Value, error) { 203 return sys.Invoke( 204 setupNewAccountSpec, 205 []cadence.Value{ 206 cadence.BytesToAddress(flowAddress.Bytes()), 207 cadence.BytesToAddress(payer.Bytes()), 208 }, 209 ) 210 } 211 212 var accountAvailableBalanceSpec = ContractFunctionSpec{ 213 AddressFromChain: ServiceAddress, 214 LocationName: systemcontracts.ContractNameStorageFees, 215 FunctionName: systemcontracts.ContractStorageFeesFunction_defaultTokenAvailableBalance, 216 ArgumentTypes: []sema.Type{ 217 &sema.AddressType{}, 218 }, 219 } 220 221 // AccountAvailableBalance executes the get available balance contract on the 222 // storage fees contract. 223 func (sys *SystemContracts) AccountAvailableBalance( 224 address flow.Address, 225 ) (cadence.Value, error) { 226 return sys.Invoke( 227 accountAvailableBalanceSpec, 228 []cadence.Value{ 229 cadence.BytesToAddress(address.Bytes()), 230 }, 231 ) 232 } 233 234 var accountBalanceInvocationSpec = ContractFunctionSpec{ 235 AddressFromChain: ServiceAddress, 236 LocationName: systemcontracts.ContractNameServiceAccount, 237 FunctionName: systemcontracts.ContractServiceAccountFunction_defaultTokenBalance, 238 ArgumentTypes: []sema.Type{ 239 sema.NewReferenceType( 240 nil, 241 sema.UnauthorizedAccess, 242 sema.AccountType, 243 ), 244 }, 245 } 246 247 // AccountBalance executes the get available balance contract on the service 248 // account. 249 func (sys *SystemContracts) AccountBalance( 250 address flow.Address, 251 ) (cadence.Value, error) { 252 return sys.Invoke( 253 accountBalanceInvocationSpec, 254 []cadence.Value{ 255 cadence.BytesToAddress(address.Bytes()), 256 }, 257 ) 258 } 259 260 var accountStorageCapacitySpec = ContractFunctionSpec{ 261 AddressFromChain: ServiceAddress, 262 LocationName: systemcontracts.ContractNameStorageFees, 263 FunctionName: systemcontracts.ContractStorageFeesFunction_calculateAccountCapacity, 264 ArgumentTypes: []sema.Type{ 265 &sema.AddressType{}, 266 }, 267 } 268 269 // AccountStorageCapacity executes the get storage capacity contract on the 270 // service account. 271 func (sys *SystemContracts) AccountStorageCapacity( 272 address flow.Address, 273 ) (cadence.Value, error) { 274 return sys.Invoke( 275 accountStorageCapacitySpec, 276 []cadence.Value{ 277 cadence.BytesToAddress(address.Bytes()), 278 }, 279 ) 280 } 281 282 // AccountsStorageCapacity gets storage capacity for multiple accounts at once. 283 func (sys *SystemContracts) AccountsStorageCapacity( 284 addresses []flow.Address, 285 payer flow.Address, 286 maxTxFees uint64, 287 ) (cadence.Value, error) { 288 arrayValues := make([]cadence.Value, len(addresses)) 289 for i, address := range addresses { 290 arrayValues[i] = cadence.BytesToAddress(address.Bytes()) 291 } 292 293 return sys.Invoke( 294 ContractFunctionSpec{ 295 AddressFromChain: ServiceAddress, 296 LocationName: systemcontracts.ContractNameStorageFees, 297 FunctionName: systemcontracts.ContractStorageFeesFunction_getAccountsCapacityForTransactionStorageCheck, 298 ArgumentTypes: []sema.Type{ 299 sema.NewConstantSizedType( 300 nil, 301 &sema.AddressType{}, 302 int64(len(arrayValues)), 303 ), 304 &sema.AddressType{}, 305 sema.UFix64Type, 306 }, 307 }, 308 []cadence.Value{ 309 cadence.NewArray(arrayValues), 310 cadence.BytesToAddress(payer.Bytes()), 311 cadence.UFix64(maxTxFees), 312 }, 313 ) 314 }