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  }