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