From 3456d90e0d0b687332b313b8e4bd932697d0d63d Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal <108157875+shrenujbansal@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:28:49 -0500 Subject: [PATCH] itests: Add tests for eth get balance and transaction submits (#9966) * Add tests for eth get balance and transaction submits * fix lint * one more lint * Add contract invocation test * address comments * fix lint * address final comment * make gen and fix lint as always --- .circleci/config.yml | 10 + chain/types/ethtypes/eth_transactions_test.go | 1 + itests/eth_balance_test.go | 97 ++++++ itests/eth_transactions_test.go | 300 ++++++++++++++++++ 4 files changed, 408 insertions(+) create mode 100644 itests/eth_balance_test.go create mode 100644 itests/eth_transactions_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 24bae7f19..bfa65b951 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -731,6 +731,11 @@ workflows: suite: itest-eth_account_abstraction target: "./itests/eth_account_abstraction_test.go" + - test: + name: test-itest-eth_balance + suite: itest-eth_balance + target: "./itests/eth_balance_test.go" + - test: name: test-itest-eth_deploy suite: itest-eth_deploy @@ -741,6 +746,11 @@ workflows: suite: itest-eth_filter target: "./itests/eth_filter_test.go" + - test: + name: test-itest-eth_transactions + suite: itest-eth_transactions + target: "./itests/eth_transactions_test.go" + - test: name: test-itest-fevm_address suite: itest-fevm_address diff --git a/chain/types/ethtypes/eth_transactions_test.go b/chain/types/ethtypes/eth_transactions_test.go index 1f34eab7b..c9b7dbc63 100644 --- a/chain/types/ethtypes/eth_transactions_test.go +++ b/chain/types/ethtypes/eth_transactions_test.go @@ -151,6 +151,7 @@ func TestTransformParams(t *testing.T) { require.Equal(t, mustDecodeHex("0x1122334455"), evmParams.Initcode) } + func TestEcRecover(t *testing.T) { rHex := "0x479ff7fa64cf8bf641eb81635d1e8a698530d2f219951d234539e6d074819529" sHex := "0x4b6146d27be50cdbb2853ba9a42f207af8d730272f1ebe9c9a78aeef1d6aa924" diff --git a/itests/eth_balance_test.go b/itests/eth_balance_test.go new file mode 100644 index 000000000..3176aefc8 --- /dev/null +++ b/itests/eth_balance_test.go @@ -0,0 +1,97 @@ +package itests + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/itests/kit" +) + +func TestEthGetBalanceExistingF4address(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + _, ethAddr, deployer := client.EVM().NewAccount() + + fundAmount := types.FromFil(0) + // send some funds to the f410 address + kit.SendFunds(ctx, t, client, deployer, fundAmount) + + balance, err := client.EthGetBalance(ctx, ethAddr, "latest") + require.NoError(t, err) + require.Equal(t, balance, ethtypes.EthBigInt{Int: fundAmount.Int}) +} + +func TestEthGetBalanceNonExistentF4address(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + _, ethAddr, _ := client.EVM().NewAccount() + + balance, err := client.EthGetBalance(ctx, ethAddr, "latest") + require.NoError(t, err) + require.Equal(t, balance, ethtypes.EthBigIntZero) +} + +func TestEthGetBalanceExistentIDMaskedAddr(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + faddr, err := client.WalletDefaultAddress(ctx) + require.NoError(t, err) + fid, err := client.StateLookupID(ctx, faddr, types.EmptyTSK) + require.NoError(t, err) + + ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(fid) + require.NoError(t, err) + + balance, err := client.WalletBalance(ctx, fid) + require.NoError(t, err) + + ebal, err := client.EthGetBalance(ctx, ethAddr, "latest") + require.NoError(t, err) + require.Equal(t, ebal, ethtypes.EthBigInt{Int: balance.Int}) +} + +func TestEthGetBalanceBuiltinActor(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // Address for market actor + fid, err := address.NewFromString("f05") + require.NoError(t, err) + + kit.SendFunds(ctx, t, client, fid, abi.TokenAmount{Int: big.NewInt(10).Int}) + + ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(fid) + require.NoError(t, err) + + ebal, err := client.EthGetBalance(ctx, ethAddr, "latest") + require.NoError(t, err) + require.Equal(t, ethtypes.EthBigInt{Int: big.NewInt(10).Int}, ebal) +} diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go new file mode 100644 index 000000000..9c28f80e1 --- /dev/null +++ b/itests/eth_transactions_test.go @@ -0,0 +1,300 @@ +package itests + +import ( + "context" + "encoding/hex" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/itests/kit" +) + +func TestValueTransferValidSignature(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // install contract + contractHex, err := os.ReadFile("./contracts/SimpleCoin.bin") + require.NoError(t, err) + + contract, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, deployer := client.EVM().NewAccount() + _, ethAddr2, _ := client.EVM().NewAccount() + + kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000)) + + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + Data: contract, + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + tx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.NewInt(100), + Nonce: 0, + To: ðAddr2, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&tx, key.PrivateKey) + // Mangle signature + tx.V.Int.Xor(tx.V.Int, big.NewInt(1).Int) + + signed, err := tx.ToRlpSignedMsg() + require.NoError(t, err) + // Submit transaction with bad signature + _, err = client.EVM().EthSendRawTransaction(ctx, signed) + require.Error(t, err) + + // Submit transaction with valid signature + client.EVM().SignTransaction(&tx, key.PrivateKey) + hash := client.EVM().SubmitTransaction(ctx, &tx) + + receipt, err := waitForEthTxReceipt(ctx, client, hash) + require.NoError(t, err) + require.NotNil(t, receipt) + + // Success. + require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) +} + +func TestLegacyTransaction(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // This is a legacy style transaction obtained from etherscan + // Tx details: https://etherscan.io/getRawTx?tx=0x0763262208d89efeeb50c8bb05b50c537903fe9d7bdef3b223fd1f5f69f69b32 + txBytes, err := hex.DecodeString("f86f830131cf8504a817c800825208942cf1e5a8250ded8835694ebeb90cfa0237fcb9b1882ec4a5251d1100008026a0f5f8d2244d619e211eeb634acd1bea0762b7b4c97bba9f01287c82bfab73f911a015be7982898aa7cc6c6f27ff33e999e4119d6cd51330353474b98067ff56d930") + require.NoError(t, err) + _, err = client.EVM().EthSendRawTransaction(ctx, txBytes) + require.ErrorContains(t, err, "legacy transaction is not supported") + +} + +func TestContractDeploymentValidSignature(t *testing.T) { + + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // install contract + contractHex, err := os.ReadFile("./contracts/SimpleCoin.bin") + require.NoError(t, err) + + contract, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, deployer := client.EVM().NewAccount() + + // send some funds to the f410 address + kit.SendFunds(ctx, t, client, deployer, types.FromFil(10)) + + // verify the deployer address is an embryo. + client.AssertActorType(ctx, deployer, manifest.EmbryoKey) + + tx, err := deployContractTx(ctx, client, ethAddr, contract) + require.NoError(t, err) + + client.EVM().SignTransaction(tx, key.PrivateKey) + // Mangle signature + tx.V.Int.Xor(tx.V.Int, big.NewInt(1).Int) + + signed, err := tx.ToRlpSignedMsg() + require.NoError(t, err) + // Submit transaction with bad signature + _, err = client.EVM().EthSendRawTransaction(ctx, signed) + require.Error(t, err) + + // Submit transaction with valid signature + client.EVM().SignTransaction(tx, key.PrivateKey) + hash := client.EVM().SubmitTransaction(ctx, tx) + + receipt, err := waitForEthTxReceipt(ctx, client, hash) + require.NoError(t, err) + require.NotNil(t, receipt) + + // Success. + require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) + + // Verify that the deployer is now an account. + client.AssertActorType(ctx, deployer, manifest.EthAccountKey) + + // Verify that the nonce was incremented. + nonce, err := client.MpoolGetNonce(ctx, deployer) + require.NoError(t, err) + require.EqualValues(t, 1, nonce) + + // Verify that the deployer is now an account. + client.AssertActorType(ctx, deployer, manifest.EthAccountKey) +} + +func TestContractInvocation(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // install contract + contractHex, err := os.ReadFile("./contracts/SimpleCoin.bin") + require.NoError(t, err) + + contract, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, deployer := client.EVM().NewAccount() + // send some funds to the f410 address + kit.SendFunds(ctx, t, client, deployer, types.FromFil(10)) + + // DEPLOY CONTRACT + tx, err := deployContractTx(ctx, client, ethAddr, contract) + require.NoError(t, err) + + client.EVM().SignTransaction(tx, key.PrivateKey) + hash := client.EVM().SubmitTransaction(ctx, tx) + + receipt, err := waitForEthTxReceipt(ctx, client, hash) + require.NoError(t, err) + require.NotNil(t, receipt) + require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) + + // Get contract address. + contractAddr := client.EVM().ComputeContractAddress(ethAddr, 0) + + // INVOKE CONTRACT + + // Params + // entry point for getBalance - f8b2cb4f + // address - ff00000000000000000000000000000000000064 + params, err := hex.DecodeString("f8b2cb4f000000000000000000000000ff00000000000000000000000000000000000064") + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + To: &contractAddr, + Data: params, + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + invokeTx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + To: &contractAddr, + Value: big.Zero(), + Nonce: 1, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + Input: params, + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&invokeTx, key.PrivateKey) + // Mangle signature + invokeTx.V.Int.Xor(invokeTx.V.Int, big.NewInt(1).Int) + + signed, err := invokeTx.ToRlpSignedMsg() + require.NoError(t, err) + // Submit transaction with bad signature + _, err = client.EVM().EthSendRawTransaction(ctx, signed) + require.Error(t, err) + + // Submit transaction with valid signature + client.EVM().SignTransaction(&invokeTx, key.PrivateKey) + hash = client.EVM().SubmitTransaction(ctx, &invokeTx) + + receipt, err = waitForEthTxReceipt(ctx, client, hash) + require.NoError(t, err) + require.NotNil(t, receipt) + + // Success. + require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) + +} + +func deployContractTx(ctx context.Context, client *kit.TestFullNode, ethAddr ethtypes.EthAddress, contract []byte) (*ethtypes.EthTxArgs, error) { + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + Data: contract, + }) + if err != nil { + return nil, err + } + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + if err != nil { + return nil, err + } + + // now deploy a contract from the embryo, and validate it went well + return ðtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.Zero(), + Nonce: 0, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + Input: contract, + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + }, nil +} + +func waitForEthTxReceipt(ctx context.Context, client *kit.TestFullNode, hash ethtypes.EthHash) (*api.EthTxReceipt, error) { + var receipt *api.EthTxReceipt + var err error + for i := 0; i < 10000000000; i++ { + receipt, err = client.EthGetTransactionReceipt(ctx, hash) + if err != nil || receipt == nil { + time.Sleep(500 * time.Millisecond) + continue + } + break + } + return receipt, err +}