github.com/koko1123/flow-go-1@v0.29.6/engine/execution/testutil/fixtures.go (about)

     1  package testutil
     2  
     3  import (
     4  	"crypto/rand"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/onflow/cadence"
    11  	jsoncdc "github.com/onflow/cadence/encoding/json"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/koko1123/flow-go-1/engine/execution/utils"
    15  	"github.com/koko1123/flow-go-1/fvm"
    16  	"github.com/koko1123/flow-go-1/fvm/derived"
    17  	"github.com/koko1123/flow-go-1/fvm/state"
    18  	fvmUtils "github.com/koko1123/flow-go-1/fvm/utils"
    19  	"github.com/koko1123/flow-go-1/model/flow"
    20  	"github.com/koko1123/flow-go-1/module/epochs"
    21  	"github.com/koko1123/flow-go-1/utils/unittest"
    22  	"github.com/onflow/flow-go/crypto"
    23  	"github.com/onflow/flow-go/crypto/hash"
    24  )
    25  
    26  func CreateContractDeploymentTransaction(contractName string, contract string, authorizer flow.Address, chain flow.Chain) *flow.TransactionBody {
    27  
    28  	encoded := hex.EncodeToString([]byte(contract))
    29  
    30  	script := []byte(fmt.Sprintf(`transaction {
    31                prepare(signer: AuthAccount, service: AuthAccount) {
    32                  signer.contracts.add(name: "%s", code: "%s".decodeHex())
    33                }
    34              }`, contractName, encoded))
    35  
    36  	txBody := flow.NewTransactionBody().
    37  		SetScript(script).
    38  		AddAuthorizer(authorizer).
    39  		AddAuthorizer(chain.ServiceAddress())
    40  
    41  	return txBody
    42  }
    43  
    44  func UpdateContractDeploymentTransaction(contractName string, contract string, authorizer flow.Address, chain flow.Chain) *flow.TransactionBody {
    45  	encoded := hex.EncodeToString([]byte(contract))
    46  
    47  	return flow.NewTransactionBody().
    48  		SetScript([]byte(fmt.Sprintf(`transaction {
    49                prepare(signer: AuthAccount, service: AuthAccount) {
    50                  signer.contracts.update__experimental(name: "%s", code: "%s".decodeHex())
    51                }
    52              }`, contractName, encoded)),
    53  		).
    54  		AddAuthorizer(authorizer).
    55  		AddAuthorizer(chain.ServiceAddress())
    56  }
    57  
    58  func UpdateContractUnathorizedDeploymentTransaction(contractName string, contract string, authorizer flow.Address) *flow.TransactionBody {
    59  	encoded := hex.EncodeToString([]byte(contract))
    60  
    61  	return flow.NewTransactionBody().
    62  		SetScript([]byte(fmt.Sprintf(`transaction {
    63                prepare(signer: AuthAccount) {
    64                  signer.contracts.update__experimental(name: "%s", code: "%s".decodeHex())
    65                }
    66              }`, contractName, encoded)),
    67  		).
    68  		AddAuthorizer(authorizer)
    69  }
    70  
    71  func RemoveContractDeploymentTransaction(contractName string, authorizer flow.Address, chain flow.Chain) *flow.TransactionBody {
    72  	return flow.NewTransactionBody().
    73  		SetScript([]byte(fmt.Sprintf(`transaction {
    74                prepare(signer: AuthAccount, service: AuthAccount) {
    75                  signer.contracts.remove(name: "%s")
    76                }
    77              }`, contractName)),
    78  		).
    79  		AddAuthorizer(authorizer).
    80  		AddAuthorizer(chain.ServiceAddress())
    81  }
    82  
    83  func RemoveContractUnathorizedDeploymentTransaction(contractName string, authorizer flow.Address) *flow.TransactionBody {
    84  	return flow.NewTransactionBody().
    85  		SetScript([]byte(fmt.Sprintf(`transaction {
    86                prepare(signer: AuthAccount) {
    87                  signer.contracts.remove(name: "%s")
    88                }
    89              }`, contractName)),
    90  		).
    91  		AddAuthorizer(authorizer)
    92  }
    93  
    94  func CreateUnauthorizedContractDeploymentTransaction(contractName string, contract string, authorizer flow.Address) *flow.TransactionBody {
    95  	encoded := hex.EncodeToString([]byte(contract))
    96  
    97  	return flow.NewTransactionBody().
    98  		SetScript([]byte(fmt.Sprintf(`transaction {
    99                prepare(signer: AuthAccount) {
   100                  signer.contracts.add(name: "%s", code: "%s".decodeHex())
   101                }
   102              }`, contractName, encoded)),
   103  		).
   104  		AddAuthorizer(authorizer)
   105  }
   106  
   107  func SignPayload(
   108  	tx *flow.TransactionBody,
   109  	account flow.Address,
   110  	privateKey flow.AccountPrivateKey,
   111  ) error {
   112  	hasher, err := utils.NewHasher(privateKey.HashAlgo)
   113  	if err != nil {
   114  		return fmt.Errorf("failed to create hasher: %w", err)
   115  	}
   116  
   117  	err = tx.SignPayload(account, 0, privateKey.PrivateKey, hasher)
   118  
   119  	if err != nil {
   120  		return fmt.Errorf("failed to sign transaction: %w", err)
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func SignEnvelope(tx *flow.TransactionBody, account flow.Address, privateKey flow.AccountPrivateKey) error {
   127  	hasher, err := utils.NewHasher(privateKey.HashAlgo)
   128  	if err != nil {
   129  		return fmt.Errorf("failed to create hasher: %w", err)
   130  	}
   131  
   132  	err = tx.SignEnvelope(account, 0, privateKey.PrivateKey, hasher)
   133  
   134  	if err != nil {
   135  		return fmt.Errorf("failed to sign transaction: %w", err)
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  func SignTransaction(
   142  	tx *flow.TransactionBody,
   143  	address flow.Address,
   144  	privateKey flow.AccountPrivateKey,
   145  	seqNum uint64,
   146  ) error {
   147  	tx.SetProposalKey(address, 0, seqNum)
   148  	tx.SetPayer(address)
   149  	return SignEnvelope(tx, address, privateKey)
   150  }
   151  
   152  func SignTransactionAsServiceAccount(tx *flow.TransactionBody, seqNum uint64, chain flow.Chain) error {
   153  	return SignTransaction(tx, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey, seqNum)
   154  }
   155  
   156  // GenerateAccountPrivateKeys generates a number of private keys.
   157  func GenerateAccountPrivateKeys(numberOfPrivateKeys int) ([]flow.AccountPrivateKey, error) {
   158  	var privateKeys []flow.AccountPrivateKey
   159  	for i := 0; i < numberOfPrivateKeys; i++ {
   160  		pk, err := GenerateAccountPrivateKey()
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  		privateKeys = append(privateKeys, pk)
   165  	}
   166  
   167  	return privateKeys, nil
   168  }
   169  
   170  // GenerateAccountPrivateKey generates a private key.
   171  func GenerateAccountPrivateKey() (flow.AccountPrivateKey, error) {
   172  	seed := make([]byte, crypto.KeyGenSeedMinLenECDSAP256)
   173  	_, err := rand.Read(seed)
   174  	if err != nil {
   175  		return flow.AccountPrivateKey{}, err
   176  	}
   177  	privateKey, err := crypto.GeneratePrivateKey(crypto.ECDSAP256, seed)
   178  	if err != nil {
   179  		return flow.AccountPrivateKey{}, err
   180  	}
   181  	pk := flow.AccountPrivateKey{
   182  		PrivateKey: privateKey,
   183  		SignAlgo:   crypto.ECDSAP256,
   184  		HashAlgo:   hash.SHA2_256,
   185  	}
   186  	return pk, nil
   187  }
   188  
   189  // CreateAccounts inserts accounts into the ledger using the provided private keys.
   190  func CreateAccounts(
   191  	vm fvm.VM,
   192  	view state.View,
   193  	derivedBlockData *derived.DerivedBlockData,
   194  	privateKeys []flow.AccountPrivateKey,
   195  	chain flow.Chain,
   196  ) ([]flow.Address, error) {
   197  	return CreateAccountsWithSimpleAddresses(vm, view, derivedBlockData, privateKeys, chain)
   198  }
   199  
   200  func CreateAccountsWithSimpleAddresses(
   201  	vm fvm.VM,
   202  	view state.View,
   203  	derivedBlockData *derived.DerivedBlockData,
   204  	privateKeys []flow.AccountPrivateKey,
   205  	chain flow.Chain,
   206  ) ([]flow.Address, error) {
   207  	ctx := fvm.NewContext(
   208  		fvm.WithChain(chain),
   209  		fvm.WithAuthorizationChecksEnabled(false),
   210  		fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   211  		fvm.WithDerivedBlockData(derivedBlockData),
   212  	)
   213  
   214  	var accounts []flow.Address
   215  
   216  	scriptTemplate := `
   217          transaction(publicKey: [UInt8]) {
   218              prepare(signer: AuthAccount) {
   219                  let acct = AuthAccount(payer: signer)
   220                  let publicKey2 = PublicKey(
   221                      publicKey: publicKey,
   222                      signatureAlgorithm: SignatureAlgorithm.%s
   223                  )
   224                  acct.keys.add(
   225                      publicKey: publicKey2,
   226                      hashAlgorithm: HashAlgorithm.%s,
   227                      weight: %d.0
   228                  )
   229              }
   230  	    }`
   231  
   232  	serviceAddress := chain.ServiceAddress()
   233  
   234  	for _, privateKey := range privateKeys {
   235  		accountKey := privateKey.PublicKey(fvm.AccountKeyWeightThreshold)
   236  		encPublicKey := accountKey.PublicKey.Encode()
   237  		cadPublicKey := BytesToCadenceArray(encPublicKey)
   238  		encCadPublicKey, _ := jsoncdc.Encode(cadPublicKey)
   239  
   240  		script := []byte(
   241  			fmt.Sprintf(
   242  				scriptTemplate,
   243  				accountKey.SignAlgo.String(),
   244  				accountKey.HashAlgo.String(),
   245  				accountKey.Weight,
   246  			),
   247  		)
   248  
   249  		txBody := flow.NewTransactionBody().
   250  			SetScript(script).
   251  			AddArgument(encCadPublicKey).
   252  			AddAuthorizer(serviceAddress)
   253  
   254  		tx := fvm.Transaction(
   255  			txBody,
   256  			derivedBlockData.NextTxIndexForTestingOnly())
   257  		err := vm.Run(ctx, tx, view)
   258  		if err != nil {
   259  			return nil, err
   260  		}
   261  
   262  		if tx.Err != nil {
   263  			return nil, fmt.Errorf("failed to create account: %w", tx.Err)
   264  		}
   265  
   266  		var addr flow.Address
   267  
   268  		for _, event := range tx.Events {
   269  			if event.Type == flow.EventAccountCreated {
   270  				data, err := jsoncdc.Decode(nil, event.Payload)
   271  				if err != nil {
   272  					return nil, errors.New("error decoding events")
   273  				}
   274  				addr = flow.Address(data.(cadence.Event).Fields[0].(cadence.Address))
   275  				break
   276  			}
   277  		}
   278  		if addr == flow.EmptyAddress {
   279  			return nil, errors.New("no account creation event emitted")
   280  		}
   281  		accounts = append(accounts, addr)
   282  	}
   283  
   284  	return accounts, nil
   285  }
   286  
   287  func RootBootstrappedLedger(vm fvm.VM, ctx fvm.Context, additionalOptions ...fvm.BootstrapProcedureOption) state.View {
   288  	view := fvmUtils.NewSimpleView()
   289  
   290  	// set 0 clusters to pass n_collectors >= n_clusters check
   291  	epochConfig := epochs.DefaultEpochConfig()
   292  	epochConfig.NumCollectorClusters = 0
   293  
   294  	options := []fvm.BootstrapProcedureOption{
   295  		fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   296  		fvm.WithEpochConfig(epochConfig),
   297  	}
   298  
   299  	options = append(options, additionalOptions...)
   300  
   301  	bootstrap := fvm.Bootstrap(
   302  		unittest.ServiceAccountPublicKey,
   303  		options...,
   304  	)
   305  
   306  	_ = vm.Run(ctx, bootstrap, view)
   307  	return view
   308  }
   309  
   310  func BytesToCadenceArray(l []byte) cadence.Array {
   311  	values := make([]cadence.Value, len(l))
   312  	for i, b := range l {
   313  		values[i] = cadence.NewUInt8(b)
   314  	}
   315  
   316  	return cadence.NewArray(values).WithType(cadence.NewVariableSizedArrayType(cadence.NewUInt8Type()))
   317  }
   318  
   319  // CreateAccountCreationTransaction creates a transaction which will create a new account.
   320  //
   321  // This function returns a randomly generated private key and the transaction.
   322  func CreateAccountCreationTransaction(t testing.TB, chain flow.Chain) (flow.AccountPrivateKey, *flow.TransactionBody) {
   323  	accountKey, err := GenerateAccountPrivateKey()
   324  	require.NoError(t, err)
   325  	encPublicKey := accountKey.PublicKey(1000).PublicKey.Encode()
   326  	cadPublicKey := BytesToCadenceArray(encPublicKey)
   327  	encCadPublicKey, err := jsoncdc.Encode(cadPublicKey)
   328  	require.NoError(t, err)
   329  
   330  	// define the cadence script
   331  	script := fmt.Sprintf(`
   332          transaction(publicKey: [UInt8]) {
   333              prepare(signer: AuthAccount) {
   334  				let acct = AuthAccount(payer: signer)
   335                  let publicKey2 = PublicKey(
   336                      publicKey: publicKey,
   337                      signatureAlgorithm: SignatureAlgorithm.%s
   338                  )
   339                  acct.keys.add(
   340                      publicKey: publicKey2,
   341                      hashAlgorithm: HashAlgorithm.%s,
   342                      weight: 1000.0
   343                  )
   344              }
   345  	    }`,
   346  		accountKey.SignAlgo.String(),
   347  		accountKey.HashAlgo.String(),
   348  	)
   349  
   350  	// create the transaction to create the account
   351  	tx := flow.NewTransactionBody().
   352  		SetScript([]byte(script)).
   353  		AddArgument(encCadPublicKey).
   354  		AddAuthorizer(chain.ServiceAddress())
   355  
   356  	return accountKey, tx
   357  }
   358  
   359  // CreateMultiAccountCreationTransaction creates a transaction which will create many (n) new account.
   360  //
   361  // This function returns a randomly generated private key and the transaction.
   362  func CreateMultiAccountCreationTransaction(t *testing.T, chain flow.Chain, n int) (flow.AccountPrivateKey, *flow.TransactionBody) {
   363  	accountKey, err := GenerateAccountPrivateKey()
   364  	require.NoError(t, err)
   365  	encPublicKey := accountKey.PublicKey(1000).PublicKey.Encode()
   366  	cadPublicKey := BytesToCadenceArray(encPublicKey)
   367  	encCadPublicKey, err := jsoncdc.Encode(cadPublicKey)
   368  	require.NoError(t, err)
   369  
   370  	// define the cadence script
   371  	script := fmt.Sprintf(`
   372          transaction(publicKey: [UInt8]) {
   373              prepare(signer: AuthAccount) {
   374                  var i = 0
   375                  while i < %d {
   376                      let account = AuthAccount(payer: signer)
   377                      let publicKey2 = PublicKey(
   378                          publicKey: publicKey,
   379                          signatureAlgorithm: SignatureAlgorithm.%s
   380                      )
   381                      account.keys.add(
   382                          publicKey: publicKey2,
   383                          hashAlgorithm: HashAlgorithm.%s,
   384                          weight: 1000.0
   385                      )
   386                      i = i + 1
   387                  }
   388              }
   389  	    }`,
   390  		n,
   391  		accountKey.SignAlgo.String(),
   392  		accountKey.HashAlgo.String(),
   393  	)
   394  
   395  	// create the transaction to create the account
   396  	tx := flow.NewTransactionBody().
   397  		SetScript([]byte(script)).
   398  		AddArgument(encCadPublicKey).
   399  		AddAuthorizer(chain.ServiceAddress())
   400  
   401  	return accountKey, tx
   402  }
   403  
   404  // CreateAddAnAccountKeyMultipleTimesTransaction generates a tx that adds a key several times to an account.
   405  // this can be used to exhaust an account's storage.
   406  func CreateAddAnAccountKeyMultipleTimesTransaction(t *testing.T, accountKey *flow.AccountPrivateKey, counts int) *flow.TransactionBody {
   407  	script := []byte(fmt.Sprintf(`
   408        transaction(counts: Int, key: [UInt8]) {
   409          prepare(signer: AuthAccount) {
   410            var i = 0
   411            while i < counts {
   412              i = i + 1
   413              let publicKey2 = PublicKey(
   414                publicKey: key,
   415                signatureAlgorithm: SignatureAlgorithm.%s
   416              )
   417              signer.keys.add(
   418                publicKey: publicKey2,
   419                hashAlgorithm: HashAlgorithm.%s,
   420                weight: 1000.0
   421              )
   422  	      }
   423          }
   424        }
   425     	`, accountKey.SignAlgo.String(), accountKey.HashAlgo.String()))
   426  
   427  	arg1, err := jsoncdc.Encode(cadence.NewInt(counts))
   428  	require.NoError(t, err)
   429  
   430  	encPublicKey := accountKey.PublicKey(1000).PublicKey.Encode()
   431  	cadPublicKey := BytesToCadenceArray(encPublicKey)
   432  	arg2, err := jsoncdc.Encode(cadPublicKey)
   433  	require.NoError(t, err)
   434  
   435  	addKeysTx := &flow.TransactionBody{
   436  		Script: script,
   437  	}
   438  	addKeysTx = addKeysTx.AddArgument(arg1).AddArgument(arg2)
   439  	return addKeysTx
   440  }
   441  
   442  // CreateAddAccountKeyTransaction generates a tx that adds a key to an account.
   443  func CreateAddAccountKeyTransaction(t *testing.T, accountKey *flow.AccountPrivateKey) *flow.TransactionBody {
   444  	keyBytes := accountKey.PublicKey(1000).PublicKey.Encode()
   445  
   446  	script := []byte(`
   447          transaction(key: [UInt8]) {
   448            prepare(signer: AuthAccount) {
   449              let acct = AuthAccount(payer: signer)
   450              let publicKey2 = PublicKey(
   451                publicKey: key,
   452                signatureAlgorithm: SignatureAlgorithm.%s
   453              )
   454              signer.keys.add(
   455                publicKey: publicKey2,
   456                hashAlgorithm: HashAlgorithm.%s,
   457                weight: %d.0
   458              )
   459            }
   460          }
   461     	`)
   462  
   463  	arg, err := jsoncdc.Encode(bytesToCadenceArray(keyBytes))
   464  	require.NoError(t, err)
   465  
   466  	addKeysTx := &flow.TransactionBody{
   467  		Script: script,
   468  	}
   469  	addKeysTx = addKeysTx.AddArgument(arg)
   470  
   471  	return addKeysTx
   472  }
   473  
   474  func bytesToCadenceArray(l []byte) cadence.Array {
   475  	values := make([]cadence.Value, len(l))
   476  	for i, b := range l {
   477  		values[i] = cadence.NewUInt8(b)
   478  	}
   479  
   480  	return cadence.NewArray(values)
   481  }