This commit is contained in:
Jeffrey Wilcke 2017-01-16 10:15:38 +00:00 committed by GitHub
commit 876f8748a4
4 changed files with 96 additions and 5 deletions

View file

@ -61,7 +61,7 @@ func (self *StateDB) RawDump() Dump {
Code: common.Bytes2Hex(obj.Code(self.db)), Code: common.Bytes2Hex(obj.Code(self.db)),
Storage: make(map[string]string), Storage: make(map[string]string),
} }
storageIt := obj.getTrie(self.db).Iterator() storageIt := obj.GetTrie(self.db).Iterator()
for storageIt.Next() { for storageIt.Next() {
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
} }

View file

@ -152,7 +152,9 @@ func (c *StateObject) touch() {
c.touched = true c.touched = true
} }
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie { // GetTrie returns the current instantiated trie or creates a new one
// using the data's root to initialise the trie.
func (c *StateObject) GetTrie(db trie.Database) *trie.SecureTrie {
if c.trie == nil { if c.trie == nil {
var err error var err error
c.trie, err = trie.NewSecure(c.data.Root, db, 0) c.trie, err = trie.NewSecure(c.data.Root, db, 0)
@ -171,7 +173,7 @@ func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash
return value return value
} }
// Load from DB in case it is missing. // Load from DB in case it is missing.
if enc := self.getTrie(db).Get(key[:]); len(enc) > 0 { if enc := self.GetTrie(db).Get(key[:]); len(enc) > 0 {
_, content, _, err := rlp.Split(enc) _, content, _, err := rlp.Split(enc)
if err != nil { if err != nil {
self.setError(err) self.setError(err)
@ -206,7 +208,7 @@ func (self *StateObject) setState(key, value common.Hash) {
// updateTrie writes cached storage modifications into the object's storage trie. // updateTrie writes cached storage modifications into the object's storage trie.
func (self *StateObject) updateTrie(db trie.Database) { func (self *StateObject) updateTrie(db trie.Database) {
tr := self.getTrie(db) tr := self.GetTrie(db)
for key, value := range self.dirtyStorage { for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key) delete(self.dirtyStorage, key)
if (value == common.Hash{}) { if (value == common.Hash{}) {
@ -388,7 +390,7 @@ func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
cb(h, value) cb(h, value)
} }
it := self.getTrie(self.db.db).Iterator() it := self.GetTrie(self.db.db).Iterator()
for it.Next() { for it.Next() {
// ignore cached values // ignore cached values
key := common.BytesToHash(self.trie.GetKey(it.Key)) key := common.BytesToHash(self.trie.GetKey(it.Key))

View file

@ -560,3 +560,87 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
} }
return nil, errors.New("database inconsistency") return nil, errors.New("database inconsistency")
} }
type StorageRangeAtResult struct {
Storage map[string]string `json:"storage"`
Complete bool `json:"complete"`
}
// StorageRangeAt returns the storage at the given block height and
// transaction index (exclusive). It may be limited using the
// storageAddress, storageAddressEnd (inclusive) and maxResult parameters.
//
// StorageRangeAt is currently limited and requires needless iterators
// due to the limitation of the trie iterator. At present we can't start
// iterating from any given key, thus we need to loop over any key that's
// not inclusive in the range of start and end.
//
// BUG: Because the state objects make use of the secure storage, iterating
// trie keys is out of order and will be returned the exact same way.
func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, storageAddressStart, storageAddressEnd common.Hash, maxResult int) (interface{}, error) {
block := api.eth.BlockChain().GetBlockByHash(blockHash)
if block == nil {
return nil, fmt.Errorf("block %x not found", blockHash)
}
// Create the state database to mutate and eventually trace
parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
}
stateDb, err := api.eth.BlockChain().StateAt(parent.Root())
if err != nil {
return nil, err
}
result := StorageRangeAtResult{Storage: make(map[string]string)}
signer := types.MakeSigner(api.config, block.Number())
// Mutate the state and trace the selected transaction
done:
for idx, tx := range block.Transactions() {
// Assemble the transaction call message
msg, err := tx.AsMessage(signer)
if err != nil {
return nil, fmt.Errorf("sender retrieval failed: %v", err)
}
context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain())
// Mutate the state if we haven't reached the tracing transaction yet
if idx < txIndex {
vmenv := vm.NewEnvironment(context, stateDb, api.config, vm.Config{})
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err)
}
stateDb.DeleteSuicides()
continue
}
stateObject := stateDb.GetStateObject(contractAddress)
// We need to check if the object exists again. It might have been deleted in between the
// transactions.
if stateObject != nil {
trie := stateObject.GetTrie(api.eth.ChainDb())
it := trie.Iterator()
for it.Next() && len(result.Storage) < maxResult {
var (
value common.Hash
key = trie.GetKey(it.Key)
)
if bytes.Compare(storageAddressStart[:], key[:]) > 0 {
continue
}
if bytes.Compare(storageAddressEnd[:], key[:]) < 0 {
break done
}
rlp.DecodeBytes(it.Value, &value)
result.Storage[common.ToHex(key)] = value.Hex()
}
result.Complete = !it.Next()
}
}
return result, nil
}

View file

@ -385,6 +385,11 @@ web3._extend({
call: 'debug_traceTransaction', call: 'debug_traceTransaction',
params: 2, params: 2,
inputFormatter: [null, null] inputFormatter: [null, null]
}),
new web3._extend.Method({
name: 'storageRangeAt',
call: 'debug_storageRangeAt',
params: 6,
}) })
], ],
properties: [] properties: []