2020-07-27 06:09:50 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2020-09-01 19:44:05 +00:00
|
|
|
|
2020-07-27 06:09:50 +00:00
|
|
|
"github.com/ledgerwatch/turbo-geth/common"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/common/hexutil"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/core"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/core/rawdb"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/core/state"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/eth"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/eth/stagedsync/stages"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/rpc"
|
2020-08-19 11:46:20 +00:00
|
|
|
"github.com/ledgerwatch/turbo-geth/turbo/adapter"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/turbo/transactions"
|
2020-07-27 06:09:50 +00:00
|
|
|
)
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
// PrivateDebugAPI Exposed RPC endpoints for debugging use
|
2020-07-27 06:09:50 +00:00
|
|
|
type PrivateDebugAPI interface {
|
|
|
|
StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error)
|
|
|
|
TraceTransaction(ctx context.Context, hash common.Hash, config *eth.TraceConfig) (interface{}, error)
|
|
|
|
AccountRange(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, start []byte, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error)
|
|
|
|
GetModifiedAccountsByNumber(ctx context.Context, startNum rpc.BlockNumber, endNum *rpc.BlockNumber) ([]common.Address, error)
|
|
|
|
GetModifiedAccountsByHash(_ context.Context, startHash common.Hash, endHash *common.Hash) ([]common.Address, error)
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
// PrivateDebugAPIImpl is implementation of the PrivateDebugAPI interface based on remote Db access
|
2020-07-27 06:09:50 +00:00
|
|
|
type PrivateDebugAPIImpl struct {
|
|
|
|
db ethdb.KV
|
2020-10-10 12:24:56 +00:00
|
|
|
dbReader ethdb.Database
|
2020-07-27 06:09:50 +00:00
|
|
|
chainContext core.ChainContext
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewPrivateDebugAPI returns PrivateDebugAPIImpl instance
|
2020-10-10 12:24:56 +00:00
|
|
|
func NewPrivateDebugAPI(db ethdb.KV, dbReader ethdb.Database) *PrivateDebugAPIImpl {
|
2020-07-27 06:09:50 +00:00
|
|
|
return &PrivateDebugAPIImpl{
|
2020-10-03 08:07:49 +00:00
|
|
|
db: db,
|
|
|
|
dbReader: dbReader,
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// StorageRangeAt re-implementation of eth/api.go:StorageRangeAt
|
|
|
|
func (api *PrivateDebugAPIImpl) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
|
2020-10-24 06:55:43 +00:00
|
|
|
tx, err := api.dbReader.Begin(ctx, false)
|
2020-10-10 12:24:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return StorageRangeResult{}, err
|
|
|
|
}
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
bc := adapter.NewBlockGetter(tx)
|
|
|
|
cc := adapter.NewChainContext(tx)
|
|
|
|
genesisHash := rawdb.ReadBlockByNumber(tx, 0).Hash()
|
|
|
|
chainConfig := rawdb.ReadChainConfig(tx, genesisHash)
|
2020-10-12 08:39:04 +00:00
|
|
|
_, _, _, stateReader, err := transactions.ComputeTxEnv(ctx, bc, chainConfig, cc, tx.(ethdb.HasTx).Tx(), blockHash, txIndex)
|
2020-07-27 06:09:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return StorageRangeResult{}, err
|
|
|
|
}
|
|
|
|
return StorageRangeAt(stateReader, contractAddress, keyStart, maxResult)
|
|
|
|
}
|
|
|
|
|
2020-10-14 15:59:42 +00:00
|
|
|
// AccountRange enumerates all accounts in the given block and start point in paging request
|
2020-07-27 06:09:50 +00:00
|
|
|
func (api *PrivateDebugAPIImpl) AccountRange(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, start []byte, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
|
2020-10-12 08:39:04 +00:00
|
|
|
tx, err := api.db.Begin(ctx, nil, false)
|
2020-10-10 12:24:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return state.IteratorDump{}, err
|
|
|
|
}
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
2020-07-27 06:09:50 +00:00
|
|
|
var blockNumber uint64
|
|
|
|
|
|
|
|
if number, ok := blockNrOrHash.Number(); ok {
|
|
|
|
if number == rpc.PendingBlockNumber {
|
|
|
|
return state.IteratorDump{}, fmt.Errorf("accountRange for pending block not supported")
|
|
|
|
}
|
|
|
|
if number == rpc.LatestBlockNumber {
|
|
|
|
var err error
|
|
|
|
|
2020-10-10 12:24:56 +00:00
|
|
|
blockNumber, _, err = stages.GetStageProgress(tx, stages.Execution)
|
2020-07-27 06:09:50 +00:00
|
|
|
if err != nil {
|
2020-09-26 21:01:11 +00:00
|
|
|
return state.IteratorDump{}, fmt.Errorf("last block has not found: %w", err)
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
blockNumber = uint64(number)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if hash, ok := blockNrOrHash.Hash(); ok {
|
2020-10-10 12:24:56 +00:00
|
|
|
block := rawdb.ReadBlockByHash(tx, hash)
|
2020-07-27 06:09:50 +00:00
|
|
|
if block == nil {
|
|
|
|
return state.IteratorDump{}, fmt.Errorf("block %s not found", hash.Hex())
|
|
|
|
}
|
|
|
|
blockNumber = block.NumberU64()
|
|
|
|
}
|
|
|
|
|
|
|
|
if maxResults > eth.AccountRangeMaxResults || maxResults <= 0 {
|
|
|
|
maxResults = eth.AccountRangeMaxResults
|
|
|
|
}
|
|
|
|
|
2020-10-12 08:39:04 +00:00
|
|
|
dumper := state.NewDumper(tx, blockNumber)
|
2020-07-27 06:09:50 +00:00
|
|
|
res, err := dumper.IteratorDump(nocode, nostorage, incompletes, start, maxResults)
|
|
|
|
if err != nil {
|
|
|
|
return state.IteratorDump{}, err
|
|
|
|
}
|
2020-08-19 06:33:49 +00:00
|
|
|
|
2020-10-10 12:24:56 +00:00
|
|
|
hash, err := rawdb.ReadCanonicalHash(tx, blockNumber)
|
2020-10-10 06:05:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return state.IteratorDump{}, err
|
|
|
|
}
|
2020-07-27 06:09:50 +00:00
|
|
|
if hash != (common.Hash{}) {
|
2020-10-10 12:24:56 +00:00
|
|
|
header := rawdb.ReadHeader(tx, hash, blockNumber)
|
2020-07-27 06:09:50 +00:00
|
|
|
if header != nil {
|
|
|
|
res.Root = header.Root.String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 06:33:49 +00:00
|
|
|
return res, nil
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
// GetModifiedAccountsByNumber returns a list of accounts found in the change sets
|
|
|
|
// startNum - first block from which to include results
|
|
|
|
// endNum - if present, last block from which to include results (inclusive). If not present, startNum.
|
|
|
|
func (api *PrivateDebugAPIImpl) GetModifiedAccountsByNumber(ctx context.Context, startNumber rpc.BlockNumber, endNumber *rpc.BlockNumber) ([]common.Address, error) {
|
|
|
|
tx, err := api.db.Begin(ctx, nil, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
2020-10-14 11:01:44 +00:00
|
|
|
defer tx.Rollback()
|
2020-07-27 06:09:50 +00:00
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
latestBlock, _, err := stages.GetStageProgress(tx, stages.Finish)
|
2020-07-27 06:09:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
// forces negative numbers to fail (too large) but allows zero
|
|
|
|
startNum := uint64(startNumber.Int64())
|
|
|
|
if startNum > latestBlock {
|
|
|
|
return nil, fmt.Errorf("start block (%d) is later than the latest block (%d)", startNum, latestBlock)
|
|
|
|
}
|
2020-07-27 06:09:50 +00:00
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
endNum := startNum // allows for single param calls
|
|
|
|
if endNumber != nil {
|
|
|
|
// forces negative numbers to fail (too large) but allows zero
|
|
|
|
endNum = uint64(endNumber.Int64())
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
// is endNum too big?
|
|
|
|
if endNum > latestBlock {
|
|
|
|
return nil, fmt.Errorf("end block (%d) is later than the latest block (%d)", endNum, latestBlock)
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
if startNum > endNum {
|
|
|
|
return nil, fmt.Errorf("start block (%d) must be less than or equal to end block (%d)", startNum, endNum)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ethdb.GetModifiedAccounts(tx, startNum, endNum)
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
// GetModifiedAccountsByHash returns a list of accounts found in the change sets
|
|
|
|
// startHash - first block to include in results
|
|
|
|
// endHash - if present, last block to include in results (inclusive)
|
2020-10-10 12:24:56 +00:00
|
|
|
func (api *PrivateDebugAPIImpl) GetModifiedAccountsByHash(ctx context.Context, startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
2020-10-14 11:01:44 +00:00
|
|
|
tx, err := api.db.Begin(ctx, nil, false)
|
2020-10-10 12:24:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
startBlock := rawdb.ReadBlockByHash(tx, startHash)
|
2020-07-27 06:09:50 +00:00
|
|
|
if startBlock == nil {
|
|
|
|
return nil, fmt.Errorf("start block %x not found", startHash)
|
|
|
|
}
|
2020-10-14 11:01:44 +00:00
|
|
|
startNum := startBlock.NumberU64()
|
|
|
|
endNum := startNum // allows for single parameter calls
|
2020-07-27 06:09:50 +00:00
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
if endHash != nil {
|
|
|
|
endBlock := rawdb.ReadBlockByHash(tx, *endHash)
|
2020-07-27 06:09:50 +00:00
|
|
|
if endBlock == nil {
|
|
|
|
return nil, fmt.Errorf("end block %x not found", *endHash)
|
|
|
|
}
|
2020-10-14 11:01:44 +00:00
|
|
|
endNum = endBlock.NumberU64()
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
if startNum > endNum {
|
|
|
|
return nil, fmt.Errorf("start block (%d) must be less than or equal to end block (%d)", startNum, endNum)
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 11:01:44 +00:00
|
|
|
return ethdb.GetModifiedAccounts(tx, startNum, endNum)
|
2020-07-27 06:09:50 +00:00
|
|
|
}
|