github.com/onflow/flow-go@v0.33.17/fvm/environment/account_key_updater.go (about)

     1  package environment
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  
     7  	"github.com/onflow/cadence/runtime"
     8  	"github.com/onflow/cadence/runtime/common"
     9  	"github.com/onflow/cadence/runtime/sema"
    10  
    11  	fgcrypto "github.com/onflow/flow-go/crypto"
    12  	fghash "github.com/onflow/flow-go/crypto/hash"
    13  	"github.com/onflow/flow-go/fvm/crypto"
    14  	"github.com/onflow/flow-go/fvm/errors"
    15  	"github.com/onflow/flow-go/fvm/storage/state"
    16  	"github.com/onflow/flow-go/fvm/tracing"
    17  	"github.com/onflow/flow-go/model/flow"
    18  	"github.com/onflow/flow-go/module/trace"
    19  )
    20  
    21  // NewAccountPublicKey construct an account public key given a runtime
    22  // public key.
    23  func NewAccountPublicKey(publicKey *runtime.PublicKey,
    24  	hashAlgo sema.HashAlgorithm,
    25  	keyIndex int,
    26  	weight int,
    27  ) (
    28  	*flow.AccountPublicKey,
    29  	error,
    30  ) {
    31  
    32  	var err error
    33  	signAlgorithm := crypto.RuntimeToCryptoSigningAlgorithm(publicKey.SignAlgo)
    34  	if signAlgorithm != fgcrypto.ECDSAP256 &&
    35  		signAlgorithm != fgcrypto.ECDSASecp256k1 {
    36  
    37  		return nil, fmt.Errorf(
    38  			"adding account key failed: %w",
    39  			errors.NewValueErrorf(
    40  				fmt.Sprintf("%d", publicKey.SignAlgo),
    41  				"signature algorithm type not supported"))
    42  	}
    43  
    44  	hashAlgorithm := crypto.RuntimeToCryptoHashingAlgorithm(hashAlgo)
    45  	if hashAlgorithm != fghash.SHA2_256 &&
    46  		hashAlgorithm != fghash.SHA3_256 {
    47  
    48  		return nil, fmt.Errorf(
    49  			"adding account key failed: %w",
    50  			errors.NewValueErrorf(
    51  				fmt.Sprintf("%d", hashAlgo),
    52  				"hashing algorithm type not supported"))
    53  	}
    54  
    55  	decodedPublicKey, err := fgcrypto.DecodePublicKey(
    56  		signAlgorithm,
    57  		publicKey.PublicKey)
    58  	if err != nil {
    59  		return nil, fmt.Errorf(
    60  			"adding account key failed: %w",
    61  			errors.NewValueErrorf(
    62  				hex.EncodeToString(publicKey.PublicKey),
    63  				"cannot decode public key: %w",
    64  				err))
    65  	}
    66  
    67  	return &flow.AccountPublicKey{
    68  		Index:     keyIndex,
    69  		PublicKey: decodedPublicKey,
    70  		SignAlgo:  signAlgorithm,
    71  		HashAlgo:  hashAlgorithm,
    72  		SeqNumber: 0,
    73  		Weight:    weight,
    74  		Revoked:   false,
    75  	}, nil
    76  }
    77  
    78  // AccountKeyUpdater handles all account keys modification.
    79  //
    80  // Note that scripts cannot modify account keys, but must expose the API in
    81  // compliance with the runtime environment interface.
    82  type AccountKeyUpdater interface {
    83  	// AddEncodedAccountKey adds an encoded public key to an existing account.
    84  	//
    85  	// This function returns an error if the specified account does not exist or
    86  	// if the key insertion fails.
    87  	//
    88  	// Note that the script variant will return OperationNotSupportedError.
    89  	AddEncodedAccountKey(runtimeAddress common.Address, publicKey []byte) error
    90  
    91  	// RevokeEncodedAccountKey revokes a public key by index from an existing
    92  	// account.
    93  	//
    94  	// This function returns an error if the specified account does not exist,
    95  	// the provided key is invalid, or if key revoking fails.
    96  	//
    97  	// Note that the script variant will return OperationNotSupportedError.
    98  	RevokeEncodedAccountKey(
    99  		runtimeAddress common.Address,
   100  		index int,
   101  	) (
   102  		[]byte,
   103  		error,
   104  	)
   105  
   106  	// AddAccountKey adds a public key to an existing account.
   107  	//
   108  	// This function returns an error if the specified account does not exist or
   109  	// if the key insertion fails.
   110  	//
   111  	// Note that the script variant will return OperationNotSupportedError.
   112  	AddAccountKey(
   113  		runtimeAddress common.Address,
   114  		publicKey *runtime.PublicKey,
   115  		hashAlgo runtime.HashAlgorithm,
   116  		weight int,
   117  	) (
   118  		*runtime.AccountKey,
   119  		error,
   120  	)
   121  
   122  	// RevokeAccountKey revokes a public key by index from an existing account,
   123  	// and returns the revoked key.
   124  	//
   125  	// This function returns a nil key with no errors, if a key doesn't exist
   126  	// at the given index.  An error is returned if the specified account does
   127  	// not exist, the provided index is not valid, or if the key revoking
   128  	// fails.
   129  	//
   130  	// Note that the script variant will return OperationNotSupportedError.
   131  	RevokeAccountKey(
   132  		runtimeAddress common.Address,
   133  		keyIndex int,
   134  	) (
   135  		*runtime.AccountKey,
   136  		error,
   137  	)
   138  }
   139  
   140  type ParseRestrictedAccountKeyUpdater struct {
   141  	txnState state.NestedTransactionPreparer
   142  	impl     AccountKeyUpdater
   143  }
   144  
   145  func NewParseRestrictedAccountKeyUpdater(
   146  	txnState state.NestedTransactionPreparer,
   147  	impl AccountKeyUpdater,
   148  ) ParseRestrictedAccountKeyUpdater {
   149  	return ParseRestrictedAccountKeyUpdater{
   150  		txnState: txnState,
   151  		impl:     impl,
   152  	}
   153  }
   154  
   155  func (updater ParseRestrictedAccountKeyUpdater) AddEncodedAccountKey(
   156  	runtimeAddress common.Address,
   157  	publicKey []byte,
   158  ) error {
   159  	return parseRestrict2Arg(
   160  		updater.txnState,
   161  		trace.FVMEnvAddEncodedAccountKey,
   162  		updater.impl.AddEncodedAccountKey,
   163  		runtimeAddress,
   164  		publicKey)
   165  }
   166  
   167  func (updater ParseRestrictedAccountKeyUpdater) RevokeEncodedAccountKey(
   168  	runtimeAddress common.Address,
   169  	index int,
   170  ) (
   171  	[]byte,
   172  	error,
   173  ) {
   174  	return parseRestrict2Arg1Ret(
   175  		updater.txnState,
   176  		trace.FVMEnvRevokeEncodedAccountKey,
   177  		updater.impl.RevokeEncodedAccountKey,
   178  		runtimeAddress,
   179  		index)
   180  }
   181  
   182  func (updater ParseRestrictedAccountKeyUpdater) AddAccountKey(
   183  	runtimeAddress common.Address,
   184  	publicKey *runtime.PublicKey,
   185  	hashAlgo runtime.HashAlgorithm,
   186  	weight int,
   187  ) (
   188  	*runtime.AccountKey,
   189  	error,
   190  ) {
   191  	return parseRestrict4Arg1Ret(
   192  		updater.txnState,
   193  		trace.FVMEnvAddAccountKey,
   194  		updater.impl.AddAccountKey,
   195  		runtimeAddress,
   196  		publicKey,
   197  		hashAlgo,
   198  		weight)
   199  }
   200  
   201  func (updater ParseRestrictedAccountKeyUpdater) RevokeAccountKey(
   202  	runtimeAddress common.Address,
   203  	keyIndex int,
   204  ) (
   205  	*runtime.AccountKey,
   206  	error,
   207  ) {
   208  	return parseRestrict2Arg1Ret(
   209  		updater.txnState,
   210  		trace.FVMEnvRevokeAccountKey,
   211  		updater.impl.RevokeAccountKey,
   212  		runtimeAddress,
   213  		keyIndex)
   214  }
   215  
   216  type NoAccountKeyUpdater struct{}
   217  
   218  func (NoAccountKeyUpdater) AddEncodedAccountKey(
   219  	runtimeAddress common.Address,
   220  	publicKey []byte,
   221  ) error {
   222  	return errors.NewOperationNotSupportedError("AddEncodedAccountKey")
   223  }
   224  
   225  func (NoAccountKeyUpdater) RevokeEncodedAccountKey(
   226  	runtimeAddress common.Address,
   227  	index int,
   228  ) (
   229  	[]byte,
   230  	error,
   231  ) {
   232  	return nil, errors.NewOperationNotSupportedError("RevokeEncodedAccountKey")
   233  }
   234  
   235  func (NoAccountKeyUpdater) AddAccountKey(
   236  	runtimeAddress common.Address,
   237  	publicKey *runtime.PublicKey,
   238  	hashAlgo runtime.HashAlgorithm,
   239  	weight int,
   240  ) (
   241  	*runtime.AccountKey,
   242  	error,
   243  ) {
   244  	return nil, errors.NewOperationNotSupportedError("AddAccountKey")
   245  }
   246  
   247  func (NoAccountKeyUpdater) RevokeAccountKey(
   248  	runtimeAddress common.Address,
   249  	keyIndex int,
   250  ) (
   251  	*runtime.AccountKey,
   252  	error,
   253  ) {
   254  	return nil, errors.NewOperationNotSupportedError("RevokeAccountKey")
   255  }
   256  
   257  type accountKeyUpdater struct {
   258  	tracer tracing.TracerSpan
   259  	meter  Meter
   260  
   261  	accounts Accounts
   262  	txnState state.NestedTransactionPreparer
   263  	env      Environment
   264  }
   265  
   266  func NewAccountKeyUpdater(
   267  	tracer tracing.TracerSpan,
   268  	meter Meter,
   269  	accounts Accounts,
   270  	txnState state.NestedTransactionPreparer,
   271  	env Environment,
   272  ) *accountKeyUpdater {
   273  	return &accountKeyUpdater{
   274  		tracer:   tracer,
   275  		meter:    meter,
   276  		accounts: accounts,
   277  		txnState: txnState,
   278  		env:      env,
   279  	}
   280  }
   281  
   282  // AddAccountKey adds a public key to an existing account.
   283  //
   284  // This function returns an error if the specified account does not exist or
   285  // if the key insertion fails.
   286  func (updater *accountKeyUpdater) addAccountKey(
   287  	address flow.Address,
   288  	publicKey *runtime.PublicKey,
   289  	hashAlgo runtime.HashAlgorithm,
   290  	weight int,
   291  ) (
   292  	*runtime.AccountKey,
   293  	error,
   294  ) {
   295  	ok, err := updater.accounts.Exists(address)
   296  	if err != nil {
   297  		return nil, fmt.Errorf("adding account key failed: %w", err)
   298  	}
   299  	if !ok {
   300  		return nil, fmt.Errorf(
   301  			"adding account key failed: %w",
   302  			errors.NewAccountNotFoundError(address))
   303  	}
   304  
   305  	keyIndex, err := updater.accounts.GetPublicKeyCount(address)
   306  	if err != nil {
   307  		return nil, fmt.Errorf("adding account key failed: %w", err)
   308  	}
   309  
   310  	accountPublicKey, err := NewAccountPublicKey(
   311  		publicKey,
   312  		hashAlgo,
   313  		int(keyIndex),
   314  		weight)
   315  	if err != nil {
   316  		return nil, fmt.Errorf("adding account key failed: %w", err)
   317  	}
   318  
   319  	err = updater.accounts.AppendPublicKey(address, *accountPublicKey)
   320  	if err != nil {
   321  		return nil, fmt.Errorf("adding account key failed: %w", err)
   322  	}
   323  
   324  	return &runtime.AccountKey{
   325  		KeyIndex:  accountPublicKey.Index,
   326  		PublicKey: publicKey,
   327  		HashAlgo:  hashAlgo,
   328  		Weight:    accountPublicKey.Weight,
   329  		IsRevoked: accountPublicKey.Revoked,
   330  	}, nil
   331  }
   332  
   333  // RevokeAccountKey revokes a public key by index from an existing account,
   334  // and returns the revoked key.
   335  //
   336  // This function returns a nil key with no errors, if a key doesn't exist at
   337  // the given index. An error is returned if the specified account does not
   338  // exist, the provided index is not valid, or if the key revoking fails.
   339  //
   340  // TODO (ramtin) do we have to return runtime.AccountKey for this method or
   341  // can be separated into another method
   342  func (updater *accountKeyUpdater) revokeAccountKey(
   343  	address flow.Address,
   344  	keyIndex int,
   345  ) (
   346  	*runtime.AccountKey,
   347  	error,
   348  ) {
   349  	ok, err := updater.accounts.Exists(address)
   350  	if err != nil {
   351  		return nil, fmt.Errorf("revoking account key failed: %w", err)
   352  	}
   353  
   354  	if !ok {
   355  		return nil, fmt.Errorf(
   356  			"revoking account key failed: %w",
   357  			errors.NewAccountNotFoundError(address))
   358  	}
   359  
   360  	// Don't return an error for invalid key indices
   361  	if keyIndex < 0 {
   362  		return nil, nil
   363  	}
   364  
   365  	var publicKey flow.AccountPublicKey
   366  	publicKey, err = updater.accounts.GetPublicKey(
   367  		address,
   368  		uint64(keyIndex))
   369  	if err != nil {
   370  		// If a key is not found at a given index, then return a nil key with
   371  		// no errors.  This is to be inline with the Cadence runtime. Otherwise
   372  		// Cadence runtime cannot distinguish between a 'key not found error'
   373  		// vs other internal errors.
   374  		if errors.IsAccountPublicKeyNotFoundError(err) {
   375  			return nil, nil
   376  		}
   377  		return nil, fmt.Errorf("revoking account key failed: %w", err)
   378  	}
   379  
   380  	// mark this key as revoked
   381  	publicKey.Revoked = true
   382  
   383  	_, err = updater.accounts.SetPublicKey(
   384  		address,
   385  		uint64(keyIndex),
   386  		publicKey)
   387  	if err != nil {
   388  		return nil, fmt.Errorf("revoking account key failed: %w", err)
   389  	}
   390  
   391  	// Prepare account key to return
   392  	signAlgo := crypto.CryptoToRuntimeSigningAlgorithm(publicKey.SignAlgo)
   393  	if signAlgo == runtime.SignatureAlgorithmUnknown {
   394  		return nil, fmt.Errorf(
   395  			"revoking account key failed: %w",
   396  			errors.NewValueErrorf(
   397  				publicKey.SignAlgo.String(),
   398  				"signature algorithm type not found"))
   399  	}
   400  
   401  	hashAlgo := crypto.CryptoToRuntimeHashingAlgorithm(publicKey.HashAlgo)
   402  	if hashAlgo == runtime.HashAlgorithmUnknown {
   403  		return nil, fmt.Errorf(
   404  			"revoking account key failed: %w",
   405  			errors.NewValueErrorf(
   406  				publicKey.HashAlgo.String(),
   407  				"hashing algorithm type not found"))
   408  	}
   409  
   410  	return &runtime.AccountKey{
   411  		KeyIndex: publicKey.Index,
   412  		PublicKey: &runtime.PublicKey{
   413  			PublicKey: publicKey.PublicKey.Encode(),
   414  			SignAlgo:  signAlgo,
   415  		},
   416  		HashAlgo:  hashAlgo,
   417  		Weight:    publicKey.Weight,
   418  		IsRevoked: publicKey.Revoked,
   419  	}, nil
   420  }
   421  
   422  // InternalAddEncodedAccountKey adds an encoded public key to an existing
   423  // account.
   424  //
   425  // This function returns following error
   426  // * NewAccountNotFoundError - if the specified account does not exist
   427  // * ValueError - if the provided encodedPublicKey is not valid public key
   428  func (updater *accountKeyUpdater) InternalAddEncodedAccountKey(
   429  	address flow.Address,
   430  	encodedPublicKey []byte,
   431  ) error {
   432  	ok, err := updater.accounts.Exists(address)
   433  	if err != nil {
   434  		return fmt.Errorf("adding encoded account key failed: %w", err)
   435  	}
   436  
   437  	if !ok {
   438  		return errors.NewAccountNotFoundError(address)
   439  	}
   440  
   441  	var publicKey flow.AccountPublicKey
   442  
   443  	publicKey, err = flow.DecodeRuntimeAccountPublicKey(encodedPublicKey, 0)
   444  	if err != nil {
   445  		hexEncodedPublicKey := hex.EncodeToString(encodedPublicKey)
   446  		return fmt.Errorf(
   447  			"adding encoded account key failed: %w",
   448  			errors.NewValueErrorf(
   449  				hexEncodedPublicKey,
   450  				"invalid encoded public key value: %w",
   451  				err))
   452  	}
   453  
   454  	err = updater.accounts.AppendPublicKey(address, publicKey)
   455  	if err != nil {
   456  		return fmt.Errorf("adding encoded account key failed: %w", err)
   457  	}
   458  
   459  	return nil
   460  }
   461  
   462  // RemoveAccountKey revokes a public key by index from an existing account.
   463  //
   464  // This function returns an error if the specified account does not exist, the
   465  // provided key is invalid, or if key revoking fails.
   466  func (updater *accountKeyUpdater) removeAccountKey(
   467  	address flow.Address,
   468  	keyIndex int,
   469  ) (
   470  	[]byte,
   471  	error,
   472  ) {
   473  	ok, err := updater.accounts.Exists(address)
   474  	if err != nil {
   475  		return nil, fmt.Errorf("remove account key failed: %w", err)
   476  	}
   477  
   478  	if !ok {
   479  		issue := errors.NewAccountNotFoundError(address)
   480  		return nil, fmt.Errorf("remove account key failed: %w", issue)
   481  	}
   482  
   483  	if keyIndex < 0 {
   484  		err = errors.NewValueErrorf(
   485  			fmt.Sprint(keyIndex),
   486  			"key index must be positive")
   487  		return nil, fmt.Errorf("remove account key failed: %w", err)
   488  	}
   489  
   490  	var publicKey flow.AccountPublicKey
   491  	publicKey, err = updater.accounts.GetPublicKey(
   492  		address,
   493  		uint64(keyIndex))
   494  	if err != nil {
   495  		return nil, fmt.Errorf("remove account key failed: %w", err)
   496  	}
   497  
   498  	// mark this key as revoked
   499  	publicKey.Revoked = true
   500  
   501  	encodedPublicKey, err := updater.accounts.SetPublicKey(
   502  		address,
   503  		uint64(keyIndex),
   504  		publicKey)
   505  	if err != nil {
   506  		return nil, fmt.Errorf("remove account key failed: %w", err)
   507  	}
   508  
   509  	return encodedPublicKey, nil
   510  }
   511  
   512  func (updater *accountKeyUpdater) AddEncodedAccountKey(
   513  	runtimeAddress common.Address,
   514  	publicKey []byte,
   515  ) error {
   516  	defer updater.tracer.StartChildSpan(
   517  		trace.FVMEnvAddEncodedAccountKey).End()
   518  
   519  	err := updater.meter.MeterComputation(
   520  		ComputationKindAddEncodedAccountKey,
   521  		1)
   522  	if err != nil {
   523  		return fmt.Errorf("add encoded account key failed: %w", err)
   524  	}
   525  
   526  	address := flow.ConvertAddress(runtimeAddress)
   527  
   528  	// TODO do a call to track the computation usage and memory usage
   529  	//
   530  	// don't enforce limit during adding a key
   531  	updater.txnState.RunWithAllLimitsDisabled(func() {
   532  		err = updater.InternalAddEncodedAccountKey(address, publicKey)
   533  	})
   534  
   535  	if err != nil {
   536  		return fmt.Errorf("add encoded account key failed: %w", err)
   537  	}
   538  	return nil
   539  }
   540  
   541  func (updater *accountKeyUpdater) RevokeEncodedAccountKey(
   542  	runtimeAddress common.Address,
   543  	index int,
   544  ) (
   545  	[]byte,
   546  	error,
   547  ) {
   548  	defer updater.tracer.StartChildSpan(trace.FVMEnvRevokeEncodedAccountKey).End()
   549  
   550  	err := updater.meter.MeterComputation(
   551  		ComputationKindRevokeEncodedAccountKey,
   552  		1)
   553  	if err != nil {
   554  		return nil, fmt.Errorf("revoke encoded account key failed: %w", err)
   555  	}
   556  
   557  	address := flow.ConvertAddress(runtimeAddress)
   558  
   559  	encodedKey, err := updater.removeAccountKey(address, index)
   560  	if err != nil {
   561  		return nil, fmt.Errorf("revoke encoded account key failed: %w", err)
   562  	}
   563  
   564  	return encodedKey, nil
   565  }
   566  
   567  func (updater *accountKeyUpdater) AddAccountKey(
   568  	runtimeAddress common.Address,
   569  	publicKey *runtime.PublicKey,
   570  	hashAlgo runtime.HashAlgorithm,
   571  	weight int,
   572  ) (
   573  	*runtime.AccountKey,
   574  	error,
   575  ) {
   576  	defer updater.tracer.StartChildSpan(trace.FVMEnvAddAccountKey).End()
   577  
   578  	err := updater.meter.MeterComputation(
   579  		ComputationKindAddAccountKey,
   580  		1)
   581  	if err != nil {
   582  		return nil, fmt.Errorf("add account key failed: %w", err)
   583  	}
   584  
   585  	accKey, err := updater.addAccountKey(
   586  		flow.ConvertAddress(runtimeAddress),
   587  		publicKey,
   588  		hashAlgo,
   589  		weight)
   590  	if err != nil {
   591  		return nil, fmt.Errorf("add account key failed: %w", err)
   592  	}
   593  
   594  	return accKey, nil
   595  }
   596  
   597  func (updater *accountKeyUpdater) RevokeAccountKey(
   598  	runtimeAddress common.Address,
   599  	keyIndex int,
   600  ) (
   601  	*runtime.AccountKey,
   602  	error,
   603  ) {
   604  	defer updater.tracer.StartChildSpan(trace.FVMEnvRevokeAccountKey).End()
   605  
   606  	err := updater.meter.MeterComputation(
   607  		ComputationKindRevokeAccountKey,
   608  		1)
   609  	if err != nil {
   610  		return nil, fmt.Errorf("revoke account key failed: %w", err)
   611  	}
   612  
   613  	return updater.revokeAccountKey(
   614  		flow.ConvertAddress(runtimeAddress),
   615  		keyIndex)
   616  }