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  }