-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(evm): added tx logs events to the funtoken related txs #2161
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
cb7ffb5
fix: added tx logs emitting to the funtoken related txs
onikonychev 4963542
Merge branch 'main' of github.com:NibiruChain/nibiru into feat/logs-f…
onikonychev eab54cf
chore: changelog update
onikonychev 7a49a1b
chore: lint
onikonychev 5977b7a
Merge branch 'main' into feat/logs-for-call-contract
Unique-Divine ef106f5
chore: debug failing integration test
onikonychev b5d5595
fix: removed tx index updating for non eth txs
onikonychev d9e34c6
chore: resolve conflicts
onikonychev 16f47a4
fix: tests
onikonychev a9ed35d
Merge branch 'main' into feat/logs-for-call-contract
k-yang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,299 @@ | ||
package backend_test | ||
|
||
import ( | ||
"fmt" | ||
"math/big" | ||
|
||
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
gethcommon "github.com/ethereum/go-ethereum/common" | ||
gethcore "github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
|
||
"github.com/NibiruChain/nibiru/v2/eth" | ||
"github.com/NibiruChain/nibiru/v2/eth/rpc/backend" | ||
"github.com/NibiruChain/nibiru/v2/x/common/testutil" | ||
"github.com/NibiruChain/nibiru/v2/x/evm" | ||
"github.com/NibiruChain/nibiru/v2/x/evm/embeds" | ||
"github.com/NibiruChain/nibiru/v2/x/evm/evmtest" | ||
"github.com/NibiruChain/nibiru/v2/x/evm/precompile" | ||
) | ||
|
||
// TestEthLogs checks that eth txs as well as funtoken txs produce tx_logs events and update tx index properly. | ||
// To check that, we send a series of transactions: | ||
// - 1: simple eth transfer | ||
// - 2: deploying erc20 contract | ||
// - 3. creating funtoken from erc20 | ||
// - 4: creating funtoken from coin | ||
// - 5. converting coin to erc20 | ||
// - 6. converting erc20 born token to coin via precompile | ||
// Each tx should emit some tx logs and emit proper tx index within ethereum tx event. | ||
func (s *BackendSuite) TestLogs() { | ||
// Test is broadcasting txs. Lock to avoid nonce conflicts. | ||
testMutex.Lock() | ||
defer testMutex.Unlock() | ||
|
||
// Start with fresh block | ||
s.Require().NoError(s.network.WaitForNextBlock()) | ||
|
||
s.T().Log("TX1: Send simple nibi transfer") | ||
randomEthAddr := evmtest.NewEthPrivAcc().EthAddr | ||
txHashFirst := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false) | ||
|
||
s.T().Log("TX2: Deploy ERC20 contract") | ||
_, erc20ContractAddr := s.DeployTestContract(false) | ||
erc20Addr, _ := eth.NewEIP55AddrFromStr(erc20ContractAddr.String()) | ||
|
||
s.T().Log("TX3: Create FunToken from ERC20") | ||
nonce := s.getCurrentNonce(eth.NibiruAddrToEthAddr(s.node.Address)) | ||
txResp, err := s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ | ||
Sender: s.node.Address.String(), | ||
FromErc20: &erc20Addr, | ||
}) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(txResp) | ||
s.Require().Equal( | ||
uint32(0), | ||
txResp.Code, | ||
fmt.Sprintf("Failed to create FunToken from ERC20. RawLog: %s", txResp.RawLog), | ||
) | ||
|
||
s.T().Log("TX4: Create FunToken from unibi coin") | ||
nonce++ | ||
erc20FromCoinAddr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, s.getCurrentNonce(evm.EVM_MODULE_ADDRESS)+1) | ||
|
||
txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ | ||
Sender: s.node.Address.String(), | ||
FromBankDenom: evm.EVMBankDenom, | ||
}) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(txResp) | ||
s.Require().Equal( | ||
uint32(0), | ||
txResp.Code, | ||
fmt.Sprintf("Failed to create FunToken from unibi coin. RawLog: %s", txResp.RawLog), | ||
) | ||
|
||
s.T().Log("TX5: Convert coin to EVM") | ||
nonce++ | ||
txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgConvertCoinToEvm{ | ||
Sender: s.node.Address.String(), | ||
BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(1)), | ||
ToEthAddr: eth.EIP55Addr{ | ||
Address: s.fundedAccEthAddr, | ||
}, | ||
}) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(txResp) | ||
s.Require().Equal( | ||
uint32(0), | ||
txResp.Code, | ||
fmt.Sprintf("Failed converting coin to evm. RawLog: %s", txResp.RawLog), | ||
) | ||
|
||
s.T().Log("TX6: Send erc20 token to coin using precompile") | ||
randomNibiAddress := testutil.AccAddress() | ||
packedArgsPass, err := embeds.SmartContract_FunToken.ABI.Pack( | ||
"sendToBank", | ||
erc20Addr.Address, | ||
big.NewInt(1), | ||
randomNibiAddress.String(), | ||
) | ||
s.Require().NoError(err) | ||
txHashLast := SendTransaction( | ||
s, | ||
&gethcore.LegacyTx{ | ||
Nonce: s.getCurrentNonce(s.fundedAccEthAddr), | ||
To: &precompile.PrecompileAddr_FunToken, | ||
Data: packedArgsPass, | ||
Gas: 1_500_000, | ||
GasPrice: big.NewInt(1), | ||
}, | ||
false, | ||
) | ||
|
||
// Wait for all txs to be included in a block | ||
blockNumFirstTx, _, _ := WaitForReceipt(s, txHashFirst) | ||
blockNumLastTx, _, _ := WaitForReceipt(s, txHashLast) | ||
s.Require().NotNil(blockNumFirstTx) | ||
s.Require().NotNil(blockNumLastTx) | ||
|
||
// Check tx logs for each tx | ||
type logsCheck struct { | ||
txInfo string | ||
logs []*gethcore.Log | ||
expectEthTx bool | ||
} | ||
checks := []logsCheck{ | ||
{ | ||
txInfo: "TX1 - simple eth transfer, should have empty logs", | ||
logs: nil, | ||
expectEthTx: true, | ||
}, | ||
{ | ||
txInfo: "TX2 - deploying erc20 contract, should have logs", | ||
logs: []*gethcore.Log{ | ||
// minting initial balance to the account | ||
{ | ||
Address: erc20ContractAddr, | ||
Topics: []gethcommon.Hash{ | ||
crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), | ||
gethcommon.Address{}.Hash(), | ||
s.fundedAccEthAddr.Hash(), | ||
}, | ||
}, | ||
}, | ||
expectEthTx: true, | ||
}, | ||
{ | ||
txInfo: "TX3 - create FunToken from ERC20, no eth tx, no logs", | ||
logs: nil, | ||
expectEthTx: false, | ||
}, | ||
{ | ||
txInfo: "TX4 - create FunToken from unibi coin, no eth tx, logs for contract deployment", | ||
logs: []*gethcore.Log{ | ||
// contract ownership to evm module | ||
{ | ||
Address: erc20FromCoinAddr, | ||
Topics: []gethcommon.Hash{ | ||
crypto.Keccak256Hash([]byte("OwnershipTransferred(address,address)")), | ||
gethcommon.Address{}.Hash(), | ||
evm.EVM_MODULE_ADDRESS.Hash(), | ||
}, | ||
}, | ||
}, | ||
expectEthTx: false, | ||
}, | ||
{ | ||
txInfo: "TX5 - Convert coin to EVM, no eth tx, logs for minting tokens to the account", | ||
logs: []*gethcore.Log{ | ||
// minting to the account | ||
{ | ||
Address: erc20FromCoinAddr, | ||
Topics: []gethcommon.Hash{ | ||
crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), | ||
gethcommon.Address{}.Hash(), | ||
s.fundedAccEthAddr.Hash(), | ||
}, | ||
}, | ||
}, | ||
expectEthTx: false, | ||
}, | ||
{ | ||
txInfo: "TX6 - Send erc20 token to coin using precompile, eth tx, logs for transferring tokens to evm module", | ||
logs: []*gethcore.Log{ | ||
// transfer from account to evm module | ||
{ | ||
Address: erc20ContractAddr, | ||
Topics: []gethcommon.Hash{ | ||
crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), | ||
s.fundedAccEthAddr.Hash(), | ||
evm.EVM_MODULE_ADDRESS.Hash(), | ||
}, | ||
}, | ||
}, | ||
expectEthTx: true, | ||
}, | ||
} | ||
|
||
// Getting block results. Note: txs could be included in more than one block | ||
blockNumber := blockNumFirstTx.Int64() | ||
blockRes, err := s.backend.TendermintBlockResultByNumber(&blockNumber) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(blockRes) | ||
txIndex := 0 | ||
ethTxIndex := 0 | ||
for idx, check := range checks { | ||
if txIndex+1 > len(blockRes.TxsResults) { | ||
blockNumber++ | ||
if blockNumber > blockNumLastTx.Int64() { | ||
s.Fail("TX %d not found in block results", idx) | ||
} | ||
txIndex = 0 | ||
ethTxIndex = 0 | ||
blockRes, err = s.backend.TendermintBlockResultByNumber(&blockNumber) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(blockRes) | ||
} | ||
s.assertTxLogsAndTxIndex(blockRes, txIndex, ethTxIndex, check.logs, check.expectEthTx, check.txInfo) | ||
txIndex++ | ||
if check.expectEthTx { | ||
ethTxIndex++ | ||
} | ||
} | ||
} | ||
|
||
// assertTxLogsAndTxIndex gets tx results from the block and checks tx logs and tx index. | ||
func (s *BackendSuite) assertTxLogsAndTxIndex( | ||
blockRes *tmrpctypes.ResultBlockResults, | ||
txIndex int, | ||
ethTxIndex int, | ||
expectedTxLogs []*gethcore.Log, | ||
expectedEthTx bool, | ||
txInfo string, | ||
) { | ||
txResults := blockRes.TxsResults[txIndex] | ||
s.Require().Equal(uint32(0x0), txResults.Code, "tx failed, %s. RawLog: %s", txInfo, txResults.Log) | ||
|
||
events := blockRes.TxsResults[txIndex].Events | ||
|
||
foundEthTx := false | ||
for _, event := range events { | ||
if event.Type == evm.TypeUrlEventTxLog { | ||
logs, err := backend.ParseTxLogsFromEvent(event) | ||
s.Require().NoError(err) | ||
if len(expectedTxLogs) > 0 { | ||
s.Require().GreaterOrEqual(len(logs), len(expectedTxLogs)) | ||
s.assertTxLogsMatch(expectedTxLogs, logs, txInfo) | ||
} else { | ||
s.Require().Nil(logs) | ||
} | ||
} | ||
if event.Type == evm.TypeUrlEventEthereumTx { | ||
foundEthTx = true | ||
if !expectedEthTx { | ||
s.Fail("unexpected EventEthereumTx event for non-eth tx, %s", txInfo) | ||
} | ||
ethereumTx, err := evm.EventEthereumTxFromABCIEvent(event) | ||
s.Require().NoError(err) | ||
s.Require().Equal( | ||
fmt.Sprintf("%d", ethTxIndex), | ||
ethereumTx.Index, | ||
"tx index mismatch, %s", txInfo, | ||
) | ||
} | ||
} | ||
if expectedEthTx && !foundEthTx { | ||
s.Fail("expected EventEthereumTx event not found, %s", txInfo) | ||
} | ||
} | ||
|
||
// assertTxLogsMatch checks that actual tx logs include the expected logs | ||
func (s *BackendSuite) assertTxLogsMatch( | ||
expectedLogs []*gethcore.Log, | ||
actualLogs []*gethcore.Log, | ||
txInfo string, | ||
) { | ||
for idx, expectedLog := range expectedLogs { | ||
actualLog := actualLogs[idx] | ||
s.Require().Equal( | ||
expectedLog.Address, | ||
actualLog.Address, fmt.Sprintf("log contract address mismatch, log index %d, %s", idx, txInfo), | ||
) | ||
|
||
s.Require().Equal( | ||
len(expectedLog.Topics), | ||
len(actualLog.Topics), | ||
fmt.Sprintf("topics length mismatch, log index %d, %s", idx, txInfo), | ||
) | ||
|
||
for idx, topic := range expectedLog.Topics { | ||
s.Require().Equal( | ||
topic, | ||
actualLog.Topics[idx], | ||
fmt.Sprintf("topic mismatch, log index %d, %s", idx, txInfo), | ||
) | ||
} | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add timeout handling for transaction receipt waiting.
The
WaitForReceipt
calls could potentially hang if transactions are never mined. Consider adding a timeout mechanism to fail fast in such scenarios.