From 55f75c1aa85dce534be48bdec616d3d737b019c9 Mon Sep 17 00:00:00 2001 From: Unique-Divine Date: Mon, 21 Oct 2024 21:37:29 -0500 Subject: [PATCH] fix funtoken.sol precompile and test --- x/evm/evmtest/test_deps.go | 6 ++ x/evm/evmtest/tx.go | 86 +++++++++++++++++++----- x/evm/keeper/erc20.go | 5 +- x/evm/keeper/funtoken_from_coin_test.go | 2 +- x/evm/keeper/funtoken_from_erc20_test.go | 15 +++-- x/evm/precompile/abci_event.go | 24 ------- x/evm/precompile/funtoken.go | 5 +- x/evm/precompile/funtoken_test.go | 16 +++-- x/evm/precompile/wasm.go | 3 + x/evm/statedb/journal.go | 2 +- x/evm/statedb/statedb.go | 1 - 11 files changed, 105 insertions(+), 60 deletions(-) delete mode 100644 x/evm/precompile/abci_event.go diff --git a/x/evm/evmtest/test_deps.go b/x/evm/evmtest/test_deps.go index 9c6015933..1810b1c8f 100644 --- a/x/evm/evmtest/test_deps.go +++ b/x/evm/evmtest/test_deps.go @@ -1,6 +1,8 @@ package evmtest import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" @@ -54,3 +56,7 @@ func (deps TestDeps) StateDB() *statedb.StateDB { func (deps *TestDeps) GethSigner() gethcore.Signer { return gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) } + +func (deps TestDeps) GoCtx() context.Context { + return sdk.WrapSDKContext(deps.Ctx) +} diff --git a/x/evm/evmtest/tx.go b/x/evm/evmtest/tx.go index 107a15593..01e9a97ed 100644 --- a/x/evm/evmtest/tx.go +++ b/x/evm/evmtest/tx.go @@ -20,8 +20,11 @@ import ( srvconfig "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/NibiruChain/nibiru/v2/x/evm" "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" ) type GethTxType = uint8 @@ -123,7 +126,9 @@ func ExecuteNibiTransfer(deps *TestDeps, t *testing.T) *evm.MsgEthereumTx { To: &recipient, Nonce: (*hexutil.Uint64)(&nonce), } - ethTxMsg, err := GenerateAndSignEthTxMsg(txArgs, deps) + ethTxMsg, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner(txArgs, deps, deps.Sender) + require.NoError(t, err) + err = ethTxMsg.Sign(gethSigner, krSigner) require.NoError(t, err) resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) @@ -153,18 +158,20 @@ func DeployContract( bytecodeForCall := append(contract.Bytecode, packedArgs...) nonce := deps.StateDB().GetNonce(deps.Sender.EthAddr) - msgEthTx, err := GenerateAndSignEthTxMsg( + ethTxMsg, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner( evm.JsonTxArgs{ Nonce: (*hexutil.Uint64)(&nonce), Input: (*hexutil.Bytes)(&bytecodeForCall), From: &deps.Sender.EthAddr, - }, deps, + }, deps, deps.Sender, ) if err != nil { return nil, errors.Wrap(err, "failed to generate and sign eth tx msg") + } else if err := ethTxMsg.Sign(gethSigner, krSigner); err != nil { + return nil, errors.Wrap(err, "failed to generate and sign eth tx msg") } - resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), msgEthTx) + resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) if err != nil { return nil, errors.Wrap(err, "failed to execute ethereum tx") } @@ -174,7 +181,7 @@ func DeployContract( return &DeployContractResult{ TxResp: resp, - EthTxMsg: msgEthTx, + EthTxMsg: ethTxMsg, ContractData: contract, Nonce: nonce, ContractAddr: crypto.CreateAddress(deps.Sender.EthAddr, nonce), @@ -210,23 +217,70 @@ func DeployAndExecuteERC20Transfer( Nonce: (*hexutil.Uint64)(&nonce), Data: (*hexutil.Bytes)(&input), } - erc20Transfer, err = GenerateAndSignEthTxMsg(txArgs, deps) + erc20Transfer, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner(txArgs, deps, deps.Sender) + require.NoError(t, err) + err = erc20Transfer.Sign(gethSigner, krSigner) require.NoError(t, err) - resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), erc20Transfer) + resp, err := deps.App.EvmKeeper.EthereumTx(deps.GoCtx(), erc20Transfer) require.NoError(t, err) require.Empty(t, resp.VmError) return erc20Transfer, predecessors } -// GenerateAndSignEthTxMsg estimates gas, sets gas limit and sings the tx -func GenerateAndSignEthTxMsg( - jsonTxArgs evm.JsonTxArgs, deps *TestDeps, -) (*evm.MsgEthereumTx, error) { +func CallContractTx( + deps *TestDeps, + contractAddr gethcommon.Address, + input []byte, + sender EthPrivKeyAcc, +) (ethTxMsg *evm.MsgEthereumTx, resp *evm.MsgEthereumTxResponse, err error) { + nonce := deps.StateDB().GetNonce(sender.EthAddr) + ethTxMsg, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner(evm.JsonTxArgs{ + From: &sender.EthAddr, + To: &contractAddr, + Nonce: (*hexutil.Uint64)(&nonce), + Data: (*hexutil.Bytes)(&input), + }, deps, sender) + if err != nil { + err = fmt.Errorf("CallContract error during tx generation: %w", err) + return + } + + txConfig := deps.EvmKeeper.TxConfig(deps.Ctx, gethcommon.HexToHash(ethTxMsg.Hash)) + stateDB := statedb.New(deps.Ctx, &deps.EvmKeeper, txConfig) + err = stateDB.Commit() + if err != nil { + return + } + + err = ethTxMsg.Sign(gethSigner, krSigner) + if err != nil { + err = fmt.Errorf("CallContract error during signature: %w", err) + return + } + + resp, err = deps.EvmKeeper.EthereumTx(deps.GoCtx(), ethTxMsg) + return ethTxMsg, resp, err +} + +// GenerateEthTxMsgAndSigner estimates gas, sets gas limit and returns signer for +// the tx. +// +// Usage: +// +// ```go +// evmTxMsg, gethSigner, krSigner, _ := GenerateEthTxMsgAndSigner( +// jsonTxArgs, &deps, sender, +// ) +// err := evmTxMsg.Sign(gethSigner, sender.KeyringSigner) +// ``` +func GenerateEthTxMsgAndSigner( + jsonTxArgs evm.JsonTxArgs, deps *TestDeps, sender EthPrivKeyAcc, +) (evmTxMsg *evm.MsgEthereumTx, gethSigner gethcore.Signer, krSigner keyring.Signer, err error) { estimateArgs, err := json.Marshal(&jsonTxArgs) if err != nil { - return nil, err + return } res, err := deps.App.EvmKeeper.EstimateGas( sdk.WrapSDKContext(deps.Ctx), @@ -238,13 +292,13 @@ func GenerateAndSignEthTxMsg( }, ) if err != nil { - return nil, err + return } jsonTxArgs.Gas = (*hexutil.Uint64)(&res.Gas) - msgEthTx := jsonTxArgs.ToMsgEthTx() - gethSigner := gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) - return msgEthTx, msgEthTx.Sign(gethSigner, deps.Sender.KeyringSigner) + evmTxMsg = jsonTxArgs.ToMsgEthTx() + gethSigner = gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) + return evmTxMsg, gethSigner, sender.KeyringSigner, nil } func TransferWei( diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go index f5e4f2123..4196bc683 100644 --- a/x/evm/keeper/erc20.go +++ b/x/evm/keeper/erc20.go @@ -214,10 +214,9 @@ func (k Keeper) CallContractWithInput( } blockHash := gethcommon.BytesToHash(ctx.HeaderHash()) - // txIdx := uint(k.EvmState.BlockTxIndex.GetOr(ctx, 0)) // TxIndex - // txLogIdx := uint(k.EvmState.BlockLogSize.GetOr(ctx, 0)) // LogIndex - ctx.TxBytes() txConfig := statedb.NewEmptyTxConfig(blockHash) + txConfig.TxIndex = uint(k.EvmState.BlockLogSize.GetOr(ctx, 0)) + txConfig.LogIndex = uint(k.EvmState.BlockLogSize.GetOr(ctx, 0)) evmResp, err = k.ApplyEvmMsg( ctx, evmMsg, evm.NewNoOpTracer(), commit, evmCfg, txConfig, diff --git a/x/evm/keeper/funtoken_from_coin_test.go b/x/evm/keeper/funtoken_from_coin_test.go index cb9b87ffb..1f5e3a85a 100644 --- a/x/evm/keeper/funtoken_from_coin_test.go +++ b/x/evm/keeper/funtoken_from_coin_test.go @@ -282,7 +282,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { // Check 3: erc-20 balance balance, err = deps.EvmKeeper.ERC20().BalanceOf(funTokenErc20Addr.Address, alice.EthAddr, deps.Ctx) s.Require().NoError(err) - s.Require().Zero(balance.Cmp(big.NewInt(0))) + s.Require().Equal("0", balance.String()) s.T().Log("sad: Convert more erc-20 to back to bank coin, insufficient funds") _, err = deps.EvmKeeper.CallContract( diff --git a/x/evm/keeper/funtoken_from_erc20_test.go b/x/evm/keeper/funtoken_from_erc20_test.go index 3eaf1462c..e6341bb22 100644 --- a/x/evm/keeper/funtoken_from_erc20_test.go +++ b/x/evm/keeper/funtoken_from_erc20_test.go @@ -160,7 +160,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { s.ErrorContains(err, "either the \"from_erc20\" or \"from_bank_denom\" must be set") } -func (s *FunTokenFromErc20Suite) TestSendFromEvmToCosmos() { +func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank() { deps := evmtest.NewTestDeps() s.T().Log("Deploy ERC20") @@ -210,7 +210,7 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToCosmos() { randomAcc := testutil.AccAddress() - s.T().Log("send erc20 tokens to cosmos") + s.T().Log("send erc20 tokens to Bank") _, err = deps.EvmKeeper.CallContract( deps.Ctx, embeds.SmartContract_FunToken.ABI, @@ -231,8 +231,8 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToCosmos() { deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount, ) - s.T().Log("sad: send too many erc20 tokens to cosmos") - _, err = deps.EvmKeeper.CallContract( + s.T().Log("sad: send too many erc20 tokens to Bank") + evmResp, err := deps.EvmKeeper.CallContract( deps.Ctx, embeds.SmartContract_FunToken.ABI, deps.Sender.EthAddr, @@ -243,9 +243,10 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToCosmos() { big.NewInt(70_000), randomAcc.String(), ) - s.Require().Error(err) + s.T().Log("check balances") + s.Require().Error(err, evmResp.String()) - s.T().Log("send cosmos tokens back to erc20") + s.T().Log("send Bank tokens back to erc20") _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ ToEthAddr: eth.EIP55Addr{ @@ -264,7 +265,7 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToCosmos() { deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount.Equal(sdk.NewInt(0)), ) - s.T().Log("sad: send too many cosmos tokens back to erc20") + s.T().Log("sad: send too many Bank tokens back to erc20") _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ ToEthAddr: eth.EIP55Addr{ diff --git a/x/evm/precompile/abci_event.go b/x/evm/precompile/abci_event.go deleted file mode 100644 index beae86a89..000000000 --- a/x/evm/precompile/abci_event.go +++ /dev/null @@ -1,24 +0,0 @@ -package precompile - -import ( - // "github.com/ethereum/go-ethereum/accounts/abi" - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - // "github.com/NibiruChain/nibiru/v2/x/evm/statedb" -) - -func MakeTopicFromStr(s string) (topic gethcommon.Hash) { - return MakeTopicFromBytes([]byte(s)) -} - -func MakeTopicFromBytes(bz []byte) (topic gethcommon.Hash) { - hash := crypto.Keccak256Hash(bz) - copy(topic[:], hash[:]) - return topic -} - -// func (p *precompileWasm) foo(stateDB *statedb.StateDB) { -// abi.MakeTopics() -// // stateDB.AddLog() -// // p.evmKeeper.State -// } diff --git a/x/evm/precompile/funtoken.go b/x/evm/precompile/funtoken.go index bcafb0c55..03b08a85c 100644 --- a/x/evm/precompile/funtoken.go +++ b/x/evm/precompile/funtoken.go @@ -45,6 +45,7 @@ func (p precompileFunToken) Run( evm *vm.EVM, contract *vm.Contract, readonly bool, ) (bz []byte, err error) { defer ErrPrecompileRun(err, p)() + res, err := OnRunStart(evm, contract, embeds.SmartContract_FunToken.ABI) if err != nil { return nil, err @@ -61,7 +62,9 @@ func (p precompileFunToken) Run( err = fmt.Errorf("invalid method called with name \"%s\"", method.Name) return } - + if err != nil { + return nil, err + } if err := OnRunEnd(res.StateDB, res.SnapshotBeforeRun, p.Address()); err != nil { return nil, err } diff --git a/x/evm/precompile/funtoken_test.go b/x/evm/precompile/funtoken_test.go index 9aad9feb3..62512968f 100644 --- a/x/evm/precompile/funtoken_test.go +++ b/x/evm/precompile/funtoken_test.go @@ -5,7 +5,7 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/suite" "github.com/NibiruChain/nibiru/v2/eth" @@ -49,19 +49,19 @@ func (s *FuntokenSuite) TestFailToPackABI() { { name: "wrong type for amount", methodName: string(precompile.FunTokenMethod_BankSend), - callArgs: []any{common.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), "foo", testutil.AccAddress().String()}, + callArgs: []any{gethcommon.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), "foo", testutil.AccAddress().String()}, wantError: "abi: cannot use string as type ptr as argument", }, { name: "wrong type for recipient", methodName: string(precompile.FunTokenMethod_BankSend), - callArgs: []any{common.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), big.NewInt(1), 111}, + callArgs: []any{gethcommon.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), big.NewInt(1), 111}, wantError: "abi: cannot use int as type string as argument", }, { name: "invalid method name", methodName: "foo", - callArgs: []any{common.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), big.NewInt(1), testutil.AccAddress().String()}, + callArgs: []any{gethcommon.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), big.NewInt(1), testutil.AccAddress().String()}, wantError: "method 'foo' not found", }, } @@ -126,10 +126,14 @@ func (s *FuntokenSuite) TestHappyPath() { input, err := embeds.SmartContract_FunToken.ABI.Pack(string(precompile.FunTokenMethod_BankSend), callArgs...) s.NoError(err) - _, err = deps.EvmKeeper.CallContractWithInput( - deps.Ctx, deps.Sender.EthAddr, &precompile.PrecompileAddr_FunToken, true, input, + _, resp, err := evmtest.CallContractTx( + &deps, + precompile.PrecompileAddr_FunToken, + input, + deps.Sender, ) s.Require().NoError(err) + s.Require().Empty(resp.VmError) evmtest.AssertERC20BalanceEqual(s.T(), deps, erc20, deps.Sender.EthAddr, big.NewInt(69_000)) evmtest.AssertERC20BalanceEqual(s.T(), deps, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(0)) diff --git a/x/evm/precompile/wasm.go b/x/evm/precompile/wasm.go index ad4484f29..583b60014 100644 --- a/x/evm/precompile/wasm.go +++ b/x/evm/precompile/wasm.go @@ -96,6 +96,9 @@ func (p precompileWasm) Run( err = fmt.Errorf("invalid method called with name \"%s\"", method.Name) return } + if err != nil { + return nil, err + } if err := OnRunEnd(res.StateDB, res.SnapshotBeforeRun, p.Address()); err != nil { return nil, err diff --git a/x/evm/statedb/journal.go b/x/evm/statedb/journal.go index c4a02a950..d8b15328e 100644 --- a/x/evm/statedb/journal.go +++ b/x/evm/statedb/journal.go @@ -162,7 +162,7 @@ func (ch PrecompileSnapshotBeforeRun) Revert(s *StateDB) { // TODO: Revert PrecompileSnapshotBeforeRun // Restore the multistore recorded in the journal entry - fmt.Printf("\"Revert was called.\"\n") + fmt.Printf("TODO: UD-DEBUG: PrecompileSnapshotBeforeRun.Revert called\n") s.cacheCtx = s.cacheCtx.WithMultiStore(ch.MultiStore) // Rewrite the `writeCacheCtxFn` using the same logic as sdk.Context.CacheCtx diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index b917845a7..b08eef272 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -523,7 +523,6 @@ func (s *StateDB) SavePrecompileSnapshotToJournal( precompileAddr common.Address, snapshot PrecompileSnapshotBeforeRun, ) error { - fmt.Println("SavePrecompileSnapshotToJournal was called") obj := s.getOrNewStateObject(precompileAddr) obj.db.journal.append(snapshot) s.precompileSnapshotsCount++