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 }