github.com/koko1123/flow-go-1@v0.29.6/fvm/transactionStorageLimiter.go (about)

     1  package fvm
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/cadence"
     7  	"github.com/onflow/cadence/runtime/common"
     8  
     9  	"github.com/koko1123/flow-go-1/fvm/environment"
    10  	"github.com/koko1123/flow-go-1/fvm/errors"
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/module/trace"
    13  )
    14  
    15  type TransactionStorageLimiter struct{}
    16  
    17  // CheckStorageLimits checks each account that had its storage written to during a transaction, that its storage used
    18  // is less than its storage capacity.
    19  // Storage used is an FVM register and is easily accessible.
    20  // Storage capacity is calculated by the FlowStorageFees contract from the account's flow balance.
    21  //
    22  // The payers balance is considered to be maxTxFees lower that its actual balance, due to the fact that
    23  // the fee deduction step happens after the storage limit check.
    24  func (d TransactionStorageLimiter) CheckStorageLimits(
    25  	env environment.Environment,
    26  	addresses []flow.Address,
    27  	payer flow.Address,
    28  	maxTxFees uint64,
    29  ) error {
    30  	if !env.LimitAccountStorage() {
    31  		return nil
    32  	}
    33  
    34  	defer env.StartChildSpan(trace.FVMTransactionStorageUsedCheck).End()
    35  
    36  	err := d.checkStorageLimits(env, addresses, payer, maxTxFees)
    37  	if err != nil {
    38  		return fmt.Errorf("storage limit check failed: %w", err)
    39  	}
    40  	return nil
    41  }
    42  
    43  func (d TransactionStorageLimiter) checkStorageLimits(
    44  	env environment.Environment,
    45  	addresses []flow.Address,
    46  	payer flow.Address,
    47  	maxTxFees uint64,
    48  ) error {
    49  	// in case the payer is not already part of the check, include it here.
    50  	// If the maxTxFees is zero, it doesn't matter if the payer is included or not.
    51  	if maxTxFees > 0 {
    52  		commonAddressesContainPayer := false
    53  		for _, address := range addresses {
    54  			if address == payer {
    55  				commonAddressesContainPayer = true
    56  				break
    57  			}
    58  		}
    59  
    60  		if !commonAddressesContainPayer {
    61  			addresses = append(addresses, payer)
    62  		}
    63  	}
    64  
    65  	commonAddresses := make([]common.Address, len(addresses))
    66  	usages := make([]uint64, len(commonAddresses))
    67  
    68  	for i, address := range addresses {
    69  		ca := common.Address(address)
    70  		u, err := env.GetStorageUsed(ca)
    71  		if err != nil {
    72  			return err
    73  		}
    74  
    75  		commonAddresses[i] = ca
    76  		usages[i] = u
    77  	}
    78  
    79  	result, invokeErr := env.AccountsStorageCapacity(
    80  		commonAddresses,
    81  		common.Address(payer),
    82  		maxTxFees,
    83  	)
    84  
    85  	// This error only occurs in case of implementation errors. The InvokeAccountsStorageCapacity
    86  	// already handles cases where the default vault is missing.
    87  	if invokeErr != nil {
    88  		return invokeErr
    89  	}
    90  
    91  	// the resultArray elements are in the same order as the addresses and the addresses are deterministically sorted
    92  	resultArray, ok := result.(cadence.Array)
    93  	if !ok {
    94  		return fmt.Errorf("AccountsStorageCapacity did not return an array")
    95  	}
    96  
    97  	for i, value := range resultArray.Values {
    98  		capacity := environment.StorageMBUFixToBytesUInt(value)
    99  
   100  		if usages[i] > capacity {
   101  			return errors.NewStorageCapacityExceededError(flow.BytesToAddress(addresses[i].Bytes()), usages[i], capacity)
   102  		}
   103  	}
   104  
   105  	return nil
   106  }