github.com/theQRL/go-zond@v0.1.1/zond/api_debug.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package zond
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"time"
    24  
    25  	"github.com/theQRL/go-zond/common"
    26  	"github.com/theQRL/go-zond/common/hexutil"
    27  	"github.com/theQRL/go-zond/core/rawdb"
    28  	"github.com/theQRL/go-zond/core/state"
    29  	"github.com/theQRL/go-zond/core/types"
    30  	"github.com/theQRL/go-zond/crypto"
    31  	"github.com/theQRL/go-zond/internal/ethapi"
    32  	"github.com/theQRL/go-zond/log"
    33  	"github.com/theQRL/go-zond/rlp"
    34  	"github.com/theQRL/go-zond/rpc"
    35  	"github.com/theQRL/go-zond/trie"
    36  )
    37  
    38  // DebugAPI is the collection of Ethereum full node APIs for debugging the
    39  // protocol.
    40  type DebugAPI struct {
    41  	eth *Ethereum
    42  }
    43  
    44  // NewDebugAPI creates a new DebugAPI instance.
    45  func NewDebugAPI(eth *Ethereum) *DebugAPI {
    46  	return &DebugAPI{eth: eth}
    47  }
    48  
    49  // DumpBlock retrieves the entire state of the database at a given block.
    50  func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
    51  	opts := &state.DumpConfig{
    52  		OnlyWithAddresses: true,
    53  		Max:               AccountRangeMaxResults, // Sanity limit over RPC
    54  	}
    55  	if blockNr == rpc.PendingBlockNumber {
    56  		// If we're dumping the pending state, we need to request
    57  		// both the pending block as well as the pending state from
    58  		// the miner and operate on those
    59  		_, stateDb := api.eth.miner.Pending()
    60  		if stateDb == nil {
    61  			return state.Dump{}, errors.New("pending state is not available")
    62  		}
    63  		return stateDb.RawDump(opts), nil
    64  	}
    65  	var header *types.Header
    66  	switch blockNr {
    67  	case rpc.LatestBlockNumber:
    68  		header = api.eth.blockchain.CurrentBlock()
    69  	case rpc.FinalizedBlockNumber:
    70  		header = api.eth.blockchain.CurrentFinalBlock()
    71  	case rpc.SafeBlockNumber:
    72  		header = api.eth.blockchain.CurrentSafeBlock()
    73  	default:
    74  		block := api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
    75  		if block == nil {
    76  			return state.Dump{}, fmt.Errorf("block #%d not found", blockNr)
    77  		}
    78  		header = block.Header()
    79  	}
    80  	if header == nil {
    81  		return state.Dump{}, fmt.Errorf("block #%d not found", blockNr)
    82  	}
    83  	stateDb, err := api.eth.BlockChain().StateAt(header.Root)
    84  	if err != nil {
    85  		return state.Dump{}, err
    86  	}
    87  	return stateDb.RawDump(opts), nil
    88  }
    89  
    90  // Preimage is a debug API function that returns the preimage for a sha3 hash, if known.
    91  func (api *DebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
    92  	if preimage := rawdb.ReadPreimage(api.eth.ChainDb(), hash); preimage != nil {
    93  		return preimage, nil
    94  	}
    95  	return nil, errors.New("unknown preimage")
    96  }
    97  
    98  // BadBlockArgs represents the entries in the list returned when bad blocks are queried.
    99  type BadBlockArgs struct {
   100  	Hash  common.Hash            `json:"hash"`
   101  	Block map[string]interface{} `json:"block"`
   102  	RLP   string                 `json:"rlp"`
   103  }
   104  
   105  // GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
   106  // and returns them as a JSON list of block hashes.
   107  func (api *DebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) {
   108  	var (
   109  		blocks  = rawdb.ReadAllBadBlocks(api.eth.chainDb)
   110  		results = make([]*BadBlockArgs, 0, len(blocks))
   111  	)
   112  	for _, block := range blocks {
   113  		var (
   114  			blockRlp  string
   115  			blockJSON map[string]interface{}
   116  		)
   117  		if rlpBytes, err := rlp.EncodeToBytes(block); err != nil {
   118  			blockRlp = err.Error() // Hacky, but hey, it works
   119  		} else {
   120  			blockRlp = fmt.Sprintf("%#x", rlpBytes)
   121  		}
   122  		blockJSON = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig())
   123  		results = append(results, &BadBlockArgs{
   124  			Hash:  block.Hash(),
   125  			RLP:   blockRlp,
   126  			Block: blockJSON,
   127  		})
   128  	}
   129  	return results, nil
   130  }
   131  
   132  // AccountRangeMaxResults is the maximum number of results to be returned per call
   133  const AccountRangeMaxResults = 256
   134  
   135  // AccountRange enumerates all accounts in the given block and start point in paging request
   136  func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hexutil.Bytes, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
   137  	var stateDb *state.StateDB
   138  	var err error
   139  
   140  	if number, ok := blockNrOrHash.Number(); ok {
   141  		if number == rpc.PendingBlockNumber {
   142  			// If we're dumping the pending state, we need to request
   143  			// both the pending block as well as the pending state from
   144  			// the miner and operate on those
   145  			_, stateDb = api.eth.miner.Pending()
   146  			if stateDb == nil {
   147  				return state.IteratorDump{}, errors.New("pending state is not available")
   148  			}
   149  		} else {
   150  			var header *types.Header
   151  			switch number {
   152  			case rpc.LatestBlockNumber:
   153  				header = api.eth.blockchain.CurrentBlock()
   154  			case rpc.FinalizedBlockNumber:
   155  				header = api.eth.blockchain.CurrentFinalBlock()
   156  			case rpc.SafeBlockNumber:
   157  				header = api.eth.blockchain.CurrentSafeBlock()
   158  			default:
   159  				block := api.eth.blockchain.GetBlockByNumber(uint64(number))
   160  				if block == nil {
   161  					return state.IteratorDump{}, fmt.Errorf("block #%d not found", number)
   162  				}
   163  				header = block.Header()
   164  			}
   165  			if header == nil {
   166  				return state.IteratorDump{}, fmt.Errorf("block #%d not found", number)
   167  			}
   168  			stateDb, err = api.eth.BlockChain().StateAt(header.Root)
   169  			if err != nil {
   170  				return state.IteratorDump{}, err
   171  			}
   172  		}
   173  	} else if hash, ok := blockNrOrHash.Hash(); ok {
   174  		block := api.eth.blockchain.GetBlockByHash(hash)
   175  		if block == nil {
   176  			return state.IteratorDump{}, fmt.Errorf("block %s not found", hash.Hex())
   177  		}
   178  		stateDb, err = api.eth.BlockChain().StateAt(block.Root())
   179  		if err != nil {
   180  			return state.IteratorDump{}, err
   181  		}
   182  	} else {
   183  		return state.IteratorDump{}, errors.New("either block number or block hash must be specified")
   184  	}
   185  
   186  	opts := &state.DumpConfig{
   187  		SkipCode:          nocode,
   188  		SkipStorage:       nostorage,
   189  		OnlyWithAddresses: !incompletes,
   190  		Start:             start,
   191  		Max:               uint64(maxResults),
   192  	}
   193  	if maxResults > AccountRangeMaxResults || maxResults <= 0 {
   194  		opts.Max = AccountRangeMaxResults
   195  	}
   196  	return stateDb.IteratorDump(opts), nil
   197  }
   198  
   199  // StorageRangeResult is the result of a debug_storageRangeAt API call.
   200  type StorageRangeResult struct {
   201  	Storage storageMap   `json:"storage"`
   202  	NextKey *common.Hash `json:"nextKey"` // nil if Storage includes the last key in the trie.
   203  }
   204  
   205  type storageMap map[common.Hash]storageEntry
   206  
   207  type storageEntry struct {
   208  	Key   *common.Hash `json:"key"`
   209  	Value common.Hash  `json:"value"`
   210  }
   211  
   212  // StorageRangeAt returns the storage at the given block height and transaction index.
   213  func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
   214  	var block *types.Block
   215  
   216  	block, err := api.eth.APIBackend.BlockByNumberOrHash(ctx, blockNrOrHash)
   217  	if err != nil {
   218  		return StorageRangeResult{}, err
   219  	}
   220  	if block == nil {
   221  		return StorageRangeResult{}, fmt.Errorf("block %v not found", blockNrOrHash)
   222  	}
   223  	_, _, statedb, release, err := api.eth.stateAtTransaction(ctx, block, txIndex, 0)
   224  	if err != nil {
   225  		return StorageRangeResult{}, err
   226  	}
   227  	defer release()
   228  
   229  	return storageRangeAt(statedb, block.Root(), contractAddress, keyStart, maxResult)
   230  }
   231  
   232  func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Address, start []byte, maxResult int) (StorageRangeResult, error) {
   233  	storageRoot := statedb.GetStorageRoot(address)
   234  	if storageRoot == types.EmptyRootHash || storageRoot == (common.Hash{}) {
   235  		return StorageRangeResult{}, nil // empty storage
   236  	}
   237  	id := trie.StorageTrieID(root, crypto.Keccak256Hash(address.Bytes()), storageRoot)
   238  	tr, err := trie.NewStateTrie(id, statedb.Database().TrieDB())
   239  	if err != nil {
   240  		return StorageRangeResult{}, err
   241  	}
   242  	trieIt, err := tr.NodeIterator(start)
   243  	if err != nil {
   244  		return StorageRangeResult{}, err
   245  	}
   246  	it := trie.NewIterator(trieIt)
   247  	result := StorageRangeResult{Storage: storageMap{}}
   248  	for i := 0; i < maxResult && it.Next(); i++ {
   249  		_, content, _, err := rlp.Split(it.Value)
   250  		if err != nil {
   251  			return StorageRangeResult{}, err
   252  		}
   253  		e := storageEntry{Value: common.BytesToHash(content)}
   254  		if preimage := tr.GetKey(it.Key); preimage != nil {
   255  			preimage := common.BytesToHash(preimage)
   256  			e.Key = &preimage
   257  		}
   258  		result.Storage[common.BytesToHash(it.Key)] = e
   259  	}
   260  	// Add the 'next key' so clients can continue downloading.
   261  	if it.Next() {
   262  		next := common.BytesToHash(it.Key)
   263  		result.NextKey = &next
   264  	}
   265  	return result, nil
   266  }
   267  
   268  // GetModifiedAccountsByNumber returns all accounts that have changed between the
   269  // two blocks specified. A change is defined as a difference in nonce, balance,
   270  // code hash, or storage hash.
   271  //
   272  // With one parameter, returns the list of accounts modified in the specified block.
   273  func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
   274  	var startBlock, endBlock *types.Block
   275  
   276  	startBlock = api.eth.blockchain.GetBlockByNumber(startNum)
   277  	if startBlock == nil {
   278  		return nil, fmt.Errorf("start block %x not found", startNum)
   279  	}
   280  
   281  	if endNum == nil {
   282  		endBlock = startBlock
   283  		startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
   284  		if startBlock == nil {
   285  			return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
   286  		}
   287  	} else {
   288  		endBlock = api.eth.blockchain.GetBlockByNumber(*endNum)
   289  		if endBlock == nil {
   290  			return nil, fmt.Errorf("end block %d not found", *endNum)
   291  		}
   292  	}
   293  	return api.getModifiedAccounts(startBlock, endBlock)
   294  }
   295  
   296  // GetModifiedAccountsByHash returns all accounts that have changed between the
   297  // two blocks specified. A change is defined as a difference in nonce, balance,
   298  // code hash, or storage hash.
   299  //
   300  // With one parameter, returns the list of accounts modified in the specified block.
   301  func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
   302  	var startBlock, endBlock *types.Block
   303  	startBlock = api.eth.blockchain.GetBlockByHash(startHash)
   304  	if startBlock == nil {
   305  		return nil, fmt.Errorf("start block %x not found", startHash)
   306  	}
   307  
   308  	if endHash == nil {
   309  		endBlock = startBlock
   310  		startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
   311  		if startBlock == nil {
   312  			return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
   313  		}
   314  	} else {
   315  		endBlock = api.eth.blockchain.GetBlockByHash(*endHash)
   316  		if endBlock == nil {
   317  			return nil, fmt.Errorf("end block %x not found", *endHash)
   318  		}
   319  	}
   320  	return api.getModifiedAccounts(startBlock, endBlock)
   321  }
   322  
   323  func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
   324  	if startBlock.Number().Uint64() >= endBlock.Number().Uint64() {
   325  		return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
   326  	}
   327  	triedb := api.eth.BlockChain().TrieDB()
   328  
   329  	oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb)
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	oldIt, err := oldTrie.NodeIterator([]byte{})
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	newIt, err := newTrie.NodeIterator([]byte{})
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	diff, _ := trie.NewDifferenceIterator(oldIt, newIt)
   346  	iter := trie.NewIterator(diff)
   347  
   348  	var dirty []common.Address
   349  	for iter.Next() {
   350  		key := newTrie.GetKey(iter.Key)
   351  		if key == nil {
   352  			return nil, fmt.Errorf("no preimage found for hash %x", iter.Key)
   353  		}
   354  		dirty = append(dirty, common.BytesToAddress(key))
   355  	}
   356  	return dirty, nil
   357  }
   358  
   359  // GetAccessibleState returns the first number where the node has accessible
   360  // state on disk. Note this being the post-state of that block and the pre-state
   361  // of the next block.
   362  // The (from, to) parameters are the sequence of blocks to search, which can go
   363  // either forwards or backwards
   364  func (api *DebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error) {
   365  	if api.eth.blockchain.TrieDB().Scheme() == rawdb.PathScheme {
   366  		return 0, errors.New("state history is not yet available in path-based scheme")
   367  	}
   368  	db := api.eth.ChainDb()
   369  	var pivot uint64
   370  	if p := rawdb.ReadLastPivotNumber(db); p != nil {
   371  		pivot = *p
   372  		log.Info("Found fast-sync pivot marker", "number", pivot)
   373  	}
   374  	var resolveNum = func(num rpc.BlockNumber) (uint64, error) {
   375  		// We don't have state for pending (-2), so treat it as latest
   376  		if num.Int64() < 0 {
   377  			block := api.eth.blockchain.CurrentBlock()
   378  			if block == nil {
   379  				return 0, errors.New("current block missing")
   380  			}
   381  			return block.Number.Uint64(), nil
   382  		}
   383  		return uint64(num.Int64()), nil
   384  	}
   385  	var (
   386  		start   uint64
   387  		end     uint64
   388  		delta   = int64(1)
   389  		lastLog time.Time
   390  		err     error
   391  	)
   392  	if start, err = resolveNum(from); err != nil {
   393  		return 0, err
   394  	}
   395  	if end, err = resolveNum(to); err != nil {
   396  		return 0, err
   397  	}
   398  	if start == end {
   399  		return 0, errors.New("from and to needs to be different")
   400  	}
   401  	if start > end {
   402  		delta = -1
   403  	}
   404  	for i := int64(start); i != int64(end); i += delta {
   405  		if time.Since(lastLog) > 8*time.Second {
   406  			log.Info("Finding roots", "from", start, "to", end, "at", i)
   407  			lastLog = time.Now()
   408  		}
   409  		if i < int64(pivot) {
   410  			continue
   411  		}
   412  		h := api.eth.BlockChain().GetHeaderByNumber(uint64(i))
   413  		if h == nil {
   414  			return 0, fmt.Errorf("missing header %d", i)
   415  		}
   416  		if ok, _ := api.eth.ChainDb().Has(h.Root[:]); ok {
   417  			return uint64(i), nil
   418  		}
   419  	}
   420  	return 0, errors.New("no state found")
   421  }
   422  
   423  // SetTrieFlushInterval configures how often in-memory tries are persisted
   424  // to disk. The value is in terms of block processing time, not wall clock.
   425  // If the value is shorter than the block generation time, or even 0 or negative,
   426  // the node will flush trie after processing each block (effectively archive mode).
   427  func (api *DebugAPI) SetTrieFlushInterval(interval string) error {
   428  	if api.eth.blockchain.TrieDB().Scheme() == rawdb.PathScheme {
   429  		return errors.New("trie flush interval is undefined for path-based scheme")
   430  	}
   431  	t, err := time.ParseDuration(interval)
   432  	if err != nil {
   433  		return err
   434  	}
   435  	api.eth.blockchain.SetTrieFlushInterval(t)
   436  	return nil
   437  }
   438  
   439  // GetTrieFlushInterval gets the current value of in-memory trie flush interval
   440  func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
   441  	if api.eth.blockchain.TrieDB().Scheme() == rawdb.PathScheme {
   442  		return "", errors.New("trie flush interval is undefined for path-based scheme")
   443  	}
   444  	return api.eth.blockchain.GetTrieFlushInterval().String(), nil
   445  }