Skip to content

Commit 74bd7a6

Browse files
committed
Merge branch 'feature/firehose-tracer-at-latest-release-tag' (v6.1.10) into release/firehose
2 parents e1c1b29 + 3cc5175 commit 74bd7a6

34 files changed

Lines changed: 10726 additions & 115 deletions

app/app.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,9 +2005,15 @@ func (app *App) checkTotalBlockGas(ctx sdk.Context, txs [][]byte) bool {
20052005

20062006
totalGasWanted += gasWanted
20072007

2008-
// If the gas estimate is set and at least 21k (the minimum gas needed for an EVM tx), use the gas estimate.
2009-
// Otherwise, use the gas wanted. Typically the gas estimate is set for EVM txs and not set for Cosmos txs.
2008+
// If the gas estimate is set and at least 21k (the minimum gas needed for an EVM tx)
2009+
// and less than or equal to the tx gas limit, use the gas estimate. Otherwise, use gasWanted.
2010+
useEstimate := false
20102011
if decodedTx.GetGasEstimate() >= MinGasEVMTx {
2012+
if decodedTx.GetGasEstimate() <= gasWanted {
2013+
useEstimate = true
2014+
}
2015+
}
2016+
if useEstimate {
20112017
totalGas += decodedTx.GetGasEstimate()
20122018
} else {
20132019
totalGas += gasWanted

app/app_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,13 @@ func TestInvalidProposalWithExcessiveGasEstimates(t *testing.T) {
341341
txs: []TxType{{isEVM: true, gasEstimate: 30000, gasWanted: 30000}},
342342
expectedStatus: abci.ResponseProcessProposal_REJECT,
343343
},
344+
{
345+
name: "single tx: ignore evm gas estimate above maxBlockGas and use gasWanted (accept)",
346+
maxBlockGas: 20000,
347+
maxBlockGasWanted: math.MaxInt64,
348+
txs: []TxType{{isEVM: true, gasEstimate: 30000, gasWanted: 15000}},
349+
expectedStatus: abci.ResponseProcessProposal_ACCEPT,
350+
},
344351
{
345352
name: "accept when total cosmos tx gas limit is below block gas limit",
346353
maxBlockGas: 20000,

app/test_helpers.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package app
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"os"
8+
"path/filepath"
79
"testing"
810
"time"
911

@@ -16,13 +18,17 @@ import (
1618
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
1719
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
1820
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
21+
ssconfig "github.com/sei-protocol/sei-db/config"
22+
"github.com/sei-protocol/sei-db/ss"
23+
seidbtypes "github.com/sei-protocol/sei-db/ss/types"
1924
"github.com/stretchr/testify/suite"
2025
abci "github.com/tendermint/tendermint/abci/types"
2126
"github.com/tendermint/tendermint/config"
2227
"github.com/tendermint/tendermint/libs/log"
2328
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
2429
dbm "github.com/tendermint/tm-db"
2530

31+
"github.com/sei-protocol/sei-chain/evmrpc"
2632
minttypes "github.com/sei-protocol/sei-chain/x/mint/types"
2733
)
2834

@@ -60,6 +66,9 @@ func (t TestAppOpts) Get(s string) interface{} {
6066
if s == FlagSCEnable {
6167
return t.useSc
6268
}
69+
if s == evmrpc.FlagFlushReceiptSync {
70+
return true
71+
}
6372
return nil
6473
}
6574

@@ -176,14 +185,41 @@ func (s *TestWrapper) EndBlock() {
176185
s.App.EndBlocker(s.Ctx, reqEndBlock)
177186
}
178187

188+
func setupReceiptStore() (seidbtypes.StateStore, error) {
189+
// Create a unique temporary directory per test process to avoid Pebble DB lock conflicts
190+
baseDir := filepath.Join(DefaultNodeHome, "test", "sei-testing")
191+
if err := os.MkdirAll(baseDir, 0o755); err != nil {
192+
return nil, err
193+
}
194+
tempDir, err := os.MkdirTemp(baseDir, "receipt.db-*")
195+
if err != nil {
196+
return nil, err
197+
}
198+
199+
ssConfig := ssconfig.DefaultStateStoreConfig()
200+
ssConfig.DedicatedChangelog = true
201+
ssConfig.KeepRecent = 0 // No min retain blocks in test
202+
ssConfig.DBDirectory = tempDir
203+
ssConfig.KeepLastVersion = false
204+
receiptStore, err := ss.NewStateStore(log.NewNopLogger(), tempDir, ssConfig)
205+
if err != nil {
206+
return nil, err
207+
}
208+
return receiptStore, nil
209+
}
210+
179211
func Setup(isCheckTx bool, enableEVMCustomPrecompiles bool, overrideWasmGasMultiplier bool, baseAppOptions ...func(*baseapp.BaseApp)) (res *App) {
180212
db := dbm.NewMemDB()
181213
encodingConfig := MakeEncodingConfig()
182214
cdc := encodingConfig.Marshaler
183215

184216
options := []AppOption{
185217
func(app *App) {
186-
app.receiptStore = NewInMemoryStateStore()
218+
receiptStore, err := setupReceiptStore()
219+
if err != nil {
220+
panic(fmt.Sprintf("error while creating receipt store: %s", err))
221+
}
222+
app.receiptStore = receiptStore
187223
},
188224
}
189225
wasmOpts := EmptyWasmOpts

contracts/src/ERC20PreTransferFromWrapper.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,9 @@ contract ERC20PreTransferFromWrapper {
2727
require(wrapped.transferFrom(from, to, amount), "Transfer from failed");
2828
return true;
2929
}
30+
<<<<<<< HEAD
3031
}
32+
||||||| 813c350f2
33+
=======
34+
}
35+
>>>>>>> v6.1.10
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
const {
2+
setupSigners, deployErc20PointerForCw20, getAdmin, deployWasm, WASM, ABI
3+
} = require("./lib");
4+
const { expect } = require("chai");
5+
6+
describe("Wrapped CW20 Pointer", function () {
7+
let accounts;
8+
let admin;
9+
let cw20Address;
10+
let cw20EvmPointerAddress;
11+
let cw20EvmPointerContract;
12+
13+
// We have a link ERC20 -> CW20, this contract wraps the ERC20 to add an extra log when doing
14+
// either a transfer or a transferFrom. An approval is required before transferFrom can be done
15+
// as ERC20 rules.
16+
//
17+
// This extra log tests the case where a DeliverTxHook adds logs to a transaction that already contains
18+
// log.
19+
let wrappedCw20EvmPointerAddress;
20+
let wrappedCw20EvmPointerContract;
21+
let deployment;
22+
23+
const balances = {
24+
admin: 1000000,
25+
account0: 2000000,
26+
account1: 3000000
27+
}
28+
29+
before(async function () {
30+
deployment = {}
31+
32+
accounts = await setupSigners(await hre.ethers.getSigners());
33+
34+
if (deployment.admin) {
35+
admin = deployment.admin;
36+
} else {
37+
admin = deployment.admin = await getAdmin();
38+
}
39+
40+
if (deployment.cw20Address) {
41+
cw20Address = deployment.cw20Address;
42+
} else {
43+
cw20Address = deployment.cw20Address = await deployWasm(WASM.CW20, accounts[0].seiAddress, "cw20", {
44+
name: "Test",
45+
symbol: "TEST",
46+
decimals: 6,
47+
initial_balances: [
48+
{ address: admin.seiAddress, amount: "1000000" },
49+
{ address: accounts[0].seiAddress, amount: "2000000" },
50+
{ address: accounts[1].seiAddress, amount: "3000000" }
51+
],
52+
mint: {
53+
"minter": admin.seiAddress, "cap": "99900000000"
54+
}
55+
});
56+
}
57+
58+
if (deployment.cw20EvmPointerAddress) {
59+
cw20EvmPointerAddress = deployment.cw20EvmPointerAddress;
60+
} else {
61+
cw20EvmPointerAddress = deployment.cw20EvmPointerAddress = await deployErc20PointerForCw20(hre.ethers.provider, cw20Address, 5);
62+
}
63+
64+
contract = new hre.ethers.Contract(cw20EvmPointerAddress, ABI.ERC20, hre.ethers.provider);
65+
cw20EvmPointerContract = await contract.connect(accounts[0].signer);
66+
67+
if (deployment.wrappedCw20EvmPointerAddress) {
68+
wrappedCw20EvmPointerAddress = deployment.wrappedCw20EvmPointerAddress;
69+
contract = new hre.ethers.Contract(wrappedCw20EvmPointerAddress, ABI.ERC20, hre.ethers.provider);
70+
wrappedCw20EvmPointerContract = await contract.connect(accounts[0].signer);
71+
} else {
72+
contract = await ethers.getContractFactory("ERC20PreTransferFromWrapper")
73+
wrappedCw20EvmPointerContract = await contract.deploy(await cw20EvmPointerAddress, { gasPrice: ethers.parseUnits('100', 'gwei') });
74+
await wrappedCw20EvmPointerContract.waitForDeployment();
75+
wrappedCw20EvmPointerAddress = deployment.wrappedCw20EvmPointerAddress = await wrappedCw20EvmPointerContract.getAddress();
76+
}
77+
78+
});
79+
80+
it("CW20 transfer performed through ERC20 pointer contract, multiple trx bridged per block", async function () {
81+
let sender = accounts[0];
82+
let recipient = accounts[1];
83+
84+
expect(await cw20EvmPointerContract.balanceOf(sender.evmAddress)).to.equal(
85+
balances.account0
86+
);
87+
expect(await cw20EvmPointerContract.balanceOf(recipient.evmAddress)).to.equal(
88+
balances.account1
89+
);
90+
91+
const txCount = 15;
92+
93+
const tx = await cw20EvmPointerContract.approve(wrappedCw20EvmPointerAddress, txCount);
94+
await tx.wait();
95+
96+
const nonce = await ethers.provider.getTransactionCount(
97+
sender.evmAddress
98+
);
99+
100+
const transfer = async (index) => {
101+
let tx
102+
try {
103+
tx = await wrappedCw20EvmPointerContract.transferFrom(sender.evmAddress, recipient.evmAddress, 1, {
104+
nonce: nonce + (index - 1),
105+
gasPrice: ethers.parseUnits('100', 'gwei')
106+
});
107+
} catch (error) {
108+
console.log(`Transfer ${index} send transaction failed`, error);
109+
throw error;
110+
}
111+
112+
let receipt
113+
try {
114+
receipt = await tx.wait();
115+
} catch (error) {
116+
console.log(`Transfer ${index} send transaction failed`, error);
117+
throw error;
118+
}
119+
};
120+
121+
let promises = [];
122+
for (let i = 1; i <= txCount; i++) {
123+
promises.push(transfer(i));
124+
}
125+
126+
const blockNumber = await ethers.provider.getBlockNumber();
127+
128+
await Promise.all(promises);
129+
130+
expect(await cw20EvmPointerContract.balanceOf(sender.evmAddress)).to.equal(
131+
balances.account0 - txCount
132+
);
133+
expect(await cw20EvmPointerContract.balanceOf(recipient.evmAddress)).to.equal(
134+
balances.account1 + txCount
135+
);
136+
137+
// check logs
138+
const filter = {
139+
fromBlock: blockNumber,
140+
toBlock: "latest",
141+
address: await cw20EvmPointerContract.getAddress(),
142+
topics: [ethers.id("Transfer(address,address,uint256)")],
143+
};
144+
145+
const logs = await ethers.provider.getLogs(filter);
146+
expect(logs.length).to.equal(txCount);
147+
148+
/** @type Record<number, Record<string, []unknown>> */
149+
const byBlockThenTx = {};
150+
logs.forEach((log) => {
151+
if (!byBlockThenTx[log.blockNumber]) {
152+
byBlockThenTx[log.blockNumber] = {};
153+
}
154+
155+
if (!byBlockThenTx[log.blockNumber][log.transactionHash]) {
156+
byBlockThenTx[log.blockNumber][log.transactionHash] = [];
157+
}
158+
159+
byBlockThenTx[log.blockNumber][log.transactionHash].push(log);
160+
});
161+
162+
// Sanity check to ensure we were able to generate a block with multiple logs
163+
expect(
164+
Object.entries(byBlockThenTx).some(
165+
([blockNumber, byTx]) => {
166+
const logCountInBlock = Object.values(byTx).reduce((logCount, logsInTx) => logCount + logsInTx.length, 0)
167+
168+
return logCountInBlock > 1
169+
}
170+
)
171+
).to.be.true;
172+
173+
Object.entries(byBlockThenTx).forEach(
174+
([blockNumber, byTx]) => {
175+
Object.entries(byTx).forEach(
176+
([txHash, logs]) => {
177+
const logIndexes = {}
178+
logs.forEach((log, index) => {
179+
expect(logIndexes[log.index], `all log indexes in block tx ${txHash} (at block #${blockNumber}) should be unique but log's Index value ${log.index} for log at position ${index} has already been seen`).to.be.undefined;
180+
logIndexes[log.index] = index
181+
})
182+
}
183+
)
184+
}
185+
)
186+
187+
const cleanupTx = await cw20EvmPointerContract
188+
.connect(recipient.signer)
189+
.transfer(sender.evmAddress, txCount);
190+
await cleanupTx.wait();
191+
});
192+
});

evmrpc/block.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,10 @@ func (a *BlockAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.Block
253253
)
254254
for i, hash := range txHashes {
255255
wg.Add(1)
256-
go func(i int, hash common.Hash) {
256+
go func(i int, hash typedTxHash) {
257257
defer wg.Done()
258258
defer recoverAndLog()
259-
receipt, err := a.keeper.GetReceipt(a.ctxProvider(height), hash)
259+
receipt, err := a.keeper.GetReceipt(a.ctxProvider(height), hash.hash)
260260
if err != nil {
261261
// When the transaction doesn't exist, skip it
262262
if !strings.Contains(err.Error(), "not found") {
@@ -265,16 +265,17 @@ func (a *BlockAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.Block
265265
mtx.Unlock()
266266
}
267267
} else {
268-
if isReceiptFromAnteError(sdkCtxAtHeight, receipt) {
268+
if receipt.Status == 0 && isReceiptFromAnteError(sdkCtxAtHeight, receipt) {
269269
return
270270
}
271-
// If the receipt has synthetic logs, we actually want to include them in the response.
272-
if !a.includeShellReceipts && receipt.TxType == types.ShellEVMTxType {
271+
272+
if !a.includeShellReceipts && !hash.isEvm {
273273
return
274274
}
275275
// tx hash is included in a future block (because it failed in the current block due to
276276
// checks before the account's nonce is updated)
277277
if receipt.BlockNumber != uint64(height) {
278+
// this shouldn't be possible given isReceiptFromAnteError check above
278279
return
279280
}
280281
encodedReceipt, err := encodeReceipt(sdkCtxAtHeight, receipt, a.txConfigProvider(height).TxDecoder(), block, func(h common.Hash) *types.Receipt {

0 commit comments

Comments
 (0)