From a256095d1a8e3f57f10f6b9f13270f85619db8ce Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sun, 26 Sep 2021 23:30:52 +0200 Subject: [PATCH 1/3] eth/api: add rpc method to obtain which states are accessible --- eth/api.go | 61 +++++++++++++++++++++++++++++++++++++ internal/web3ext/web3ext.go | 6 ++++ 2 files changed, 67 insertions(+) diff --git a/eth/api.go b/eth/api.go index 8b96d1f316d79..3d276c8593fab 100644 --- a/eth/api.go +++ b/eth/api.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -545,3 +546,63 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc } return dirty, nil } + +// GetAccessibleState returns the list of roots where the node has state accessible on disk. +// The (from, to) parameters are the sequence of blocks to search, and the order of the +// searchresults follows the order of (from, to). +func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) ([]uint64, error) { + var resolveNum = func(num rpc.BlockNumber) (uint64, error) { + // We don't have state for pending (-2), so treat it as latest + if num.Int64() < 0 { + block := api.eth.blockchain.CurrentBlock() + if block == nil { + return 0, fmt.Errorf("current block missing") + } + return block.NumberU64(), nil + } + return uint64(num.Int64()), nil + } + var ( + start uint64 + end uint64 + delta = int64(1) + lastLog time.Time + results []uint64 + err error + ) + if start, err = resolveNum(from); err != nil { + return nil, err + } + if end, err = resolveNum(to); err != nil { + return nil, err + } + if start == end { + return nil, fmt.Errorf("from and to needs to be different") + } + if start > end { + delta = -1 + } + for i := int64(start); i != int64(end); i += delta { + if time.Since(lastLog) > 8*time.Second { + logCtx := []interface{}{"from", start, "to", end, "at", i, "found", len(results)} + if l := len(results); l > 0 { + logCtx = append(logCtx, "latest", results[l-1]) + } + log.Info("Finding roots", logCtx...) + lastLog = time.Now() + } + h := api.eth.BlockChain().GetHeaderByNumber(uint64(i)) + if h == nil { + return nil, fmt.Errorf("missing header %d", i) + } + if ok, _ := api.eth.ChainDb().Has(h.Root[:]); ok { + results = append(results, uint64(i)) + // If it's an archive mode, don't return 13M numbers, bail after 10K + if len(results) > 10_000 { + log.Warn("AccessibleRoots operation aborting early, too many roots found") + break + } + } + } + return results, nil +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index fe15cb0509d30..cb7d069ae265d 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -454,6 +454,12 @@ web3._extend({ call: 'debug_freezeClient', params: 1, }), + new web3._extend.Method({ + name: 'getAccessibleState', + call: 'debug_getAccessibleState', + params: 2, + inputFormatter:[web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter], + }), ], properties: [] }); From 0b72a1fdd4b80bffca7941e1e255de7be3bc38d6 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 27 Sep 2021 11:28:52 +0200 Subject: [PATCH 2/3] eth: debug_accessbleState - abort < pivot checks, return first result --- eth/api.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/eth/api.go b/eth/api.go index 3d276c8593fab..a0ed1d98dc62e 100644 --- a/eth/api.go +++ b/eth/api.go @@ -547,10 +547,17 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc return dirty, nil } -// GetAccessibleState returns the list of roots where the node has state accessible on disk. -// The (from, to) parameters are the sequence of blocks to search, and the order of the -// searchresults follows the order of (from, to). -func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) ([]uint64, error) { +// GetAccessibleState returns the first number where the node has state an accessible +// state on disk. +// The (from, to) parameters are the sequence of blocks to search, which can go +// either forwards or backwards +func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error) { + db := api.eth.ChainDb() + var pivot uint64 + if p := rawdb.ReadLastPivotNumber(db); p != nil { + pivot = *p + log.Info("Found fast-sync pivot marker", "number", pivot) + } var resolveNum = func(num rpc.BlockNumber) (uint64, error) { // We don't have state for pending (-2), so treat it as latest if num.Int64() < 0 { @@ -567,42 +574,35 @@ func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) ([]uint end uint64 delta = int64(1) lastLog time.Time - results []uint64 err error ) if start, err = resolveNum(from); err != nil { - return nil, err + return 0, err } if end, err = resolveNum(to); err != nil { - return nil, err + return 0, err } if start == end { - return nil, fmt.Errorf("from and to needs to be different") + return 0, fmt.Errorf("from and to needs to be different") } if start > end { delta = -1 } for i := int64(start); i != int64(end); i += delta { if time.Since(lastLog) > 8*time.Second { - logCtx := []interface{}{"from", start, "to", end, "at", i, "found", len(results)} - if l := len(results); l > 0 { - logCtx = append(logCtx, "latest", results[l-1]) - } - log.Info("Finding roots", logCtx...) + log.Info("Finding roots", "from", start, "to", end, "at", i) lastLog = time.Now() } + if i < int64(pivot) { + continue + } h := api.eth.BlockChain().GetHeaderByNumber(uint64(i)) if h == nil { - return nil, fmt.Errorf("missing header %d", i) + return 0, fmt.Errorf("missing header %d", i) } if ok, _ := api.eth.ChainDb().Has(h.Root[:]); ok { - results = append(results, uint64(i)) - // If it's an archive mode, don't return 13M numbers, bail after 10K - if len(results) > 10_000 { - log.Warn("AccessibleRoots operation aborting early, too many roots found") - break - } + return uint64(i), nil } } - return results, nil + return 0, fmt.Errorf("No state found") } From 60e66deb8dc2de252880a50cee6397cccb092da1 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 28 Sep 2021 19:59:49 +0200 Subject: [PATCH 3/3] Update eth/api.go Co-authored-by: Sina Mahmoodi <1591639+s1na@users.noreply.github.com> --- eth/api.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/eth/api.go b/eth/api.go index a0ed1d98dc62e..98d126958782d 100644 --- a/eth/api.go +++ b/eth/api.go @@ -547,8 +547,9 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc return dirty, nil } -// GetAccessibleState returns the first number where the node has state an accessible -// state on disk. +// GetAccessibleState returns the first number where the node has accessible +// state on disk. Note this being the post-state of that block and the pre-state +// of the next block. // The (from, to) parameters are the sequence of blocks to search, which can go // either forwards or backwards func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error) {