diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 4db994c20b..1e5b19fc0f 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -438,6 +438,15 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro return dl.storage(accountHash, storageHash, 0) } +func (dl *diffLayer) StorageSnapshots(accountHash common.Hash) (map[common.Hash]string, error) { + dl.lock.RLock() + res, err := dl.origin.StorageSnapshots(accountHash) + + dl.lock.RUnlock() + + return res, err +} + // storage is an internal version of Storage that skips the bloom filter checks // and uses the internal maps to try and retrieve the data. It's meant to be // used if a higher layer's bloom filter hit already. diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index ca4a1051aa..b2ca1d4be0 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -18,6 +18,7 @@ package snapshot import ( "bytes" + "fmt" "sync" "github.com/VictoriaMetrics/fastcache" @@ -137,6 +138,22 @@ func (dl *diskLayer) AccountRLP(hash common.Hash) ([]byte, error) { return blob, nil } +func (dl *diskLayer) StorageSnapshots(accountHash common.Hash) (map[common.Hash]string, error) { + dl.lock.RLock() + defer dl.lock.RUnlock() + + res := make(map[common.Hash]string) + + it := rawdb.IterateStorageSnapshots(dl.diskdb, accountHash) + for it.Next() { + v := it.Value() + _, content, _, _ := rlp.Split(v) + res[common.BytesToHash(it.Key()[1+len(accountHash):])] = fmt.Sprintf("%x", content) + } + it.Release() + return res, nil +} + // Storage directly retrieves the storage data associated with a particular hash, // within a particular account. func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, error) { diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 2f13631607..03464baa0f 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -129,6 +129,8 @@ type Snapshot interface { // within a particular account. Storage(accountHash, storageHash common.Hash) ([]byte, error) + StorageSnapshots(accountHash common.Hash) (map[common.Hash]string, error) + // Parent returns the subsequent layer of a snapshot, or nil if the base was // reached. Parent() snapshot diff --git a/core/state/state_object.go b/core/state/state_object.go index 1ede96ec63..fd6013c36e 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -285,6 +285,28 @@ func (s *StateObject) GetCommittedState(db Database, key common.Hash) common.Has return value } +// GetCommittedState retrieves a value from the committed account storage trie. +func (s *StateObject) GetCommittedStateAll(db Database) map[common.Hash]string { + if s.db.snap != nil { + if _, destructed := s.db.snapDestructs[s.address]; destructed { + return nil + } + // enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes())) + + it, err := s.db.snap.StorageSnapshots(s.addrHash) + + if err != nil { + s.setError(err) + return nil + } + + return it + } + + return nil + +} + // SetState updates a value in account storage. func (s *StateObject) SetState(db Database, key, value common.Hash) { // If the fake storage is set, put the temporary state update here. diff --git a/core/state/statedb.go b/core/state/statedb.go index f02908e881..a74124f00c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -481,6 +481,14 @@ func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { return common.Hash{} } +func (s *StateDB) GetStateAll(addr common.Address) map[common.Hash]string { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.GetCommittedStateAll(s.db) + } + return nil +} + // GetProof returns the Merkle proof for a given account. func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) { return s.GetProofByHash(crypto.Keccak256Hash(addr.Bytes())) @@ -779,8 +787,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) // CreateAccount is called during the EVM CREATE operation. The situation might arise that // a contract does the following: // -// 1. sends funds to sha(account ++ (nonce + 1)) -// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) +// 1. sends funds to sha(account ++ (nonce + 1)) +// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) // // Carrying over the balance ensures that Ether doesn't disappear. func (s *StateDB) CreateAccount(addr common.Address) { @@ -1043,7 +1051,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.StateIntermediateRoot() } -//CorrectAccountsRoot will fix account roots in pipecommit mode +// CorrectAccountsRoot will fix account roots in pipecommit mode func (s *StateDB) CorrectAccountsRoot(blockRoot common.Hash) { var snapshot snapshot.Snapshot if blockRoot == (common.Hash{}) { @@ -1071,7 +1079,7 @@ func (s *StateDB) CorrectAccountsRoot(blockRoot common.Hash) { } } -//PopulateSnapAccountAndStorage tries to populate required accounts and storages for pipecommit +// PopulateSnapAccountAndStorage tries to populate required accounts and storages for pipecommit func (s *StateDB) PopulateSnapAccountAndStorage() { for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { @@ -1083,7 +1091,7 @@ func (s *StateDB) PopulateSnapAccountAndStorage() { } } -//populateSnapStorage tries to populate required storages for pipecommit, and returns a flag to indicate whether the storage root changed or not +// populateSnapStorage tries to populate required storages for pipecommit, and returns a flag to indicate whether the storage root changed or not func (s *StateDB) populateSnapStorage(obj *StateObject) bool { for key, value := range obj.dirtyStorage { obj.pendingStorage[key] = value diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 742e758b99..1d64b4fad1 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -737,10 +737,10 @@ func (s *PublicBlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.H } // GetBlockByNumber returns the requested canonical block. -// * When blockNr is -1 the chain head is returned. -// * When blockNr is -2 the pending chain head is returned. -// * When fullTx is true all transactions in the block are returned, otherwise -// only the transaction hash is returned. +// - When blockNr is -1 the chain head is returned. +// - When blockNr is -2 the pending chain head is returned. +// - When fullTx is true all transactions in the block are returned, otherwise +// only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { block, err := s.b.BlockByNumber(ctx, number) if block != nil && err == nil { @@ -843,6 +843,15 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A return res[:], state.Error() } +func (s *PublicBlockChainAPI) GetStorageAll(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (map[common.Hash]string, error) { + state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if state == nil || err != nil { + return nil, err + } + res := state.GetStateAll(address) + return res, state.Error() +} + // OverrideAccount indicates the overriding fields of account during the execution // of a message call. // Note, state and stateDiff can't be specified at the same time. If state is