diff --git a/core/state/statedb.go b/core/state/statedb.go index 2230b10ef0..c7814b8159 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -568,6 +568,11 @@ func (self *StateDB) GetRefund() uint64 { return self.refund } +// GetTrie returns the current value of the trie. +func (self *StateDB) GetTrie() Trie { + return self.trie +} + // Finalise finalises the state by removing the self destructed objects // and clears the journal as well as the refunds. func (s *StateDB) Finalise(deleteEmptyObjects bool) { diff --git a/eth/api.go b/eth/api.go index 816b9cd335..a2f5ec4b89 100644 --- a/eth/api.go +++ b/eth/api.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -76,6 +77,50 @@ func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 { return (hexutil.Uint64)(chainID.Uint64()) } +// ListAccounts retrieves accounts of the database at a given block. +func (api *PublicEthereumAPI) ListAccounts(count uint64, offset *common.Address, blockNr *rpc.BlockNumber) ([]common.Address, error) { + var block *types.Block = nil + var tr state.Trie + + if blockNr == nil || *blockNr == rpc.LatestBlockNumber { + block = api.e.blockchain.CurrentBlock() + } else if *blockNr != rpc.PendingBlockNumber { + block = api.e.blockchain.GetBlockByNumber(uint64(*blockNr)) + } + if block != nil { + stateDb, err := api.e.BlockChain().StateAt(block.Root()) + if err != nil { + return nil, err + } + tr, err = stateDb.Database().OpenTrie(block.Root()) + if err != nil { + return nil, err + } + } else if *blockNr != rpc.PendingBlockNumber { + return nil, fmt.Errorf("block #%d not found", *blockNr) + } else { + // Get the pending state + _, stateDb := api.e.miner.Pending() + tr = stateDb.GetTrie() + } + + var accounts []common.Address + var it *trie.Iterator + + if offset != nil && *offset != (common.Address{}) { + it = trie.NewIterator(tr.NodeIterator(crypto.Keccak256(offset[:]))) + it.Next() + } else { + it = trie.NewIterator(tr.NodeIterator(nil)) + } + + for ; count > 0 && it.Next(); count-- { + addr := tr.GetKey(it.Key) + accounts = append(accounts, common.BytesToAddress(addr)) + } + return accounts, nil +} + // PublicMinerAPI provides an API to control the miner. // It offers only methods that operate on data that pose no security risk when it is publicly accessible. type PublicMinerAPI struct { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 6b98c8b7e1..87ffa04783 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -500,6 +500,12 @@ web3._extend({ params: 3, inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter] }), + new web3._extend.Method({ + name: 'listAccounts', + call: 'eth_listAccounts', + params: 3, + inputFormatter: [null, null, web3._extend.formatters.inputDefaultBlockNumberFormatter], + }), ], properties: [ new web3._extend.Property({