github.com/koko1123/flow-go-1@v0.29.6/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/koko1123/flow-go-1/fvm/systemcontracts"
    10  	"github.com/koko1123/flow-go-1/fvm/tracing"
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/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.Address(spec.AddressFromChain(sys.chain)),
    57  		Name:    spec.LocationName,
    58  	}
    59  
    60  	span := sys.tracer.StartChildSpan(trace.FVMInvokeContractFunction)
    61  	span.SetAttributes(
    62  		attribute.String(
    63  			"transaction.ContractFunctionCall",
    64  			contractLocation.String()+"."+spec.FunctionName))
    65  	defer span.End()
    66  
    67  	runtime := sys.runtime.BorrowCadenceRuntime()
    68  	defer sys.runtime.ReturnCadenceRuntime(runtime)
    69  
    70  	value, err := runtime.InvokeContractFunction(
    71  		contractLocation,
    72  		spec.FunctionName,
    73  		arguments,
    74  		spec.ArgumentTypes,
    75  	)
    76  	if err != nil {
    77  		sys.logger.Logger().
    78  			Info().
    79  			Err(err).
    80  			Str("contract", contractLocation.String()).
    81  			Str("function", spec.FunctionName).
    82  			Msg("Contract function call executed with error")
    83  	}
    84  	return value, err
    85  }
    86  
    87  func FlowFeesAddress(chain flow.Chain) flow.Address {
    88  	address, _ := chain.AddressAtIndex(FlowFeesAccountIndex)
    89  	return address
    90  }
    91  
    92  func ServiceAddress(chain flow.Chain) flow.Address {
    93  	return chain.ServiceAddress()
    94  }
    95  
    96  var verifyPayersBalanceForTransactionExecutionSpec = ContractFunctionSpec{
    97  	AddressFromChain: FlowFeesAddress,
    98  	LocationName:     systemcontracts.ContractNameFlowFees,
    99  	FunctionName:     systemcontracts.ContractServiceAccountFunction_verifyPayersBalanceForTransactionExecution,
   100  	ArgumentTypes: []sema.Type{
   101  		sema.AuthAccountType,
   102  		sema.UInt64Type,
   103  		sema.UInt64Type,
   104  	},
   105  }
   106  
   107  // CheckPayerBalanceAndGetMaxTxFees executes the verifyPayersBalanceForTransactionExecution
   108  // on the FlowFees account.
   109  // It checks whether the given payer has enough balance to cover inclusion fee and max execution
   110  // fee.
   111  // It returns (maxTransactionFee, ErrCodeInsufficientPayerBalance) if the payer doesn't have enough balance
   112  // It returns (maxTransactionFee, nil) if the payer has enough balance
   113  func (sys *SystemContracts) CheckPayerBalanceAndGetMaxTxFees(
   114  	payer flow.Address,
   115  	inclusionEffort uint64,
   116  	maxExecutionEffort uint64,
   117  ) (cadence.Value, error) {
   118  	return sys.Invoke(
   119  		verifyPayersBalanceForTransactionExecutionSpec,
   120  		[]cadence.Value{
   121  			cadence.BytesToAddress(payer.Bytes()),
   122  			cadence.UFix64(inclusionEffort),
   123  			cadence.UFix64(maxExecutionEffort),
   124  		},
   125  	)
   126  }
   127  
   128  var deductTransactionFeeSpec = ContractFunctionSpec{
   129  	AddressFromChain: FlowFeesAddress,
   130  	LocationName:     systemcontracts.ContractNameFlowFees,
   131  	FunctionName:     systemcontracts.ContractServiceAccountFunction_deductTransactionFee,
   132  	ArgumentTypes: []sema.Type{
   133  		sema.AuthAccountType,
   134  		sema.UInt64Type,
   135  		sema.UInt64Type,
   136  	},
   137  }
   138  
   139  // DeductTransactionFees executes the fee deduction function
   140  // on the FlowFees account.
   141  func (sys *SystemContracts) DeductTransactionFees(
   142  	payer flow.Address,
   143  	inclusionEffort uint64,
   144  	executionEffort uint64,
   145  ) (cadence.Value, error) {
   146  	return sys.Invoke(
   147  		deductTransactionFeeSpec,
   148  		[]cadence.Value{
   149  			cadence.BytesToAddress(payer.Bytes()),
   150  			cadence.UFix64(inclusionEffort),
   151  			cadence.UFix64(executionEffort),
   152  		},
   153  	)
   154  }
   155  
   156  // uses `FlowServiceAccount.setupNewAccount` from https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowServiceAccount.cdc
   157  var setupNewAccountSpec = ContractFunctionSpec{
   158  	AddressFromChain: ServiceAddress,
   159  	LocationName:     systemcontracts.ContractServiceAccount,
   160  	FunctionName:     systemcontracts.ContractServiceAccountFunction_setupNewAccount,
   161  	ArgumentTypes: []sema.Type{
   162  		sema.AuthAccountType,
   163  		sema.AuthAccountType,
   164  	},
   165  }
   166  
   167  // SetupNewAccount executes the new account setup contract on the service
   168  // account.
   169  func (sys *SystemContracts) SetupNewAccount(
   170  	flowAddress flow.Address,
   171  	payer common.Address,
   172  ) (cadence.Value, error) {
   173  	return sys.Invoke(
   174  		setupNewAccountSpec,
   175  		[]cadence.Value{
   176  			cadence.BytesToAddress(flowAddress.Bytes()),
   177  			cadence.BytesToAddress(payer.Bytes()),
   178  		},
   179  	)
   180  }
   181  
   182  var accountAvailableBalanceSpec = ContractFunctionSpec{
   183  	AddressFromChain: ServiceAddress,
   184  	LocationName:     systemcontracts.ContractStorageFees,
   185  	FunctionName:     systemcontracts.ContractStorageFeesFunction_defaultTokenAvailableBalance,
   186  	ArgumentTypes: []sema.Type{
   187  		&sema.AddressType{},
   188  	},
   189  }
   190  
   191  // AccountAvailableBalance executes the get available balance contract on the
   192  // storage fees contract.
   193  func (sys *SystemContracts) AccountAvailableBalance(
   194  	address common.Address,
   195  ) (cadence.Value, error) {
   196  	return sys.Invoke(
   197  		accountAvailableBalanceSpec,
   198  		[]cadence.Value{
   199  			cadence.BytesToAddress(address.Bytes()),
   200  		},
   201  	)
   202  }
   203  
   204  var accountBalanceInvocationSpec = ContractFunctionSpec{
   205  	AddressFromChain: ServiceAddress,
   206  	LocationName:     systemcontracts.ContractServiceAccount,
   207  	FunctionName:     systemcontracts.ContractServiceAccountFunction_defaultTokenBalance,
   208  	ArgumentTypes: []sema.Type{
   209  		sema.PublicAccountType,
   210  	},
   211  }
   212  
   213  // AccountBalance executes the get available balance contract on the service
   214  // account.
   215  func (sys *SystemContracts) AccountBalance(
   216  	address common.Address,
   217  ) (cadence.Value, error) {
   218  	return sys.Invoke(
   219  		accountBalanceInvocationSpec,
   220  		[]cadence.Value{
   221  			cadence.BytesToAddress(address.Bytes()),
   222  		},
   223  	)
   224  }
   225  
   226  var accountStorageCapacitySpec = ContractFunctionSpec{
   227  	AddressFromChain: ServiceAddress,
   228  	LocationName:     systemcontracts.ContractStorageFees,
   229  	FunctionName:     systemcontracts.ContractStorageFeesFunction_calculateAccountCapacity,
   230  	ArgumentTypes: []sema.Type{
   231  		&sema.AddressType{},
   232  	},
   233  }
   234  
   235  // AccountStorageCapacity executes the get storage capacity contract on the
   236  // service account.
   237  func (sys *SystemContracts) AccountStorageCapacity(
   238  	address common.Address,
   239  ) (cadence.Value, error) {
   240  	return sys.Invoke(
   241  		accountStorageCapacitySpec,
   242  		[]cadence.Value{
   243  			cadence.BytesToAddress(address.Bytes()),
   244  		},
   245  	)
   246  }
   247  
   248  // AccountsStorageCapacity gets storage capacity for multiple accounts at once.
   249  func (sys *SystemContracts) AccountsStorageCapacity(
   250  	addresses []common.Address,
   251  	payer common.Address,
   252  	maxTxFees uint64,
   253  ) (cadence.Value, error) {
   254  	arrayValues := make([]cadence.Value, len(addresses))
   255  	for i, address := range addresses {
   256  		arrayValues[i] = cadence.BytesToAddress(address.Bytes())
   257  	}
   258  
   259  	return sys.Invoke(
   260  		ContractFunctionSpec{
   261  			AddressFromChain: ServiceAddress,
   262  			LocationName:     systemcontracts.ContractStorageFees,
   263  			FunctionName:     systemcontracts.ContractStorageFeesFunction_getAccountsCapacityForTransactionStorageCheck,
   264  			ArgumentTypes: []sema.Type{
   265  				sema.NewConstantSizedType(
   266  					nil,
   267  					&sema.AddressType{},
   268  					int64(len(arrayValues)),
   269  				),
   270  				&sema.AddressType{},
   271  				sema.UFix64Type,
   272  			},
   273  		},
   274  		[]cadence.Value{
   275  			cadence.NewArray(arrayValues),
   276  			cadence.BytesToAddress(payer.Bytes()),
   277  			cadence.UFix64(maxTxFees),
   278  		},
   279  	)
   280  }
   281  
   282  var useContractAuditVoucherSpec = ContractFunctionSpec{
   283  	AddressFromChain: ServiceAddress,
   284  	LocationName:     systemcontracts.ContractDeploymentAudits,
   285  	FunctionName:     systemcontracts.ContractDeploymentAuditsFunction_useVoucherForDeploy,
   286  	ArgumentTypes: []sema.Type{
   287  		&sema.AddressType{},
   288  		sema.StringType,
   289  	},
   290  }
   291  
   292  // UseContractAuditVoucher executes the use a contract deployment audit voucher
   293  // contract.
   294  func (sys *SystemContracts) UseContractAuditVoucher(
   295  	address common.Address,
   296  	code string,
   297  ) (bool, error) {
   298  	resultCdc, err := sys.Invoke(
   299  		useContractAuditVoucherSpec,
   300  		[]cadence.Value{
   301  			cadence.BytesToAddress(address.Bytes()),
   302  			cadence.String(code),
   303  		},
   304  	)
   305  	if err != nil {
   306  		return false, err
   307  	}
   308  	result := resultCdc.(cadence.Bool).ToGoValue().(bool)
   309  	return result, nil
   310  }