package itests import ( "context" "encoding/hex" "os" "testing" "time" "github.com/stretchr/testify/require" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" ) // TestGetCodeAndNonce ensures that GetCode and GetTransactionCount return the correct results for: // 1. Placeholders. // 2. Non-existent actors. // 3. Normal EVM actors. // 4. Self-destructed EVM actors. func TestGetCodeAndNonce(t *testing.T) { kit.QuietMiningLogs() 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() // Accounts should have empty code, empty nonce. { // A random eth address should have no code. _, ethAddr, filAddr := client.EVM().NewAccount() bytecode, err := client.EVM().EthGetCode(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Empty(t, bytecode) // Nonce should also be zero nonce, err := client.EVM().EthGetTransactionCount(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Zero(t, nonce) // send some funds to the account. kit.SendFunds(ctx, t, client, filAddr, types.FromFil(10)) // The code should still be empty, target is now a placeholder. bytecode, err = client.EVM().EthGetCode(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Empty(t, bytecode) // Nonce should still be zero. nonce, err = client.EVM().EthGetTransactionCount(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Zero(t, nonce) } // Check contract code. { // install a contract contractHex, err := os.ReadFile("./contracts/SelfDestruct.hex") require.NoError(t, err) contract, err := hex.DecodeString(string(contractHex)) require.NoError(t, err) createReturn := client.EVM().DeployContract(ctx, client.DefaultKey.Address, contract) contractAddr := createReturn.EthAddress contractFilAddr := *createReturn.RobustAddress // The newly deployed contract should not be empty. bytecode, err := client.EVM().EthGetCode(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.NotEmpty(t, bytecode) // Nonce should be one. nonce, err := client.EVM().EthGetTransactionCount(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, ethtypes.EthUint64(1), nonce) // Destroy it. _, _, err = client.EVM().InvokeContractByFuncName(ctx, client.DefaultKey.Address, contractFilAddr, "destroy()", nil) require.NoError(t, err) // The code should be empty again. bytecode, err = client.EVM().EthGetCode(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Empty(t, bytecode) // Nonce should go back to zero nonce, err = client.EVM().EthGetTransactionCount(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Zero(t, nonce) } }