Skip to content

Commit

Permalink
Synthetic events in separate sei endpoints (#1867)
Browse files Browse the repository at this point in the history
* remove synthetic events from eth_ endpoints & fix param reading

* add sei_ specific endpts for block, tx, filter isn't working

* filter logs should work with synethetic endpts

* fix namespace for metrics

* fix complilation

* fix

* fix CW20ToERC20Pointer - should transfer token

* fix ERC20toCW20Pointer failing tests

* minor fix

* revert tx.go changes

* fix ERC721ToCW721Pointer tests

---------

Co-authored-by: codchen <codchen03@gmail.com>
  • Loading branch information
jewei1997 and codchen authored Sep 24, 2024
1 parent 8cfd890 commit 4890dcc
Show file tree
Hide file tree
Showing 11 changed files with 456 additions and 133 deletions.
17 changes: 12 additions & 5 deletions contracts/test/CW20toERC20PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const {getAdmin, queryWasm, executeWasm, associateWasm, deployEvmContract, setup
proposeCW20toERC20Upgrade
} = require("./lib");
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("CW20 to ERC20 Pointer", function () {
let accounts;
Expand Down Expand Up @@ -102,18 +103,24 @@ describe("CW20 to ERC20 Pointer", function () {
const txHash = res["txhash"];
const receipt = await ethers.provider.getTransactionReceipt(`0x${txHash}`);
expect(receipt).not.to.be.null;
console.log("receipt[\"blockNumber\"]", receipt["blockNumber"]);
const bn = receipt["blockNumber"];
const filter = {
fromBlock: receipt["blockNumber"],
fromBlock: '0x' + bn.toString(16),
toBlock: 'latest',
address: receipt["to"],
topics: [ethers.id("Transfer(address,address,uint256)")]
};
const logs = await ethers.provider.getLogs(filter);
expect(logs.length).to.equal(1);
expect(logs[0]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
// 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);

// 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]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
const respAfter = await queryWasm(pointer, "balance", {address: accounts[1].seiAddress});
const balanceAfter = respAfter.data.balance;

expect(balanceAfter).to.equal((parseInt(balanceBefore) + 100).toString());
});

Expand Down
39 changes: 25 additions & 14 deletions contracts/test/ERC20toCW20PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,23 @@ describe("ERC20 to CW20 Pointer", function () {

// check logs
const filter = {
fromBlock: blockNumber,
fromBlock: '0x' + blockNumber.toString(16),
toBlock: 'latest',
address: await pointer.getAddress(),
topics: [ethers.id("Transfer(address,address,uint256)")]
};
const logs = await ethers.provider.getLogs(filter);
expect(logs.length).to.equal(1);
expect(logs[0]["address"]).to.equal(await pointer.getAddress());
expect(logs[0]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(logs[0]["topics"][1].substring(26)).to.equal(sender.evmAddress.substring(2).toLowerCase());
expect(logs[0]["topics"][2].substring(26)).to.equal(recipient.evmAddress.substring(2).toLowerCase());
// 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);

// 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 cleanupTx = await pointer.connect(recipient.signer).transfer(sender.evmAddress, 1);
await cleanupTx.wait();
Expand Down Expand Up @@ -147,17 +153,22 @@ describe("ERC20 to CW20 Pointer", function () {

// check logs
const filter = {
fromBlock: blockNumber,
fromBlock: '0x' + blockNumber.toString(16),
toBlock: 'latest',
address: await pointer.getAddress(),
topics: [ethers.id("Approval(address,address,uint256)")]
};
const logs = await ethers.provider.getLogs(filter);
expect(logs.length).to.equal(1);
expect(logs[0]["address"]).to.equal(await pointer.getAddress());
expect(logs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)"));
expect(logs[0]["topics"][1].substring(26)).to.equal(owner.substring(2).toLowerCase());
expect(logs[0]["topics"][2].substring(26)).to.equal(spender.substring(2).toLowerCase());
// 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);

// 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
37 changes: 23 additions & 14 deletions contracts/test/ERC721toCW721PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,22 @@ describe("ERC721 to CW721 Pointer", function () {
expect(approved).to.equal(accounts[1].evmAddress);

const filter = {
fromBlock: blockNumber,
fromBlock: '0x' + blockNumber.toString(16),
toBlock: 'latest',
address: await pointerAcc1.getAddress(),
topics: [ethers.id("Approval(address,address,uint256)")]
};
const logs = await ethers.provider.getLogs(filter);
expect(logs.length).to.equal(1);
expect(logs[0]["address"]).to.equal(await pointerAcc1.getAddress());
expect(logs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)"));
expect(logs[0]["topics"][1].substring(26)).to.equal(accounts[0].evmAddress.substring(2).toLowerCase());
expect(logs[0]["topics"][2].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
// 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);

// 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());
});

it("cannot approve token you don't own", async function () {
Expand All @@ -113,17 +118,21 @@ describe("ERC721 to CW721 Pointer", function () {
transferTxResp = await pointerAcc1.transferFrom(accounts[0].evmAddress, accounts[1].evmAddress, 2);
await transferTxResp.wait();
const filter = {
fromBlock: blockNumber,
fromBlock: '0x' + blockNumber.toString(16),
toBlock: 'latest',
address: await pointerAcc1.getAddress(),
topics: [ethers.id("Transfer(address,address,uint256)")]
};
const logs = await ethers.provider.getLogs(filter);
expect(logs.length).to.equal(1);
expect(logs[0]["address"]).to.equal(await pointerAcc1.getAddress());
expect(logs[0]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(logs[0]["topics"][1].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
expect(logs[0]["topics"][2].substring(26)).to.equal(accounts[1].evmAddress.substring(2).toLowerCase());
// 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);

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 balance0 = await pointerAcc0.balanceOf(accounts[0].evmAddress);
expect(balance0).to.equal(0);
const balance1 = await pointerAcc0.balanceOf(accounts[1].evmAddress);
Expand Down
102 changes: 87 additions & 15 deletions evmrpc/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package evmrpc

import (
"context"
"crypto/sha256"
"errors"
"fmt"
"math/big"
"strings"
"sync"
"time"

wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -23,20 +26,22 @@ import (
)

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

func NewBlockAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfig client.TxConfig, connectionType ConnectionType) *BlockAPI {
return &BlockAPI{tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, txConfig: txConfig, connectionType: connectionType}
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)}
}

func (a *BlockAPI) GetBlockTransactionCountByNumber(ctx context.Context, number rpc.BlockNumber) (result *hexutil.Uint, returnErr error) {
startTime := time.Now()
defer recordMetrics("eth_getBlockTransactionCountByNumber", a.connectionType, startTime, returnErr == nil)
defer recordMetrics(fmt.Sprintf("%s_getBlockTransactionCountByNumber", a.namespace), a.connectionType, startTime, returnErr == nil)
numberPtr, err := getBlockNumber(ctx, a.tmClient, number)
if err != nil {
return nil, err
Expand All @@ -50,7 +55,7 @@ func (a *BlockAPI) GetBlockTransactionCountByNumber(ctx context.Context, number

func (a *BlockAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) (result *hexutil.Uint, returnErr error) {
startTime := time.Now()
defer recordMetrics("eth_getBlockTransactionCountByHash", a.connectionType, startTime, returnErr == nil)
defer recordMetrics(fmt.Sprintf("%s_getBlockTransactionCountByHash", a.namespace), a.connectionType, startTime, returnErr == nil)
block, err := blockByHashWithRetry(ctx, a.tmClient, blockHash[:], 1)
if err != nil {
return nil, err
Expand All @@ -60,7 +65,11 @@ func (a *BlockAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash

func (a *BlockAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (result map[string]interface{}, returnErr error) {
startTime := time.Now()
defer recordMetrics("eth_getBlockByHash", a.connectionType, startTime, returnErr == nil)
defer recordMetrics(fmt.Sprintf("%s_getBlockByHash", a.namespace), a.connectionType, startTime, returnErr == nil)
return a.getBlockByHash(ctx, blockHash, fullTx)
}

func (a *BlockAPI) getBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (result map[string]interface{}, returnErr error) {
block, err := blockByHashWithRetry(ctx, a.tmClient, blockHash[:], 1)
if err != nil {
return nil, err
Expand All @@ -70,12 +79,41 @@ 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)
return EncodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, blockBloom, a.keeper, a.txConfig.TxDecoder(), fullTx, a.includeSyntheticTxs)
}

func (a *BlockAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (result map[string]interface{}, returnErr error) {
startTime := time.Now()
defer recordMetrics("eth_getBlockByNumber", a.connectionType, startTime, returnErr == nil)
defer recordMetrics(fmt.Sprintf("%s_getBlockByNumber", a.namespace), a.connectionType, startTime, returnErr == nil)
if number == 0 {
// for compatibility with the graph, always return genesis block
return map[string]interface{}{
"number": (*hexutil.Big)(big.NewInt(0)),
"hash": common.HexToHash("F9D3845DF25B43B1C6926F3CEDA6845C17F5624E12212FD8847D0BA01DA1AB9E"),
"parentHash": common.Hash{},
"nonce": ethtypes.BlockNonce{}, // inapplicable to Sei
"mixHash": common.Hash{}, // inapplicable to Sei
"sha3Uncles": ethtypes.EmptyUncleHash, // inapplicable to Sei
"logsBloom": ethtypes.Bloom{},
"stateRoot": common.Hash{},
"miner": common.Address{},
"difficulty": (*hexutil.Big)(big.NewInt(0)), // inapplicable to Sei
"extraData": hexutil.Bytes{}, // inapplicable to Sei
"gasLimit": hexutil.Uint64(0),
"gasUsed": hexutil.Uint64(0),
"timestamp": hexutil.Uint64(0),
"transactionsRoot": common.Hash{},
"receiptsRoot": common.Hash{},
"size": hexutil.Uint64(0),
"uncles": []common.Hash{}, // inapplicable to Sei
"transactions": []interface{}{},
"baseFeePerGas": (*hexutil.Big)(big.NewInt(0)),
}, nil
}
return a.getBlockByNumber(ctx, number, fullTx)
}

func (a *BlockAPI) getBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (result map[string]interface{}, returnErr error) {
numberPtr, err := getBlockNumber(ctx, a.tmClient, number)
if err != nil {
return nil, err
Expand All @@ -89,12 +127,12 @@ 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)
return EncodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, blockBloom, a.keeper, a.txConfig.TxDecoder(), fullTx, a.includeSyntheticTxs)
}

func (a *BlockAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (result []map[string]interface{}, returnErr error) {
startTime := time.Now()
defer recordMetrics("eth_getBlockReceipts", a.connectionType, startTime, returnErr == nil)
defer recordMetrics(fmt.Sprintf("%s_getBlockReceipts", a.namespace), a.connectionType, startTime, returnErr == nil)
// Get height from params
heightPtr, err := GetBlockNumberByNrOrHash(ctx, a.tmClient, blockNrOrHash)
if err != nil {
Expand Down Expand Up @@ -130,6 +168,9 @@ func (a *BlockAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.Block
mtx.Unlock()
}
} else {
if !a.includeSyntheticTxs && len(receipt.Logs) > 0 && receipt.Logs[0].Synthetic {
return
}
encodedReceipt, err := encodeReceipt(receipt, a.txConfig.TxDecoder(), block, func(h common.Hash) bool {
_, err := a.keeper.GetReceipt(a.ctxProvider(height), h)
return err == nil
Expand All @@ -144,10 +185,16 @@ func (a *BlockAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.Block
}(i, hash)
}
wg.Wait()
compactReceipts := make([]map[string]interface{}, 0)
for _, r := range allReceipts {
if len(r) > 0 {
compactReceipts = append(compactReceipts, r)
}
}
if returnErr != nil {
return nil, returnErr
}
return allReceipts, nil
return compactReceipts, nil
}

func EncodeTmBlock(
Expand All @@ -158,6 +205,7 @@ func EncodeTmBlock(
k *keeper.Keeper,
txDecoder sdk.TxDecoder,
fullTx bool,
includeSyntheticTxs bool,
) (map[string]interface{}, error) {
number := big.NewInt(block.Block.Height)
blockhash := common.HexToHash(block.BlockID.Hash.String())
Expand Down Expand Up @@ -196,6 +244,30 @@ func EncodeTmBlock(
newTx := ethapi.NewRPCTransaction(ethtx, blockhash, number.Uint64(), uint64(blockTime.Second()), uint64(receipt.TransactionIndex), baseFeePerGas, chainConfig)
transactions = append(transactions, newTx)
}
case *wasmtypes.MsgExecuteContract:
if !includeSyntheticTxs {
continue
}
th := sha256.Sum256(block.Block.Txs[i])
receipt, err := k.GetReceipt(ctx, th)
if err != nil {
continue
}
if !fullTx {
transactions = append(transactions, th)
} else {
ti := uint64(receipt.TransactionIndex)
to := k.GetEVMAddressOrDefault(ctx, sdk.MustAccAddressFromBech32(m.Contract))
transactions = append(transactions, &ethapi.RPCTransaction{
BlockHash: &blockhash,
BlockNumber: (*hexutil.Big)(number),
From: common.HexToAddress(receipt.From),
To: &to,
Input: m.Msg.Bytes(),
Hash: th,
TransactionIndex: (*hexutil.Uint64)(&ti),
})
}
}
}
}
Expand Down
Loading

0 comments on commit 4890dcc

Please sign in to comment.