Skip to content

Commit

Permalink
Exclude only shell receipts from eth endpoints v5.9.0 (#1879)
Browse files Browse the repository at this point in the history
* Exclude only shell receipts from eth endpoints

* remove it.only
  • Loading branch information
jewei1997 authored Oct 1, 2024
1 parent e742d5e commit f3e5fdf
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 50 deletions.
50 changes: 35 additions & 15 deletions contracts/test/ERC20toCW20PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ describe("ERC20 to CW20 Pointer", function () {
expect(await pointer.balanceOf(sender.evmAddress)).to.equal(balances.account0);
expect(await pointer.balanceOf(recipient.evmAddress)).to.equal(balances.account1);

const blockNumber = await ethers.provider.getBlockNumber();
const tx = await pointer.transfer(recipient.evmAddress, 1);
await tx.wait();
const receipt = await tx.wait();
const blockNumber = receipt.blockNumber;

expect(await pointer.balanceOf(sender.evmAddress)).to.equal(balances.account0-1);
expect(await pointer.balanceOf(recipient.evmAddress)).to.equal(balances.account1+1);
Expand All @@ -107,18 +107,38 @@ describe("ERC20 to CW20 Pointer", function () {
address: await pointer.getAddress(),
topics: [ethers.id("Transfer(address,address,uint256)")]
};
// send via eth_ endpoint - synthetic event doesn't show up
// send via eth_ endpoint - synthetic event should show up because we are using the
// synthetic event in place of a real EVM event
const ethlogs = await ethers.provider.send('eth_getLogs', [filter]);
expect(ethlogs.length).to.equal(0);
expect(ethlogs.length).to.equal(1);

// send via sei_ endpoint - synthetic event shows up
const seilogs = await ethers.provider.send('sei_getLogs', [filter]);
expect(seilogs.length).to.equal(1);
expect(seilogs.length).to.equal(1);
expect(seilogs[0]["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(seilogs[0]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(seilogs[0]["topics"][1].substring(26)).to.equal(sender.evmAddress.substring(2).toLowerCase());
expect(seilogs[0]["topics"][2].substring(26)).to.equal(recipient.evmAddress.substring(2).toLowerCase());

const logs = [...ethlogs, ...seilogs];
logs.forEach(async (log) => {
expect(log["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(log["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(log["topics"][1].substring(26)).to.equal(sender.evmAddress.substring(2).toLowerCase());
expect(log["topics"][2].substring(26)).to.equal(recipient.evmAddress.substring(2).toLowerCase());
});

const ethBlock = await ethers.provider.send('eth_getBlockByNumber', ['0x' + blockNumber.toString(16), false]);
const seiBlock = await ethers.provider.send('sei_getBlockByNumber', ['0x' + blockNumber.toString(16), false]);
expect(ethBlock.transactions.length).to.equal(1);
expect(seiBlock.transactions.length).to.equal(1);

const ethReceipts = await ethers.provider.send('eth_getBlockReceipts', ['0x' + blockNumber.toString(16)]);
const seiReceipts = await ethers.provider.send('sei_getBlockReceipts', ['0x' + blockNumber.toString(16)]);
expect(ethReceipts.length).to.equal(1);
expect(seiReceipts.length).to.equal(1);
expect(ethReceipts[0].transactionHash).to.equal(seiReceipts[0].transactionHash);

const ethTx = await ethers.provider.send('eth_getTransactionReceipt', [receipt.hash]);
expect(ethTx.logs.length).to.equal(1); // check for transfer event
const ethTxByHash = await ethers.provider.send('eth_getTransactionByHash', [tx.hash]);
expect(ethTxByHash).to.not.be.null;

const cleanupTx = await pointer.connect(recipient.signer).transfer(sender.evmAddress, 1);
await cleanupTx.wait();
Expand Down Expand Up @@ -147,7 +167,7 @@ describe("ERC20 to CW20 Pointer", function () {
const spender = accounts[1].evmAddress;
const blockNumber = await ethers.provider.getBlockNumber();
const tx = await pointer.approve(spender, 1000000);
await tx.wait();
const receipt = await tx.wait();
const allowance = await pointer.allowance(owner, spender);
expect(Number(allowance)).to.equal(1000000);

Expand All @@ -160,15 +180,15 @@ describe("ERC20 to CW20 Pointer", function () {
};
// send via eth_ endpoint - synthetic event doesn't show up
const ethlogs = await ethers.provider.send('eth_getLogs', [filter]);
expect(ethlogs.length).to.equal(0);
expect(ethlogs.length).to.equal(1);
expect(ethlogs[0]["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(ethlogs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)"));
expect(ethlogs[0]["topics"][1].substring(26)).to.equal(owner.substring(2).toLowerCase());
expect(ethlogs[0]["topics"][2].substring(26)).to.equal(spender.substring(2).toLowerCase());

// send via sei_ endpoint - synthetic event shows up
const seilogs = await ethers.provider.send('sei_getLogs', [filter]);
expect(seilogs.length).to.equal(1);
expect(seilogs[0]["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(seilogs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)"));
expect(seilogs[0]["topics"][1].substring(26)).to.equal(owner.substring(2).toLowerCase());
expect(seilogs[0]["topics"][2].substring(26)).to.equal(spender.substring(2).toLowerCase());
});

it("should lower approval", async function () {
Expand Down
45 changes: 32 additions & 13 deletions contracts/test/ERC721toCW721PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,22 @@ describe("ERC721 to CW721 Pointer", function () {
address: await pointerAcc1.getAddress(),
topics: [ethers.id("Approval(address,address,uint256)")]
};
// send via eth_ endpoint - synthetic event doesn't show up
// send via eth_ endpoint - synthetic event should show up because we are using the
// synthetic event in place of a real EVM event
const ethlogs = await ethers.provider.send('eth_getLogs', [filter]);
expect(ethlogs.length).to.equal(0);
expect(ethlogs.length).to.equal(1);

// send via sei_ endpoint - synthetic event shows up
const seilogs = await ethers.provider.send('sei_getLogs', [filter]);
expect(seilogs.length).to.equal(1);
expect(seilogs[0]["address"].toLowerCase()).to.equal((await pointerAcc1.getAddress()).toLowerCase());
expect(seilogs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)"));
expect(seilogs[0]["topics"][1].substring(26)).to.equal(accounts[0].evmAddress.substring(2).toLowerCase());
expect(seilogs[0]["topics"][2].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());

const logs = [...ethlogs, ...seilogs];
logs.forEach(async (log) => {
expect(log["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(log["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(log["topics"][1].substring(26)).to.equal(accounts[0].evmAddress.substring(2).toLowerCase());
expect(log["topics"][2].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
});
});

it("cannot approve token you don't own", async function () {
Expand All @@ -116,7 +121,7 @@ describe("ERC721 to CW721 Pointer", function () {
await mine(pointerAcc0.approve(accounts[1].evmAddress, 2));
const blockNumber = await ethers.provider.getBlockNumber();
transferTxResp = await pointerAcc1.transferFrom(accounts[0].evmAddress, accounts[1].evmAddress, 2);
await transferTxResp.wait();
const receipt = await transferTxResp.wait();
const filter = {
fromBlock: '0x' + blockNumber.toString(16),
toBlock: 'latest',
Expand All @@ -125,19 +130,33 @@ describe("ERC721 to CW721 Pointer", function () {
};
// send via eth_ endpoint - synthetic event doesn't show up
const ethlogs = await ethers.provider.send('eth_getLogs', [filter]);
expect(ethlogs.length).to.equal(0);

expect(ethlogs.length).to.equal(1);
const seilogs = await ethers.provider.send('sei_getLogs', [filter]);
expect(seilogs.length).to.equal(1);
expect(seilogs[0]["address"].toLowerCase()).to.equal((await pointerAcc1.getAddress()).toLowerCase());
expect(seilogs[0]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(seilogs[0]["topics"][1].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
expect(seilogs[0]["topics"][2].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
const logs = [...ethlogs, ...seilogs];
logs.forEach(async (log) => {
expect(log["address"].toLowerCase()).to.equal((await pointerAcc1.getAddress()).toLowerCase());
expect(log["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(log["topics"][1].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
expect(log["topics"][2].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
});

const balance0 = await pointerAcc0.balanceOf(accounts[0].evmAddress);
expect(balance0).to.equal(0);
const balance1 = await pointerAcc0.balanceOf(accounts[1].evmAddress);
expect(balance1).to.equal(2);

// do same for eth_getBlockReceipts and sei_getBlockReceipts
const ethBlockReceipts = await ethers.provider.send('eth_getBlockReceipts', ['0x' + blockNumber.toString(16)]);
expect(ethBlockReceipts.length).to.equal(1);
const seiBlockReceipts = await ethers.provider.send('sei_getBlockReceipts', ['0x' + blockNumber.toString(16)]);
expect(seiBlockReceipts.length).to.equal(1);

const ethTx = await ethers.provider.send('eth_getTransactionReceipt', [receipt.hash]);
expect(ethTx.logs.length).to.equal(1);
const ethTxByHash = await ethers.provider.send('eth_getTransactionByHash', [receipt.hash]);
expect(ethTxByHash).to.not.be.null;

// return token id 2 back to accounts[0] using safe transfer from
await mine(pointerAcc1.approve(accounts[0].evmAddress, 2));
await mine(pointerAcc1.safeTransferFrom(accounts[1].evmAddress, accounts[0].evmAddress, 2));
Expand Down
29 changes: 18 additions & 11 deletions evmrpc/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"math"
"math/big"
"strings"
"sync"
Expand All @@ -26,18 +27,20 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
)

const ShellEVMTxType = math.MaxUint32

type BlockAPI struct {
tmClient rpcclient.Client
keeper *keeper.Keeper
ctxProvider func(int64) sdk.Context
txConfig client.TxConfig
connectionType ConnectionType
namespace string
includeSyntheticTxs bool
tmClient rpcclient.Client
keeper *keeper.Keeper
ctxProvider func(int64) sdk.Context
txConfig client.TxConfig
connectionType ConnectionType
namespace string
includeShellReceipts bool
}

func NewBlockAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfig client.TxConfig, connectionType ConnectionType, namespace string) *BlockAPI {
return &BlockAPI{tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, txConfig: txConfig, connectionType: connectionType, includeSyntheticTxs: shouldIncludeSynthetic(namespace)}
return &BlockAPI{tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, txConfig: txConfig, connectionType: connectionType, includeShellReceipts: shouldIncludeSynthetic(namespace)}
}

func (a *BlockAPI) GetBlockTransactionCountByNumber(ctx context.Context, number rpc.BlockNumber) (result *hexutil.Uint, returnErr error) {
Expand Down Expand Up @@ -80,7 +83,7 @@ func (a *BlockAPI) getBlockByHash(ctx context.Context, blockHash common.Hash, fu
return nil, err
}
blockBloom := a.keeper.GetBlockBloom(a.ctxProvider(block.Block.Height))
return EncodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, blockBloom, a.keeper, a.txConfig.TxDecoder(), fullTx, a.includeSyntheticTxs)
return EncodeTmBlock(a.ctxProvider(block.Block.Height), block, blockRes, blockBloom, a.keeper, a.txConfig.TxDecoder(), fullTx, a.includeShellReceipts)
}

func (a *BlockAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (result map[string]interface{}, returnErr error) {
Expand Down Expand Up @@ -128,7 +131,7 @@ func (a *BlockAPI) getBlockByNumber(ctx context.Context, number rpc.BlockNumber,
return nil, err
}
blockBloom := a.keeper.GetBlockBloom(a.ctxProvider(block.Block.Height))
return EncodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, blockBloom, a.keeper, a.txConfig.TxDecoder(), fullTx, a.includeSyntheticTxs)
return EncodeTmBlock(a.ctxProvider(block.Block.Height), block, blockRes, blockBloom, a.keeper, a.txConfig.TxDecoder(), fullTx, a.includeShellReceipts)
}

func (a *BlockAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (result []map[string]interface{}, returnErr error) {
Expand Down Expand Up @@ -169,7 +172,8 @@ func (a *BlockAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.Block
mtx.Unlock()
}
} else {
if !a.includeSyntheticTxs && len(receipt.Logs) > 0 && receipt.Logs[0].Synthetic {
// If the receipt has synthetic logs, we actually want to include them in the response.
if !a.includeShellReceipts && receipt.TxType == ShellEVMTxType {
return
}
encodedReceipt, err := encodeReceipt(receipt, a.txConfig.TxDecoder(), block, func(h common.Hash) bool {
Expand Down Expand Up @@ -242,6 +246,9 @@ func EncodeTmBlock(
if err != nil {
continue
}
if !includeSyntheticTxs && receipt.TxType == ShellEVMTxType {
continue
}
newTx := ethapi.NewRPCTransaction(ethtx, blockhash, number.Uint64(), uint64(blockTime.Second()), uint64(receipt.TransactionIndex), baseFeePerGas, chainConfig)
transactions = append(transactions, newTx)
}
Expand Down
9 changes: 8 additions & 1 deletion evmrpc/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,18 @@ func TestGetBlockReceipts(t *testing.T) {
// Query by tag latest => retrieves block 8
resObj3 := sendRequestGood(t, "getBlockReceipts", "latest")
result = resObj3["result"].([]interface{})
require.Equal(t, 1, len(result))
require.Equal(t, 2, len(result))
receipt1 = result[0].(map[string]interface{})
require.Equal(t, "0x8", receipt1["blockNumber"])
require.Equal(t, "0x0", receipt1["transactionIndex"])
require.Equal(t, multiTxBlockTx4.Hash().Hex(), receipt1["transactionHash"])
receiptWithSyntheticLog := result[1].(map[string]interface{})
require.Equal(t, "0x8", receiptWithSyntheticLog["blockNumber"])
logs := receiptWithSyntheticLog["logs"].([]interface{})
firstLog := logs[0].(map[string]interface{})
topics := firstLog["topics"].([]interface{})
syntheticLogFirstTopic := "0x0000000000000000000000000000000000000000000000000000000000000234"
require.Equal(t, syntheticLogFirstTopic, topics[0].(string))
}

func verifyGenesisBlockResult(t *testing.T, resObj map[string]interface{}) {
Expand Down
17 changes: 9 additions & 8 deletions evmrpc/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type EventItemDataWrapper struct {
}

func NewFilterAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, filterConfig *FilterConfig, connectionType ConnectionType, namespace string) *FilterAPI {
logFetcher := &LogFetcher{tmClient: tmClient, k: k, ctxProvider: ctxProvider, filterConfig: filterConfig, includeSynthetic: shouldIncludeSynthetic(namespace)}
logFetcher := &LogFetcher{tmClient: tmClient, k: k, ctxProvider: ctxProvider, filterConfig: filterConfig, includeSyntheticReceipts: shouldIncludeSynthetic(namespace)}
filters := make(map[ethrpc.ID]filter)
api := &FilterAPI{
namespace: namespace,
Expand Down Expand Up @@ -275,11 +275,11 @@ func (a *FilterAPI) UninstallFilter(
}

type LogFetcher struct {
tmClient rpcclient.Client
k *keeper.Keeper
ctxProvider func(int64) sdk.Context
filterConfig *FilterConfig
includeSynthetic bool
tmClient rpcclient.Client
k *keeper.Keeper
ctxProvider func(int64) sdk.Context
filterConfig *FilterConfig
includeSyntheticReceipts bool
}

func (f *LogFetcher) GetLogsByFilters(ctx context.Context, crit filters.FilterCriteria, lastToHeight int64) ([]*ethtypes.Log, int64, error) {
Expand Down Expand Up @@ -366,10 +366,11 @@ func (f *LogFetcher) FindLogsByBloom(height int64, filters [][]bloomIndexes) (re
ctx.Logger().Error(fmt.Sprintf("FindLogsByBloom: unable to find receipt for hash %s", hash.Hex()))
continue
}
if !f.includeSynthetic && len(receipt.Logs) > 0 && receipt.Logs[0].Synthetic {
// if includeShellReceipts is false, include receipts with synthetic logs but exclude shell tx receipts
if !f.includeSyntheticReceipts && receipt.TxType == ShellEVMTxType {
continue
}
if !f.includeSynthetic && receipt.EffectiveGasPrice == 0 {
if !f.includeSyntheticReceipts && receipt.EffectiveGasPrice == 0 {
return
}
if len(receipt.LogsBloom) > 0 && MatchFilters(ethtypes.Bloom(receipt.LogsBloom), filters) {
Expand Down
4 changes: 2 additions & 2 deletions evmrpc/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func TestSeiFilterGetLogs(t *testing.T) {
})
}

func TestEthEndpointShouldNotReturnSyntheticLogs(t *testing.T) {
func TestEthEndpointCanReturnSyntheticLogs(t *testing.T) {
testFilterGetLogs(t, "eth", []GetFilterLogTests{
{
name: "filter by single topic with default range, exclude synethetic logs",
Expand All @@ -230,7 +230,7 @@ func TestEthEndpointShouldNotReturnSyntheticLogs(t *testing.T) {
check: func(t *testing.T, log map[string]interface{}) {
require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000234", log["topics"].([]interface{})[0].(string))
},
wantLen: 0,
wantLen: 1,
},
})
}
Expand Down

0 comments on commit f3e5fdf

Please sign in to comment.