From a1d260e83818f8d3991910b87b238afe96af3130 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 21 Nov 2023 14:04:46 -0800 Subject: [PATCH 01/13] fix: ci: fetch params for the storage unit tests In #11212, we added the necessary logic to download the params automatically. However, we're not taking advantage of CI caching. Here, we configure CI to cache these params. --- .circleci/config.yml | 8 ++------ .circleci/template.yml | 13 ++++++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e91c41129..20701f7d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -996,23 +996,20 @@ workflows: - build suite: utest-unit-cli target: "./cli/... ./cmd/... ./api/..." - get-params: true executor: golang-2xl + get-params: true - test: name: test-unit-node requires: - build suite: utest-unit-node target: "./node/..." - - - test: name: test-unit-rest requires: - build suite: utest-unit-rest target: "./blockstore/... ./build/... ./chain/... ./conformance/... ./gateway/... ./journal/... ./lib/... ./markets/... ./paychmgr/... ./tools/..." - executor: golang-2xl - test: name: test-unit-storage @@ -1020,8 +1017,7 @@ workflows: - build suite: utest-unit-storage target: "./storage/... ./extern/..." - - + get-params: true - test: go-test-flags: "-run=TestMulticoreSDR" requires: diff --git a/.circleci/template.yml b/.circleci/template.yml index 71616f05f..0b244d013 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -557,9 +557,16 @@ workflows: - build suite: utest-[[ $suite ]] target: "[[ $pkgs ]]" - [[if eq $suite "unit-cli"]]get-params: true[[end]] - [[if eq $suite "unit-cli"]]executor: golang-2xl[[end]] - [[- if eq $suite "unit-rest"]]executor: golang-2xl[[end]] + [[- if eq $suite "unit-storage"]] + get-params: true + [[- end -]] + [[- if eq $suite "unit-cli"]] + executor: golang-2xl + get-params: true + [[- end -]] + [[- if eq $suite "unit-rest"]] + executor: golang-2xl + [[- end -]] [[- end]] - test: go-test-flags: "-run=TestMulticoreSDR" From 3407848e8c4daf58bcdebae31e6855c46efe7491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 23 Nov 2023 21:19:16 +0100 Subject: [PATCH 02/13] fix: miner info: Show correct sector state counts --- CHANGELOG.md | 1 + storage/pipeline/stats.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 664fa3778..05caea4f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - chore: Auto remove local chain data when importing chain file or snapshot ([filecoin-project/lotus#11277](https://github.com/filecoin-project/lotus/pull/11277)) - feat: metric: export Mpool message count ([filecoin-project/lotus#11361](https://github.com/filecoin-project/lotus/pull/11361)) - feat: sealing: load SectorsSummary from sealing SectorStats instead of calling API each time ([filecoin-project/lotus#11353](https://github.com/filecoin-project/lotus/pull/11353)) +- fix: miner info: Show correct sector state counts ([filecoin-project/lotus#11456](https://github.com/filecoin-project/lotus/pull/11456)) ## Improvements - fix: Add time slicing to splitstore purging step during compaction to reduce lock congestion [filecoin-project/lotus#11269](https://github.com/filecoin-project/lotus/pull/11269) diff --git a/storage/pipeline/stats.go b/storage/pipeline/stats.go index 696cd56a5..a558421ae 100644 --- a/storage/pipeline/stats.go +++ b/storage/pipeline/stats.go @@ -44,6 +44,10 @@ func (ss *SectorStats) updateSector(ctx context.Context, cfg sealiface.Config, i ss.totals[toStatState(oldst, cfg.FinalizeEarly)]-- ss.byState[oldst]-- + if ss.byState[oldst] <= 0 { + delete(ss.byState, oldst) + } + mctx, _ := tag.New(ctx, tag.Upsert(metrics.SectorState, string(oldst))) stats.Record(mctx, metrics.SectorStates.M(ss.byState[oldst])) } From 231b2070d4bf9d1492536817454c3b7467738d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 25 Nov 2023 22:29:57 +0100 Subject: [PATCH 03/13] feat: chain import: don't walk to genesis - 2-3x faster snapshot import (#11446) * chain import: don't walk to genesis * fix daemon build * fast snapshot genesis: address review --- chain/store/index_test.go | 2 +- chain/store/snapshot.go | 46 +++++++++++++++++++++++++------- chain/store/store_test.go | 6 ++--- cmd/lotus-bench/import.go | 2 +- cmd/lotus-shed/genesis-verify.go | 2 +- cmd/lotus/daemon.go | 12 +++------ 6 files changed, 47 insertions(+), 23 deletions(-) diff --git a/chain/store/index_test.go b/chain/store/index_test.go index a3a4ad6ce..3cde40062 100644 --- a/chain/store/index_test.go +++ b/chain/store/index_test.go @@ -42,7 +42,7 @@ func TestIndexSeeks(t *testing.T) { cs := store.NewChainStore(nbs, nbs, syncds.MutexWrap(datastore.NewMapDatastore()), filcns.Weight, nil) defer cs.Close() //nolint:errcheck - _, err = cs.Import(ctx, bytes.NewReader(gencar)) + _, _, err = cs.Import(ctx, bytes.NewReader(gencar)) if err != nil { t.Fatal(err) } diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index 301a5f87b..de2190c5d 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -60,7 +60,7 @@ func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRo }) } -func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, error) { +func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (head *types.TipSet, genesis *types.BlockHeader, err error) { // TODO: writing only to the state blockstore is incorrect. // At this time, both the state and chain blockstores are backed by the // universal store. When we physically segregate the stores, we will need @@ -69,7 +69,7 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e br, err := carv2.NewBlockReader(r) if err != nil { - return nil, xerrors.Errorf("loadcar failed: %w", err) + return nil, nil, xerrors.Errorf("loadcar failed: %w", err) } s := cs.StateBlockstore() @@ -80,27 +80,51 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e putThrottle <- nil } + if len(br.Roots) == 0 { + return nil, nil, xerrors.Errorf("no roots in snapshot car file") + } + nextTailCid := br.Roots[0] + + var tailBlock types.BlockHeader + tailBlock.Height = abi.ChainEpoch(-1) + var buf []blocks.Block for { blk, err := br.Next() if err != nil { + + // we're at the end if err == io.EOF { if len(buf) > 0 { if err := s.PutMany(ctx, buf); err != nil { - return nil, err + return nil, nil, err } } break } - return nil, err + return nil, nil, err } + // check for header block, looking for genesis + if blk.Cid() == nextTailCid && tailBlock.Height != 0 { + if err := tailBlock.UnmarshalCBOR(bytes.NewReader(blk.RawData())); err != nil { + return nil, nil, xerrors.Errorf("failed to unmarshal genesis block: %w", err) + } + if len(tailBlock.Parents) > 0 { + nextTailCid = tailBlock.Parents[0] + } else { + // note: even the 0th block has a parent linking to the cbor genesis block + return nil, nil, xerrors.Errorf("current block (epoch %d cid %s) has no parents", tailBlock.Height, tailBlock.Cid()) + } + } + + // append to batch buf = append(buf, blk) if len(buf) > 1000 { if lastErr := <-putThrottle; lastErr != nil { // consume one error to have the right to add one - return nil, lastErr + return nil, nil, lastErr } go func(buf []blocks.Block) { @@ -113,13 +137,17 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e // check errors for i := 0; i < parallelPuts; i++ { if lastErr := <-putThrottle; lastErr != nil { - return nil, lastErr + return nil, nil, lastErr } } + if tailBlock.Height != 0 { + return nil, nil, xerrors.Errorf("expected genesis block to have height 0 (genesis), got %d: %s", tailBlock.Height, tailBlock.Cid()) + } + root, err := cs.LoadTipSet(ctx, types.NewTipSetKey(br.Roots...)) if err != nil { - return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err) + return nil, nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err) } ts := root @@ -135,10 +163,10 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e } if err := cs.PersistTipsets(ctx, tssToPersist); err != nil { - return nil, xerrors.Errorf("failed to persist tipsets: %w", err) + return nil, nil, xerrors.Errorf("failed to persist tipsets: %w", err) } - return root, nil + return root, &tailBlock, nil } type walkSchedTaskType int diff --git a/chain/store/store_test.go b/chain/store/store_test.go index 9c717fdbe..1ecfc474a 100644 --- a/chain/store/store_test.go +++ b/chain/store/store_test.go @@ -118,7 +118,7 @@ func TestChainExportImport(t *testing.T) { cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), filcns.Weight, nil) defer cs.Close() //nolint:errcheck - root, err := cs.Import(context.TODO(), buf) + root, _, err := cs.Import(context.TODO(), buf) if err != nil { t.Fatal(err) } @@ -153,7 +153,7 @@ func TestChainImportTipsetKeyCid(t *testing.T) { cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), filcns.Weight, nil) defer cs.Close() //nolint:errcheck - root, err := cs.Import(ctx, buf) + root, _, err := cs.Import(ctx, buf) require.NoError(t, err) require.Truef(t, root.Equals(last), "imported chain differed from exported chain") @@ -202,7 +202,7 @@ func TestChainExportImportFull(t *testing.T) { cs := store.NewChainStore(nbs, nbs, ds, filcns.Weight, nil) defer cs.Close() //nolint:errcheck - root, err := cs.Import(context.TODO(), buf) + root, _, err := cs.Import(context.TODO(), buf) if err != nil { t.Fatal(err) } diff --git a/cmd/lotus-bench/import.go b/cmd/lotus-bench/import.go index 95b91054a..16adbad60 100644 --- a/cmd/lotus-bench/import.go +++ b/cmd/lotus-bench/import.go @@ -304,7 +304,7 @@ var importBenchCmd = &cli.Command{ return fmt.Errorf("no CAR file provided for import") } - head, err = cs.Import(cctx.Context, carFile) + head, _, err = cs.Import(cctx.Context, carFile) if err != nil { return err } diff --git a/cmd/lotus-shed/genesis-verify.go b/cmd/lotus-shed/genesis-verify.go index 6795f1528..4ead8467e 100644 --- a/cmd/lotus-shed/genesis-verify.go +++ b/cmd/lotus-shed/genesis-verify.go @@ -62,7 +62,7 @@ var genesisVerifyCmd = &cli.Command{ return xerrors.Errorf("opening the car file: %w", err) } - ts, err := cs.Import(cctx.Context, f) + ts, _, err := cs.Import(cctx.Context, f) if err != nil { return err } diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 58c326586..44da4139a 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -565,7 +565,7 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) } bar.Start() - ts, err := cst.Import(ctx, ir) + ts, gen, err := cst.Import(ctx, ir) bar.Finish() if err != nil { @@ -576,18 +576,14 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) return xerrors.Errorf("flushing validation cache failed: %w", err) } - gb, err := cst.GetTipsetByHeight(ctx, 0, ts, true) - if err != nil { - return err - } - - err = cst.SetGenesis(ctx, gb.Blocks()[0]) + log.Infof("setting genesis") + err = cst.SetGenesis(ctx, gen) if err != nil { return err } if !snapshot { - shd, err := drand.BeaconScheduleFromDrandSchedule(build.DrandConfigSchedule(), gb.MinTimestamp(), nil) + shd, err := drand.BeaconScheduleFromDrandSchedule(build.DrandConfigSchedule(), gen.Timestamp, nil) if err != nil { return xerrors.Errorf("failed to construct beacon schedule: %w", err) } From 00535fe3eece9592f541142f92b5b578559b220d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 27 Nov 2023 18:30:46 +0100 Subject: [PATCH 04/13] itests: TestSectorsSummary --- itests/sector_pledge_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/itests/sector_pledge_test.go b/itests/sector_pledge_test.go index 1e045c79d..b4e5c1133 100644 --- a/itests/sector_pledge_test.go +++ b/itests/sector_pledge_test.go @@ -223,3 +223,29 @@ func TestPledgeSynth(t *testing.T) { runTest(t, 3) }) } + +func TestSectorsSummary(t *testing.T) { + kit.QuietMiningLogs() + + blockTime := 50 * time.Millisecond + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + nPreseal := 2 + + _, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.PresealSectors(nPreseal)) + ens.InterconnectAll().BeginMining(blockTime) + + miner.PledgeSectors(ctx, 1, 0, nil) + + ms, err := miner.SectorsSummary(ctx) + require.NoError(t, err) + + require.Len(t, ms, 1) // all proving + + for st, n := range ms { + require.Equal(t, api.SectorState(sealing.Proving), st) + require.Equal(t, 1+nPreseal, n) + } +} From 5519b15d690275584a81b5a3a940220aba64ea9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 27 Nov 2023 23:36:29 +0100 Subject: [PATCH 05/13] feat: bench: flag to output GenerateWinningPoStWithVanilla params --- cmd/lotus-bench/simple.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/lotus-bench/simple.go b/cmd/lotus-bench/simple.go index 8341c4b53..31fcdc4c3 100644 --- a/cmd/lotus-bench/simple.go +++ b/cmd/lotus-bench/simple.go @@ -658,6 +658,10 @@ var simpleWinningPost = &cli.Command{ Usage: "pass miner address (only necessary if using existing sectorbuilder)", Value: "t01000", }, + &cli.BoolFlag{ + Name: "output-inputs", + Usage: "output inputs for winning post generation", + }, }, ArgsUsage: "[sealed] [cache] [comm R] [sector num]", Action: func(cctx *cli.Context) error { @@ -728,6 +732,17 @@ var simpleWinningPost = &cli.Command{ fmt.Printf("Vanilla %s (%s)\n", challenge.Sub(start), bps(sectorSize, 1, challenge.Sub(start))) fmt.Printf("Proof %s (%s)\n", end.Sub(challenge), bps(sectorSize, 1, end.Sub(challenge))) fmt.Println(base64.StdEncoding.EncodeToString(proof[0].ProofBytes)) + + if cctx.Bool("output-inputs") { + fmt.Println("GenerateWinningPoStWithVanilla info:") + + fmt.Printf(" wpt: %d\n", wpt) + fmt.Printf(" mid: %d\n", mid) + fmt.Printf(" rand: %x\n", rand) + fmt.Printf(" vp: %x\n", vp) + fmt.Printf(" proof: %x\n", proof) + } + return nil }, } From 7214abfa3f26bb1e29452cb27b07349b4de8ea10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 28 Nov 2023 11:24:10 +0100 Subject: [PATCH 06/13] bench: --show-inputs instead of --output-inputs --- cmd/lotus-bench/simple.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-bench/simple.go b/cmd/lotus-bench/simple.go index 31fcdc4c3..09df22078 100644 --- a/cmd/lotus-bench/simple.go +++ b/cmd/lotus-bench/simple.go @@ -659,7 +659,7 @@ var simpleWinningPost = &cli.Command{ Value: "t01000", }, &cli.BoolFlag{ - Name: "output-inputs", + Name: "show-inputs", Usage: "output inputs for winning post generation", }, }, @@ -733,7 +733,7 @@ var simpleWinningPost = &cli.Command{ fmt.Printf("Proof %s (%s)\n", end.Sub(challenge), bps(sectorSize, 1, end.Sub(challenge))) fmt.Println(base64.StdEncoding.EncodeToString(proof[0].ProofBytes)) - if cctx.Bool("output-inputs") { + if cctx.Bool("show-inputs") { fmt.Println("GenerateWinningPoStWithVanilla info:") fmt.Printf(" wpt: %d\n", wpt) From 717d2a0eebf0e53647b91a83eecf2a8a97b11e41 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Tue, 28 Nov 2023 14:49:06 +0000 Subject: [PATCH 07/13] Add block param to EthEstimateGas --- CHANGELOG.md | 1 + api/api_full.go | 6 +++--- api/api_gateway.go | 2 +- api/mocks/mock_full.go | 8 ++++---- api/proxy_gen.go | 16 ++++++++-------- build/openrpc/full.json.gz | Bin 34831 -> 34835 bytes build/openrpc/gateway.json.gz | Bin 11883 -> 11887 bytes documentation/en/api-v1-unstable-methods.md | 3 ++- gateway/node.go | 2 +- gateway/proxy_eth.go | 4 ++-- itests/eth_account_abstraction_test.go | 2 +- itests/eth_conformance_test.go | 6 +++--- itests/eth_deploy_test.go | 2 +- itests/eth_hash_lookup_test.go | 4 ++-- itests/eth_transactions_test.go | 6 +++--- itests/fevm_test.go | 6 +++--- node/impl/full/dummy.go | 2 +- node/impl/full/eth.go | 10 +++++++--- 18 files changed, 43 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05caea4f3..38b7d2f38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - feat: metric: export Mpool message count ([filecoin-project/lotus#11361](https://github.com/filecoin-project/lotus/pull/11361)) - feat: sealing: load SectorsSummary from sealing SectorStats instead of calling API each time ([filecoin-project/lotus#11353](https://github.com/filecoin-project/lotus/pull/11353)) - fix: miner info: Show correct sector state counts ([filecoin-project/lotus#11456](https://github.com/filecoin-project/lotus/pull/11456)) +- feat: add support for specifying block number when calling `eth_estimateGas` RPC number which s ## Improvements - fix: Add time slicing to splitstore purging step during compaction to reduce lock congestion [filecoin-project/lotus#11269](https://github.com/filecoin-project/lotus/pull/11269) diff --git a/api/api_full.go b/api/api_full.go index f919bc13b..8a23131af 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -823,9 +823,9 @@ type FullNode interface { EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read - EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) //perm:read - EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) //perm:read + EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read + EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) //perm:read + EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) //perm:read EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) //perm:read diff --git a/api/api_gateway.go b/api/api_gateway.go index 27e725457..63489f31b 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -114,7 +114,7 @@ type Gateway interface { EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) + EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 856d83813..f27aa6f79 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1042,18 +1042,18 @@ func (mr *MockFullNodeMockRecorder) EthChainId(arg0 interface{}) *gomock.Call { } // EthEstimateGas mocks base method. -func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 ethtypes.EthCall, arg2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1) + ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1, arg2) ret0, _ := ret[0].(ethtypes.EthUint64) ret1, _ := ret[1].(error) return ret0, ret1 } // EthEstimateGas indicates an expected call of EthEstimateGas. -func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1, arg2) } // EthFeeHistory mocks base method. diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 2d1333495..3c53ba57b 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -255,7 +255,7 @@ type FullNodeMethods struct { EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` - EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `perm:"read"` + EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) `perm:"read"` EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `perm:"read"` @@ -679,7 +679,7 @@ type GatewayMethods struct { EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `` - EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `` + EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) `` EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `` @@ -2134,14 +2134,14 @@ func (s *FullNodeStub) EthChainId(p0 context.Context) (ethtypes.EthUint64, error return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { if s.Internal.EthEstimateGas == nil { return *new(ethtypes.EthUint64), ErrNotSupported } - return s.Internal.EthEstimateGas(p0, p1) + return s.Internal.EthEstimateGas(p0, p1, p2) } -func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { return *new(ethtypes.EthUint64), ErrNotSupported } @@ -4400,14 +4400,14 @@ func (s *GatewayStub) EthChainId(p0 context.Context) (ethtypes.EthUint64, error) return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *GatewayStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (s *GatewayStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { if s.Internal.EthEstimateGas == nil { return *new(ethtypes.EthUint64), ErrNotSupported } - return s.Internal.EthEstimateGas(p0, p1) + return s.Internal.EthEstimateGas(p0, p1, p2) } -func (s *GatewayStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (s *GatewayStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { return *new(ethtypes.EthUint64), ErrNotSupported } diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 29131b824e6cc4c01ab1560c68c27b6e85587201..cbe2471bb5d7ce80a6bb9103c6920aa7e7fbf061 100644 GIT binary patch delta 16837 zcmV)`Kz_fEkOGsC0^(vsG2=A2Yl9Aw+Ew7-FGfk=I~!;4CJvWPf6knSt zP916g!VH;7rv00L|9gE*yhV&%v&<@snP4F5xrlKo++&n7h77$&f9Mni0`n?sSj^NE zvabxeXOLss+xxyb+!#r+Z(!gG)1@I~$#r9TF;7un7a>B5%wmqnYIu{G3^P%$#-8e= zqd{bUKe5aPn_CsfE|2)^LxkH}NYkk9V%Nbwo*t9h>uXcZneG+HHk-=I6wNDl9w-Xy zVO~_k%t`jFc8WK*e=82qh|h@lKJ*}Ixp3p!4Z=~H?~NHP`k0i}F8lO|@7+o8tb<9c z$@ip8J}cCh6Y9Ga5&l|8iKVcAr&oU~!Jf2WBHTqeDcMza-xmdjG?3p(kESwQ)Mz8Z zGh@4VF>WrC2DhW#owoyLf}9;^b3EB{M&q}`H*cI>xb5z~e;J{-Z+1tU=*OE?Pi%IxmS@-hfWP95Du`h($MWw#Z0g zqZHakDcDmi4PjKw;tg^}X);!sHYypZjfXcwBQ_r1#>3k@Ko2kZht+w2 zk|!uSZW}5ePBj=jr79u4&Y3@<0f1m4tM_9Rh^`Ugz@t+J)U6QIal#-#E?}5A2zZcj z;8V}rxFj#)wEL4{CoCW`ld7d%yxpXxq(QFqpTB-I(_E_Y&fKq0%%?aURp*yy?eE`KkSET6ju8TX=GGsDqfb2uFI`q51V&3y};`dBPo5_(?4NTs;Xd)Po- z9FkIMa6i1=pZZ*dOBBWI*S%9;rfhcCWuLk$*=iNi6H-XqHJOPJ|D-lp4zRm_m)e_I zVza-dMI-g4-PtA=C@<`=L-kcZtd*yPD@>7v(Dq(bdw(x#hjMbKHWk)6rvaRzcSUt) zCKcoQOKtn9Z9RXqip{{7!WHibre`AB78GN$TZ8MhHTY<^l#&sS?B=K4{9NOex~7`& zO>O?L6!_i0+mBZCJK`P_7u~j0kM)f<$}+Yv*zV$Xu`;Zu$52CF+_q4U3unUSBztgc z!&r%~bAQm?BEfr6j+}6o9YkrDomG(}Sru(&c3iEOA zHqSZ(cWZrE>%&?f*7~s4hqXSe^ti&iO~aCb#Y@Cv720VOg|(C+(Qyb? z*?≦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 E0J+AkB>(^b delta 16832 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 diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 1dae12101..377f8b4e6 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2409,7 +2409,8 @@ Inputs: "gasPrice": "0x0", "value": "0x0", "data": "0x07" - } + }, + "string value" ] ``` diff --git a/gateway/node.go b/gateway/node.go index 367e645c1..5c686e774 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -131,7 +131,7 @@ type TargetAPI interface { EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) + EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) diff --git a/gateway/proxy_eth.go b/gateway/proxy_eth.go index e6d433a17..4fc3b2fd6 100644 --- a/gateway/proxy_eth.go +++ b/gateway/proxy_eth.go @@ -353,13 +353,13 @@ func (gw *Node) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt return gw.target.EthMaxPriorityFeePerGas(ctx) } -func (gw *Node) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (gw *Node) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return 0, err } // todo limit gas? to what? - return gw.target.EthEstimateGas(ctx, tx) + return gw.target.EthEstimateGas(ctx, tx, blkParam) } func (gw *Node) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) { diff --git a/itests/eth_account_abstraction_test.go b/itests/eth_account_abstraction_test.go index 8d92d0a04..62c0083a7 100644 --- a/itests/eth_account_abstraction_test.go +++ b/itests/eth_account_abstraction_test.go @@ -275,7 +275,7 @@ func TestEthAccountAbstractionFailsFromEvmActor(t *testing.T) { gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ From: ðAddr, Data: contract, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 15b249977..c43dd7dfa 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -35,7 +35,7 @@ type ethAPIRaw struct { EthBlockNumber func(context.Context) (json.RawMessage, error) EthCall func(context.Context, ethtypes.EthCall, ethtypes.EthBlockNumberOrHash) (json.RawMessage, error) EthChainId func(context.Context) (json.RawMessage, error) - EthEstimateGas func(context.Context, ethtypes.EthCall) (json.RawMessage, error) + EthEstimateGas func(context.Context, ethtypes.EthCall, ethtypes.EthBlockNumberOrHash) (json.RawMessage, error) EthFeeHistory func(context.Context, ethtypes.EthUint64, string, []float64) (json.RawMessage, error) EthGasPrice func(context.Context) (json.RawMessage, error) EthGetBalance func(context.Context, ethtypes.EthAddress, ethtypes.EthBlockNumberOrHash) (json.RawMessage, error) @@ -185,7 +185,7 @@ func TestEthOpenRPCConformance(t *testing.T) { return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ From: &senderEthAddr, Data: contractBin, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) }, }, @@ -451,7 +451,7 @@ func createRawSignedEthTx(ctx context.Context, t *testing.T, client *kit.TestFul gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ From: &senderEthAddr, Data: contractBin, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_deploy_test.go b/itests/eth_deploy_test.go index ce4c94a28..422bc3449 100644 --- a/itests/eth_deploy_test.go +++ b/itests/eth_deploy_test.go @@ -63,7 +63,7 @@ func TestDeployment(t *testing.T) { gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ From: ðAddr, Data: contract, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_hash_lookup_test.go b/itests/eth_hash_lookup_test.go index e1474a90d..dd3980867 100644 --- a/itests/eth_hash_lookup_test.go +++ b/itests/eth_hash_lookup_test.go @@ -50,7 +50,7 @@ func TestTransactionHashLookup(t *testing.T) { gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ From: ðAddr, Data: contract, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -353,7 +353,7 @@ func TestEthGetMessageCidByTransactionHashEthTx(t *testing.T) { gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ From: ðAddr, Data: contract, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 026ac2387..0039ebde0 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -47,7 +47,7 @@ func TestValueTransferValidSignature(t *testing.T) { gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ From: ðAddr, Data: contract, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -235,7 +235,7 @@ func TestContractInvocation(t *testing.T) { From: ðAddr, To: &contractAddr, Data: params, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -353,7 +353,7 @@ func deployContractTx(ctx context.Context, client *kit.TestFullNode, ethAddr eth gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ From: ðAddr, Data: contract, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) if err != nil { return nil, err } diff --git a/itests/fevm_test.go b/itests/fevm_test.go index cb69c036c..7b09ac7f9 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -661,7 +661,7 @@ func TestFEVMRecursiveActorCallEstimate(t *testing.T) { From: ðAddr, To: &contractAddr, Data: params, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) require.LessOrEqual(t, int64(gaslimit), build.BlockGasLimit) @@ -820,7 +820,7 @@ func TestFEVMBareTransferTriggersSmartContractLogic(t *testing.T) { From: &accntEth, To: &contractEth, Value: ethtypes.EthBigInt(big.NewInt(100)), - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -1037,7 +1037,7 @@ func TestFEVMErrorParsing(t *testing.T) { _, err := e.EthEstimateGas(ctx, ethtypes.EthCall{ To: &contractAddrEth, Data: entryPoint, - }) + }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) require.ErrorContains(t, err, expected) }) }) diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index 743eadf34..857b4c57f 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -122,7 +122,7 @@ func (e *EthModuleDummy) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, e return ethtypes.EthBigIntZero, ErrModuleDisabled } -func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { return 0, ErrModuleDisabled } diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 22f78e723..12a9d56ea 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -66,7 +66,7 @@ type EthModuleAPI interface { NetListening(ctx context.Context) (bool, error) EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) + EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) @@ -1007,7 +1007,7 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty return res, nil } -func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) { +func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { msg, err := ethCallToFilecoinMessage(ctx, tx) if err != nil { return ethtypes.EthUint64(0), err @@ -1017,7 +1017,11 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et // gas estimation actually run. msg.GasLimit = 0 - ts := a.Chain.GetHeaviestTipSet() + ts, err := getTipsetByEthBlockNumberOrHash(ctx, a.Chain, blkParam) + if err != nil { + return ethtypes.EthUint64(0), xerrors.Errorf("failed to process block param: %v; %w", blkParam, err) + } + gassedMsg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key()) if err != nil { // On failure, GasEstimateMessageGas doesn't actually return the invocation result, From 6ae91467e6e07de9b6a8185e294cd8b4ac380336 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Tue, 28 Nov 2023 19:39:15 +0000 Subject: [PATCH 08/13] fix spelling --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38b7d2f38..2b987a3c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - feat: metric: export Mpool message count ([filecoin-project/lotus#11361](https://github.com/filecoin-project/lotus/pull/11361)) - feat: sealing: load SectorsSummary from sealing SectorStats instead of calling API each time ([filecoin-project/lotus#11353](https://github.com/filecoin-project/lotus/pull/11353)) - fix: miner info: Show correct sector state counts ([filecoin-project/lotus#11456](https://github.com/filecoin-project/lotus/pull/11456)) -- feat: add support for specifying block number when calling `eth_estimateGas` RPC number which s +- feat: add support for specifying block number when calling `eth_estimateGas` ([filecoin-project/lotus#11462](https://github.com/filecoin-project/lotus/pull/11462)). ## Improvements - fix: Add time slicing to splitstore purging step during compaction to reduce lock congestion [filecoin-project/lotus#11269](https://github.com/filecoin-project/lotus/pull/11269) From a3417e7488356d97ada733ffa27a7df22b6d1d16 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Wed, 29 Nov 2023 10:17:10 +0000 Subject: [PATCH 09/13] Make block param to EthEstimateGas optional --- api/api_full.go | 6 +-- api/api_gateway.go | 2 +- api/mocks/mock_full.go | 8 ++-- api/proxy_gen.go | 16 ++++---- build/openrpc/full.json.gz | Bin 34835 -> 34820 bytes build/openrpc/gateway.json.gz | Bin 11887 -> 11872 bytes chain/types/ethtypes/eth_types.go | 39 ++++++++++++++++++++ documentation/en/api-v1-unstable-methods.md | 10 +---- gateway/node.go | 2 +- gateway/proxy_eth.go | 10 ++++- itests/eth_account_abstraction_test.go | 8 +++- itests/eth_conformance_test.go | 16 +++++--- itests/eth_deploy_test.go | 7 +++- itests/eth_hash_lookup_test.go | 15 ++++++-- itests/eth_transactions_test.go | 24 +++++++++--- itests/fevm_test.go | 23 +++++++++--- node/impl/full/dummy.go | 2 +- node/impl/full/eth.go | 22 ++++++++--- 18 files changed, 150 insertions(+), 60 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 8a23131af..4ae2ea531 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -823,9 +823,9 @@ type FullNode interface { EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read - EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) //perm:read - EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) //perm:read + EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read + EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) //perm:read + EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) //perm:read EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) //perm:read diff --git a/api/api_gateway.go b/api/api_gateway.go index 63489f31b..238bf43ab 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -114,7 +114,7 @@ type Gateway interface { EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) + EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index f27aa6f79..92b719550 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1042,18 +1042,18 @@ func (mr *MockFullNodeMockRecorder) EthChainId(arg0 interface{}) *gomock.Call { } // EthEstimateGas mocks base method. -func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 ethtypes.EthCall, arg2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { +func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 jsonrpc.RawParams) (ethtypes.EthUint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1) ret0, _ := ret[0].(ethtypes.EthUint64) ret1, _ := ret[1].(error) return ret0, ret1 } // EthEstimateGas indicates an expected call of EthEstimateGas. -func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1) } // EthFeeHistory mocks base method. diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 3c53ba57b..8adcbc189 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -255,7 +255,7 @@ type FullNodeMethods struct { EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` - EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) `perm:"read"` + EthEstimateGas func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) `perm:"read"` EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `perm:"read"` @@ -679,7 +679,7 @@ type GatewayMethods struct { EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `` - EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) `` + EthEstimateGas func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) `` EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `` @@ -2134,14 +2134,14 @@ func (s *FullNodeStub) EthChainId(p0 context.Context) (ethtypes.EthUint64, error return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { +func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) { if s.Internal.EthEstimateGas == nil { return *new(ethtypes.EthUint64), ErrNotSupported } - return s.Internal.EthEstimateGas(p0, p1, p2) + return s.Internal.EthEstimateGas(p0, p1) } -func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { +func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) { return *new(ethtypes.EthUint64), ErrNotSupported } @@ -4400,14 +4400,14 @@ func (s *GatewayStub) EthChainId(p0 context.Context) (ethtypes.EthUint64, error) return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *GatewayStruct) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { +func (s *GatewayStruct) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) { if s.Internal.EthEstimateGas == nil { return *new(ethtypes.EthUint64), ErrNotSupported } - return s.Internal.EthEstimateGas(p0, p1, p2) + return s.Internal.EthEstimateGas(p0, p1) } -func (s *GatewayStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { +func (s *GatewayStub) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) { return *new(ethtypes.EthUint64), ErrNotSupported } diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index cbe2471bb5d7ce80a6bb9103c6920aa7e7fbf061..99426649275b903af8f200301ae779290cfda9cb 100644 GIT binary patch delta 17048 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 diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index b796e6f56..b933329f4 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -799,6 +799,45 @@ func GetContractEthAddressFromCode(sender EthAddress, salt [32]byte, initcode [] return ethAddr, nil } +// EthEstimateGasParams handles raw jsonrpc params for eth_estimateGas +type EthEstimateGasParams struct { + Tx EthCall + BlkParam *EthBlockNumberOrHash +} + +func (e *EthEstimateGasParams) UnmarshalJSON(b []byte) error { + var params []json.RawMessage + err := json.Unmarshal(b, ¶ms) + if err != nil { + return err + } + + switch len(params) { + case 2: + err = json.Unmarshal(params[1], &e.BlkParam) + if err != nil { + return err + } + fallthrough + case 1: + err = json.Unmarshal(params[0], &e.Tx) + if err != nil { + return err + } + default: + return xerrors.Errorf("expected 1 or 2 params, got %d", len(params)) + } + + return nil +} + +func (e EthEstimateGasParams) MarshalJSON() ([]byte, error) { + if e.BlkParam != nil { + return json.Marshal([]interface{}{e.Tx, e.BlkParam}) + } + return json.Marshal([]interface{}{e.Tx}) +} + // EthFeeHistoryParams handles raw jsonrpc params for eth_feeHistory type EthFeeHistoryParams struct { BlkCount EthUint64 diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 377f8b4e6..c2929f0f5 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2402,15 +2402,7 @@ Perms: read Inputs: ```json [ - { - "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "gas": "0x5", - "gasPrice": "0x0", - "value": "0x0", - "data": "0x07" - }, - "string value" + "Bw==" ] ``` diff --git a/gateway/node.go b/gateway/node.go index 5c686e774..f2464d274 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -131,7 +131,7 @@ type TargetAPI interface { EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) + EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) diff --git a/gateway/proxy_eth.go b/gateway/proxy_eth.go index 4fc3b2fd6..13a12acfa 100644 --- a/gateway/proxy_eth.go +++ b/gateway/proxy_eth.go @@ -353,13 +353,19 @@ func (gw *Node) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt return gw.target.EthMaxPriorityFeePerGas(ctx) } -func (gw *Node) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { +func (gw *Node) EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) { + // validate params + _, err := jsonrpc.DecodeParams[ethtypes.EthEstimateGasParams](p) + if err != nil { + return ethtypes.EthUint64(0), xerrors.Errorf("decoding params: %w", err) + } + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return 0, err } // todo limit gas? to what? - return gw.target.EthEstimateGas(ctx, tx, blkParam) + return gw.target.EthEstimateGas(ctx, p) } func (gw *Node) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) { diff --git a/itests/eth_account_abstraction_test.go b/itests/eth_account_abstraction_test.go index 62c0083a7..5ca672674 100644 --- a/itests/eth_account_abstraction_test.go +++ b/itests/eth_account_abstraction_test.go @@ -3,6 +3,7 @@ package itests import ( "context" "encoding/hex" + "encoding/json" "os" "testing" "time" @@ -272,10 +273,13 @@ func TestEthAccountAbstractionFailsFromEvmActor(t *testing.T) { contract, err := hex.DecodeString(string(contractHex)) require.NoError(t, err) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, Data: contract, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index c43dd7dfa..9c1b2ae34 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -35,7 +35,7 @@ type ethAPIRaw struct { EthBlockNumber func(context.Context) (json.RawMessage, error) EthCall func(context.Context, ethtypes.EthCall, ethtypes.EthBlockNumberOrHash) (json.RawMessage, error) EthChainId func(context.Context) (json.RawMessage, error) - EthEstimateGas func(context.Context, ethtypes.EthCall, ethtypes.EthBlockNumberOrHash) (json.RawMessage, error) + EthEstimateGas func(context.Context, jsonrpc.RawParams) (json.RawMessage, error) EthFeeHistory func(context.Context, ethtypes.EthUint64, string, []float64) (json.RawMessage, error) EthGasPrice func(context.Context) (json.RawMessage, error) EthGetBalance func(context.Context, ethtypes.EthAddress, ethtypes.EthBlockNumberOrHash) (json.RawMessage, error) @@ -182,10 +182,13 @@ func TestEthOpenRPCConformance(t *testing.T) { { method: "eth_estimateGas", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: &senderEthAddr, Data: contractBin, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + return ethapi.EthEstimateGas(ctx, gasParams) }, }, @@ -448,10 +451,13 @@ func TestEthOpenRPCConformance(t *testing.T) { } func createRawSignedEthTx(ctx context.Context, t *testing.T, client *kit.TestFullNode, senderEthAddr ethtypes.EthAddress, receiverEthAddr ethtypes.EthAddress, senderKey *key.Key, contractBin []byte) []byte { - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: &senderEthAddr, Data: contractBin, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_deploy_test.go b/itests/eth_deploy_test.go index 422bc3449..68861f98f 100644 --- a/itests/eth_deploy_test.go +++ b/itests/eth_deploy_test.go @@ -60,10 +60,13 @@ func TestDeployment(t *testing.T) { // verify the deployer address is an Placeholder. client.AssertActorType(ctx, deployer, manifest.PlaceholderKey) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, Data: contract, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_hash_lookup_test.go b/itests/eth_hash_lookup_test.go index dd3980867..0a2c11d3c 100644 --- a/itests/eth_hash_lookup_test.go +++ b/itests/eth_hash_lookup_test.go @@ -3,6 +3,7 @@ package itests import ( "context" "encoding/hex" + "encoding/json" "os" "testing" "time" @@ -47,10 +48,13 @@ func TestTransactionHashLookup(t *testing.T) { // send some funds to the f410 address kit.SendFunds(ctx, t, client, deployer, types.FromFil(10)) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, Data: contract, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -350,10 +354,13 @@ func TestEthGetMessageCidByTransactionHashEthTx(t *testing.T) { // send some funds to the f410 address kit.SendFunds(ctx, t, client, deployer, types.FromFil(10)) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, Data: contract, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 0039ebde0..59e5852c9 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -3,6 +3,7 @@ package itests import ( "context" "encoding/hex" + "encoding/json" "os" "testing" "time" @@ -44,10 +45,13 @@ func TestValueTransferValidSignature(t *testing.T) { kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000)) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, Data: contract, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -231,11 +235,14 @@ func TestContractInvocation(t *testing.T) { params, err := hex.DecodeString("f8b2cb4f000000000000000000000000ff00000000000000000000000000000000000064") require.NoError(t, err) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, To: &contractAddr, Data: params, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -350,10 +357,15 @@ func TestGetBlockByNumber(t *testing.T) { } func deployContractTx(ctx context.Context, client *kit.TestFullNode, ethAddr ethtypes.EthAddress, contract []byte) (*ethtypes.EthTxArgs, error) { - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, Data: contract, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + if err != nil { + return nil, err + } + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) if err != nil { return nil, err } diff --git a/itests/fevm_test.go b/itests/fevm_test.go index 7b09ac7f9..2dcb8ef1d 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "encoding/binary" "encoding/hex" + "encoding/json" "fmt" "testing" "time" @@ -657,11 +658,15 @@ func TestFEVMRecursiveActorCallEstimate(t *testing.T) { t.Logf("running with %d recursive calls", r) params := makeParams(r) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: ðAddr, To: &contractAddr, Data: params, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) require.LessOrEqual(t, int64(gaslimit), build.BlockGasLimit) @@ -816,11 +821,14 @@ func TestFEVMBareTransferTriggersSmartContractLogic(t *testing.T) { contractEth, err := ethtypes.EthAddressFromFilecoinAddress(contractAddr) require.NoError(t, err) - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ From: &accntEth, To: &contractEth, Value: ethtypes.EthBigInt(big.NewInt(100)), - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, gasParams) require.NoError(t, err) maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) @@ -1034,10 +1042,13 @@ func TestFEVMErrorParsing(t *testing.T) { require.ErrorContains(t, err, expected) }) t.Run("EthEstimateGas", func(t *testing.T) { - _, err := e.EthEstimateGas(ctx, ethtypes.EthCall{ + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ To: &contractAddrEth, Data: entryPoint, - }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) + }}) + require.NoError(t, err) + + _, err = e.EthEstimateGas(ctx, gasParams) require.ErrorContains(t, err, expected) }) }) diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index 857b4c57f..11ff95a63 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -122,7 +122,7 @@ func (e *EthModuleDummy) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, e return ethtypes.EthBigIntZero, ErrModuleDisabled } -func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { +func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) { return 0, ErrModuleDisabled } diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 12a9d56ea..d4298492f 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -66,7 +66,7 @@ type EthModuleAPI interface { NetListening(ctx context.Context) (bool, error) EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) + EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) @@ -1007,8 +1007,13 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty return res, nil } -func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) { - msg, err := ethCallToFilecoinMessage(ctx, tx) +func (a *EthModule) EthEstimateGas(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthUint64, error) { + params, err := jsonrpc.DecodeParams[ethtypes.EthEstimateGasParams](p) + if err != nil { + return ethtypes.EthUint64(0), xerrors.Errorf("decoding params: %w", err) + } + + msg, err := ethCallToFilecoinMessage(ctx, params.Tx) if err != nil { return ethtypes.EthUint64(0), err } @@ -1017,9 +1022,14 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall, blk // gas estimation actually run. msg.GasLimit = 0 - ts, err := getTipsetByEthBlockNumberOrHash(ctx, a.Chain, blkParam) - if err != nil { - return ethtypes.EthUint64(0), xerrors.Errorf("failed to process block param: %v; %w", blkParam, err) + var ts *types.TipSet + if params.BlkParam == nil { + ts = a.Chain.GetHeaviestTipSet() + } else { + ts, err = getTipsetByEthBlockNumberOrHash(ctx, a.Chain, *params.BlkParam) + if err != nil { + return ethtypes.EthUint64(0), xerrors.Errorf("failed to process block param: %v; %w", params.BlkParam, err) + } } gassedMsg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key()) From afa95681af0bebb261df1efc9eaef1b367e1331e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2023 16:01:26 +0400 Subject: [PATCH 10/13] fix: chain: make failure to load the chain state fatal (#11426) Otherwise, we get strange panics later. --- node/modules/chain.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/modules/chain.go b/node/modules/chain.go index d08b7dbec..70cb9b585 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -82,12 +82,12 @@ func ChainStore(lc fx.Lifecycle, basebs dtypes.BaseBlockstore, weight store.WeightFunc, us stmgr.UpgradeSchedule, - j journal.Journal) *store.ChainStore { + j journal.Journal) (*store.ChainStore, error) { chain := store.NewChainStore(cbs, sbs, ds, weight, j) if err := chain.Load(helpers.LifecycleCtx(mctx, lc)); err != nil { - log.Warnf("loading chain state from disk: %s", err) + return nil, xerrors.Errorf("loading chain state from disk: %w", err) } var startHook func(context.Context) error @@ -108,7 +108,7 @@ func ChainStore(lc fx.Lifecycle, }, }) - return chain + return chain, nil } func NetworkName(mctx helpers.MetricsCtx, From a34cc5e4e98d5ea8daa7b05228770f7a710fe9f6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2023 16:12:18 +0400 Subject: [PATCH 11/13] fix: eth: handle unresolvable addresses (#11433) Correctly handle "unresolvable" to/from addresses in top-level messages in the Ethereum API. Specifically: 1. Fail if we can't resolve the from address. As far as I can tell, this should be impossible (the message statically couldn't have been included in the block if the sender didn't exist). 2. If we can't resolve the "to" address to an ID, use "max uint64" as the ID (`0xff0000000000000000000000ffffffffffffffff`). This will only happen if the transaction was reverted. It'll be a little confusing, but the alternative is to (a) use an empty address (will look like a contract creation, which is definitely wrong) or (b) use a random/hashed address which will likely be more confusing as it won't be "obviously weird". --- CHANGELOG.md | 1 + itests/eth_transactions_test.go | 180 +++++++++++++++++++++++++------- node/impl/full/eth_utils.go | 48 +++++++-- 3 files changed, 185 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05caea4f3..f6c923fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - feat: Unambiguously translate native messages to Ethereum transactions by: - Detecting native messages that "look" like Ethereum transactions (creating smart contracts, invoking a smart contract, etc.), and decoding them as such. - Otherwise, ABI-encoding the inputs as if they were calls to a `handle_filecoin_method` Solidity method. +- fix: ensure that the Ethereum API never returns "empty" addresses for native messages. When a "to" address cannot be resolved to a 0x-style address, it will be re-written to `0xff0000000000000000000000ffffffffffffffff`. This can only happen when the native transaction _reverted_ (failing to create an account at the specified "to" address). # v 1.25.0 / 2023-11-22 diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 026ac2387..1fffc6618 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -9,11 +9,14 @@ import ( "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" builtin2 "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/manifest" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/store" @@ -378,7 +381,54 @@ func deployContractTx(ctx context.Context, client *kit.TestFullNode, ethAddr eth }, nil } -func TestEthTxFromNativeAccount(t *testing.T) { +// Invoke a contract with empty input. +func TestEthTxFromNativeAccount_EmptyInput(t *testing.T) { + blockTime := 10 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + secpAddr, err := address.NewSecp256k1Address([]byte("foobar")) + require.NoError(t, err) + + msg := &types.Message{ + From: client.DefaultKey.Address, + To: secpAddr, + Value: abi.TokenAmount(types.MustParseFIL("100")), + Method: builtin2.MethodsEVM.InvokeContract, + } + + sMsg, err := client.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + client.WaitMsg(ctx, sMsg.Cid()) + + hash, err := client.EthGetTransactionHashByCid(ctx, sMsg.Cid()) + require.NoError(t, err) + tx, err := client.EthGetTransactionByHash(ctx, hash) + require.NoError(t, err) + + // Expect empty input params given that we "invoked" the contract (well, invoked ourselves). + require.Equal(t, ethtypes.EthBytes{}, tx.Input) + + // Validate the to/from addresses. + toId, err := client.StateLookupID(ctx, msg.To, types.EmptyTSK) + require.NoError(t, err) + fromId, err := client.StateLookupID(ctx, msg.From, types.EmptyTSK) + require.NoError(t, err) + + expectedTo, err := ethtypes.EthAddressFromFilecoinAddress(toId) + require.NoError(t, err) + expectedFrom, err := ethtypes.EthAddressFromFilecoinAddress(fromId) + require.NoError(t, err) + require.Equal(t, &expectedTo, tx.To) + require.Equal(t, expectedFrom, tx.From) +} + +// Invoke a contract with non-empty input. +func TestEthTxFromNativeAccount_NonEmptyInput(t *testing.T) { blockTime := 10 * time.Millisecond client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) @@ -394,49 +444,48 @@ func TestEthTxFromNativeAccount(t *testing.T) { Method: builtin2.MethodsEVM.InvokeContract, } - // Send a message with no input. + var err error + input := abi.CborBytes([]byte{0x1, 0x2, 0x3, 0x4}) + msg.Params, err = actors.SerializeParams(&input) + require.NoError(t, err) sMsg, err := client.MpoolPushMessage(ctx, msg, nil) require.NoError(t, err) client.WaitMsg(ctx, sMsg.Cid()) - hash, err := client.EthGetTransactionHashByCid(ctx, sMsg.Cid()) require.NoError(t, err) tx, err := client.EthGetTransactionByHash(ctx, hash) require.NoError(t, err) - // Expect empty input params given that we "invoked" the contract (well, invoked ourselves). - require.Equal(t, ethtypes.EthBytes{}, tx.Input) - - // Send a message with some input. - - input := abi.CborBytes([]byte{0x1, 0x2, 0x3, 0x4}) - msg.Params, err = actors.SerializeParams(&input) - require.NoError(t, err) - - sMsg, err = client.MpoolPushMessage(ctx, msg, nil) - require.NoError(t, err) - client.WaitMsg(ctx, sMsg.Cid()) - hash, err = client.EthGetTransactionHashByCid(ctx, sMsg.Cid()) - require.NoError(t, err) - tx, err = client.EthGetTransactionByHash(ctx, hash) - require.NoError(t, err) - // Expect the decoded input. require.EqualValues(t, input, tx.Input) +} - // Invoke the contract, but with incorrectly encoded input. We expect this to be abi-encoded - // as if it were any other method call. +// Invoke a contract, but with incorrectly encoded input. We expect this to be abi-encoded as if it +// were any other method call. +func TestEthTxFromNativeAccount_BadInput(t *testing.T) { + blockTime := 10 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) - msg.Params = input - require.NoError(t, err) + ens.InterconnectAll().BeginMining(blockTime) - sMsg, err = client.MpoolPushMessage(ctx, msg, nil) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + msg := &types.Message{ + From: client.DefaultKey.Address, + To: client.DefaultKey.Address, + Value: abi.TokenAmount(types.MustParseFIL("100")), + Method: builtin2.MethodsEVM.InvokeContract, + Params: []byte{0x1, 0x2, 0x3, 0x4}, + } + + sMsg, err := client.MpoolPushMessage(ctx, msg, nil) require.NoError(t, err) client.WaitMsg(ctx, sMsg.Cid()) - hash, err = client.EthGetTransactionHashByCid(ctx, sMsg.Cid()) + hash, err := client.EthGetTransactionHashByCid(ctx, sMsg.Cid()) require.NoError(t, err) - tx, err = client.EthGetTransactionByHash(ctx, hash) + tx, err := client.EthGetTransactionByHash(ctx, hash) require.NoError(t, err) const expectedHex1 = "868e10c4" + // "handle filecoin method" function selector @@ -451,24 +500,39 @@ func TestEthTxFromNativeAccount(t *testing.T) { // Input: 1, 2, 3, 4 "0102030400000000000000000000000000000000000000000000000000000000" - input, err = hex.DecodeString(expectedHex1) + input, err := hex.DecodeString(expectedHex1) require.NoError(t, err) require.EqualValues(t, input, tx.Input) - // Invoke a random method with the same input. We expect the same result as above, but with - // a different method number. +} - msg.Method++ +// Invoke a native method. +func TestEthTxFromNativeAccount_NativeMethod(t *testing.T) { + blockTime := 10 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) - sMsg, err = client.MpoolPushMessage(ctx, msg, nil) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + msg := &types.Message{ + From: client.DefaultKey.Address, + To: client.DefaultKey.Address, + Value: abi.TokenAmount(types.MustParseFIL("100")), + Method: builtin2.MethodsEVM.InvokeContract + 1, + Params: []byte{0x1, 0x2, 0x3, 0x4}, + } + + sMsg, err := client.MpoolPushMessage(ctx, msg, nil) require.NoError(t, err) client.WaitMsg(ctx, sMsg.Cid()) - hash, err = client.EthGetTransactionHashByCid(ctx, sMsg.Cid()) + hash, err := client.EthGetTransactionHashByCid(ctx, sMsg.Cid()) require.NoError(t, err) - tx, err = client.EthGetTransactionByHash(ctx, hash) + tx, err := client.EthGetTransactionByHash(ctx, hash) require.NoError(t, err) - const expectedHex2 = "868e10c4" + // "handle filecoin method" function selector + const expectedHex = "868e10c4" + // "handle filecoin method" function selector // InvokeEVM+1 "00000000000000000000000000000000000000000000000000000000e525aa16" + // CBOR multicodec (0x51) @@ -479,7 +543,51 @@ func TestEthTxFromNativeAccount(t *testing.T) { "0000000000000000000000000000000000000000000000000000000000000004" + // Input: 1, 2, 3, 4 "0102030400000000000000000000000000000000000000000000000000000000" - input, err = hex.DecodeString(expectedHex2) + input, err := hex.DecodeString(expectedHex) require.NoError(t, err) require.EqualValues(t, input, tx.Input) } + +// Send to an invalid receiver. We're checking to make sure we correctly set `txn.To` to the special +// "reverted" eth addr. +func TestEthTxFromNativeAccount_InvalidReceiver(t *testing.T) { + blockTime := 10 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + to, err := address.NewActorAddress([]byte("foobar")) + require.NoError(t, err) + + msg := &types.Message{ + From: client.DefaultKey.Address, + To: to, + Value: abi.TokenAmount(types.MustParseFIL("100")), + Method: builtin2.MethodsEVM.InvokeContract + 1, + Params: []byte{0x1, 0x2, 0x3, 0x4}, + // We can't estimate gas for a failed message, so we hard-code these values. + GasLimit: 10_000_000, + GasFeeCap: abi.NewTokenAmount(10000), + } + + // We expect the "to" address to be the special "reverted" eth address. + expectedTo, err := ethtypes.ParseEthAddress("ff0000000000000000000000ffffffffffffffff") + require.NoError(t, err) + + sMsg, err := client.WalletSignMessage(ctx, client.DefaultKey.Address, msg) + require.NoError(t, err) + k, err := client.MpoolPush(ctx, sMsg) + require.NoError(t, err) + res, err := client.StateWaitMsg(ctx, k, 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.Equal(t, res.Receipt.ExitCode, exitcode.SysErrInvalidReceiver) + + hash, err := client.EthGetTransactionHashByCid(ctx, k) + require.NoError(t, err) + tx, err := client.EthGetTransactionByHash(ctx, hash) + require.NoError(t, err) + require.EqualValues(t, &expectedTo, tx.To) +} diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index 7b5a35b40..1e407a321 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -29,6 +29,18 @@ import ( "github.com/filecoin-project/lotus/chain/vm" ) +// The address used in messages to actors that have since been deleted. +// +// 0xff0000000000000000000000ffffffffffffffff +var revertedEthAddress ethtypes.EthAddress + +func init() { + revertedEthAddress[0] = 0xff + for i := 20 - 8; i < 20; i++ { + revertedEthAddress[i] = 0xff + } +} + func getTipsetByBlockNumber(ctx context.Context, chain *store.ChainStore, blkParam string, strict bool) (*types.TipSet, error) { if blkParam == "earliest" { return nil, fmt.Errorf("block param \"earliest\" is not supported") @@ -463,13 +475,19 @@ func newEthTxFromSignedMessage(smsg *types.SignedMessage, st *state.StateTree) ( return ethtypes.EthTx{}, xerrors.Errorf("failed to calculate hash for ethTx: %w", err) } } else if smsg.Signature.Type == crypto.SigTypeSecp256k1 { // Secp Filecoin Message - tx = ethTxFromNativeMessage(smsg.VMMessage(), st) + tx, err = ethTxFromNativeMessage(smsg.VMMessage(), st) + if err != nil { + return ethtypes.EthTx{}, err + } tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid()) if err != nil { return ethtypes.EthTx{}, err } } else { // BLS Filecoin message - tx = ethTxFromNativeMessage(smsg.VMMessage(), st) + tx, err = ethTxFromNativeMessage(smsg.VMMessage(), st) + if err != nil { + return ethtypes.EthTx{}, err + } tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid()) if err != nil { return ethtypes.EthTx{}, err @@ -482,19 +500,33 @@ func newEthTxFromSignedMessage(smsg *types.SignedMessage, st *state.StateTree) ( // Convert a native message to an eth transaction. // // - The state-tree must be from after the message was applied (ideally the following tipset). +// - In some cases, the "to" address may be `0xff0000000000000000000000ffffffffffffffff`. This +// means that the "to" address has not been assigned in the passed state-tree and can only +// happen if the transaction reverted. // // ethTxFromNativeMessage does NOT populate: // - BlockHash // - BlockNumber // - TransactionIndex // - Hash -func ethTxFromNativeMessage(msg *types.Message, st *state.StateTree) ethtypes.EthTx { - // We don't care if we error here, conversion is best effort for non-eth transactions - from, _ := lookupEthAddress(msg.From, st) - to, _ := lookupEthAddress(msg.To, st) +func ethTxFromNativeMessage(msg *types.Message, st *state.StateTree) (ethtypes.EthTx, error) { + // Lookup the from address. This must succeed. + from, err := lookupEthAddress(msg.From, st) + if err != nil { + return ethtypes.EthTx{}, xerrors.Errorf("failed to lookup sender address %s when converting a native message to an eth txn: %w", msg.From, err) + } + // Lookup the to address. If the recipient doesn't exist, we replace the address with a + // known sentinel address. + to, err := lookupEthAddress(msg.To, st) + if err != nil { + if !errors.Is(err, types.ErrActorNotFound) { + return ethtypes.EthTx{}, xerrors.Errorf("failed to lookup receiver address %s when converting a native message to an eth txn: %w", msg.To, err) + } + to = revertedEthAddress + } toPtr := &to - // Convert the input parameters to "solidity ABI". + // Finally, convert the input parameters to "solidity ABI". // For empty, we use "0" as the codec. Otherwise, we use CBOR for message // parameters. @@ -536,7 +568,7 @@ func ethTxFromNativeMessage(msg *types.Message, st *state.StateTree) ethtypes.Et MaxFeePerGas: ethtypes.EthBigInt(msg.GasFeeCap), MaxPriorityFeePerGas: ethtypes.EthBigInt(msg.GasPremium), AccessList: []ethtypes.EthHash{}, - } + }, nil } func getSignedMessage(ctx context.Context, cs *store.ChainStore, msgCid cid.Cid) (*types.SignedMessage, error) { From 8fec6e31f24099795556f2981dd0127122fa4380 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2023 16:50:05 +0400 Subject: [PATCH 12/13] fix: build: an epoch is near an upgrade iff the upgrade is enabled (#11401) This isn't a huge deal, but we'd otherwise consider the first finality epochs (or so) to be "near" any disabled upgrade. --- build/isnearupgrade.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/isnearupgrade.go b/build/isnearupgrade.go index 4273f0e9e..74975780f 100644 --- a/build/isnearupgrade.go +++ b/build/isnearupgrade.go @@ -5,5 +5,8 @@ import ( ) func IsNearUpgrade(epoch, upgradeEpoch abi.ChainEpoch) bool { + if upgradeEpoch < 0 { + return false + } return epoch > upgradeEpoch-Finality && epoch < upgradeEpoch+Finality } From dd667d28becf20b1cdbdc81487ea79a85ac37550 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Wed, 29 Nov 2023 15:17:05 +0000 Subject: [PATCH 13/13] Update one test to pass optional block param to EthEstimateGas --- itests/eth_transactions_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 59e5852c9..62e21da27 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -45,10 +45,14 @@ func TestValueTransferValidSignature(t *testing.T) { kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000)) - gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{ - From: ðAddr, - Data: contract, - }}) + blkParam := ethtypes.NewEthBlockNumberOrHashFromPredefined("latest") + gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{ + Tx: ethtypes.EthCall{ + From: ðAddr, + Data: contract, + }, + BlkParam: &blkParam, + }) require.NoError(t, err) gaslimit, err := client.EthEstimateGas(ctx, gasParams)