github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/environment/account_key_reader.go (about)

     1  package environment
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/cadence/runtime"
     7  	"github.com/onflow/cadence/runtime/common"
     8  
     9  	"github.com/onflow/flow-go/fvm/crypto"
    10  	"github.com/onflow/flow-go/fvm/errors"
    11  	"github.com/onflow/flow-go/fvm/storage/state"
    12  	"github.com/onflow/flow-go/fvm/tracing"
    13  	"github.com/onflow/flow-go/model/flow"
    14  	"github.com/onflow/flow-go/module/trace"
    15  )
    16  
    17  // AccountKeyReader provide read access to account keys.
    18  type AccountKeyReader interface {
    19  	// GetAccountKey retrieves a public key by index from an existing account.
    20  	//
    21  	// This function returns a nil key with no errors, if a key doesn't exist at
    22  	// the given index. An error is returned if the specified account does not
    23  	// exist, the provided index is not valid, or if the key retrieval fails.
    24  	GetAccountKey(
    25  		runtimeAddress common.Address,
    26  		keyIndex int,
    27  	) (
    28  		*runtime.AccountKey,
    29  		error,
    30  	)
    31  	AccountKeysCount(runtimeAddress common.Address) (uint64, error)
    32  }
    33  
    34  type ParseRestrictedAccountKeyReader struct {
    35  	txnState state.NestedTransactionPreparer
    36  	impl     AccountKeyReader
    37  }
    38  
    39  func NewParseRestrictedAccountKeyReader(
    40  	txnState state.NestedTransactionPreparer,
    41  	impl AccountKeyReader,
    42  ) AccountKeyReader {
    43  	return ParseRestrictedAccountKeyReader{
    44  		txnState: txnState,
    45  		impl:     impl,
    46  	}
    47  }
    48  
    49  func (reader ParseRestrictedAccountKeyReader) GetAccountKey(
    50  	runtimeAddress common.Address,
    51  	keyIndex int,
    52  ) (
    53  	*runtime.AccountKey,
    54  	error,
    55  ) {
    56  	return parseRestrict2Arg1Ret(
    57  		reader.txnState,
    58  		trace.FVMEnvGetAccountKey,
    59  		reader.impl.GetAccountKey,
    60  		runtimeAddress,
    61  		keyIndex)
    62  }
    63  
    64  func (reader ParseRestrictedAccountKeyReader) AccountKeysCount(
    65  	runtimeAddress common.Address,
    66  ) (
    67  	uint64,
    68  	error,
    69  ) {
    70  	return parseRestrict1Arg1Ret(
    71  		reader.txnState,
    72  		"AccountKeysCount",
    73  		reader.impl.AccountKeysCount,
    74  		runtimeAddress,
    75  	)
    76  }
    77  
    78  type accountKeyReader struct {
    79  	tracer tracing.TracerSpan
    80  	meter  Meter
    81  
    82  	accounts Accounts
    83  }
    84  
    85  func NewAccountKeyReader(
    86  	tracer tracing.TracerSpan,
    87  	meter Meter,
    88  	accounts Accounts,
    89  ) AccountKeyReader {
    90  	return &accountKeyReader{
    91  		tracer:   tracer,
    92  		meter:    meter,
    93  		accounts: accounts,
    94  	}
    95  }
    96  
    97  func (reader *accountKeyReader) GetAccountKey(
    98  	runtimeAddress common.Address,
    99  	keyIndex int,
   100  ) (
   101  	*runtime.AccountKey,
   102  	error,
   103  ) {
   104  	defer reader.tracer.StartChildSpan(trace.FVMEnvGetAccountKey).End()
   105  
   106  	formatErr := func(err error) (*runtime.AccountKey, error) {
   107  		return nil, fmt.Errorf("getting account key failed: %w", err)
   108  	}
   109  
   110  	err := reader.meter.MeterComputation(ComputationKindGetAccountKey, 1)
   111  	if err != nil {
   112  		return formatErr(err)
   113  	}
   114  
   115  	// Don't return an error for invalid key indices
   116  	if keyIndex < 0 {
   117  		return nil, nil
   118  	}
   119  
   120  	address := flow.ConvertAddress(runtimeAddress)
   121  
   122  	// address verification is also done in this step
   123  	accountPublicKey, err := reader.accounts.GetPublicKey(
   124  		address,
   125  		uint64(keyIndex))
   126  	if err != nil {
   127  		// If a key is not found at a given index, then return a nil key with
   128  		// no errors.  This is to be inline with the Cadence runtime. Otherwise,
   129  		// Cadence runtime cannot distinguish between a 'key not found error'
   130  		// vs other internal errors.
   131  		if errors.IsAccountPublicKeyNotFoundError(err) {
   132  			return nil, nil
   133  		}
   134  
   135  		return formatErr(err)
   136  	}
   137  
   138  	// Prepare the account key to return
   139  	runtimeAccountKey, err := FlowToRuntimeAccountKey(accountPublicKey)
   140  	if err != nil {
   141  		return formatErr(err)
   142  	}
   143  
   144  	return runtimeAccountKey, nil
   145  }
   146  
   147  func (reader *accountKeyReader) AccountKeysCount(
   148  	runtimeAddress common.Address,
   149  ) (
   150  	uint64,
   151  	error,
   152  ) {
   153  	defer reader.tracer.StartChildSpan(trace.FVMEnvAccountKeysCount).End()
   154  
   155  	formatErr := func(err error) (uint64, error) {
   156  		return 0, fmt.Errorf("fetching account key count failed: %w", err)
   157  	}
   158  
   159  	err := reader.meter.MeterComputation(ComputationKindAccountKeysCount, 1)
   160  	if err != nil {
   161  		return formatErr(err)
   162  	}
   163  
   164  	// address verification is also done in this step
   165  	return reader.accounts.GetPublicKeyCount(
   166  		flow.ConvertAddress(runtimeAddress))
   167  }
   168  
   169  func FlowToRuntimeAccountKey(
   170  	flowKey flow.AccountPublicKey,
   171  ) (
   172  	*runtime.AccountKey,
   173  	error,
   174  ) {
   175  	signAlgo := crypto.CryptoToRuntimeSigningAlgorithm(flowKey.SignAlgo)
   176  	if signAlgo == runtime.SignatureAlgorithmUnknown {
   177  		return nil, errors.NewValueErrorf(
   178  			flowKey.SignAlgo.String(),
   179  			"signature algorithm type not found",
   180  		)
   181  	}
   182  
   183  	hashAlgo := crypto.CryptoToRuntimeHashingAlgorithm(flowKey.HashAlgo)
   184  	if hashAlgo == runtime.HashAlgorithmUnknown {
   185  		return nil, errors.NewValueErrorf(
   186  			flowKey.HashAlgo.String(),
   187  			"hashing algorithm type not found",
   188  		)
   189  	}
   190  
   191  	publicKey := &runtime.PublicKey{
   192  		PublicKey: flowKey.PublicKey.Encode(),
   193  		SignAlgo:  signAlgo,
   194  	}
   195  
   196  	return &runtime.AccountKey{
   197  		KeyIndex:  flowKey.Index,
   198  		PublicKey: publicKey,
   199  		HashAlgo:  hashAlgo,
   200  		Weight:    flowKey.Weight,
   201  		IsRevoked: flowKey.Revoked,
   202  	}, nil
   203  }