diff --git a/common/bytes.go b/common/bytes.go index 0c257a1ee0..8520b330db 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -31,6 +31,15 @@ func ToHex(b []byte) string { return "0x" + hex } +// creates a array of hex-string based on []byte +func ToHexArray(b [][]byte) []string { + r := make([]string, len(b)) + for i := range b { + r[i] = ToHex(b[i]) + } + return r +} + // FromHex returns the bytes represented by the hexadecimal string s. // s may be prefixed with "0x". func FromHex(s string) []byte { diff --git a/core/state/statedb.go b/core/state/statedb.go index 216667ce98..788449c9e2 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -256,6 +256,32 @@ func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash return common.Hash{} } +// MerkleProof +type ProofList [][]byte + +func (n *ProofList) Put(key []byte, value []byte) error { + *n = append(*n, value) + return nil +} + +// returns the MerkleProof for a given Account +func (self *StateDB) GetProof(a common.Address) [][]byte { + var proof ProofList + self.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof) + return [][]byte(proof) +} + +// returns the StorageProof for given key +func (self *StateDB) GetStorageProof(a common.Address, key common.Hash) [][]byte { + trie := self.StorageTrie(a) + if trie == nil { + return [][]byte{} + } + var proof ProofList + trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) + return [][]byte(proof) +} + // GetCommittedState retrieves a value from the given account's committed storage trie. func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { stateObject := self.getStateObject(addr) diff --git a/core/vm/interface.go b/core/vm/interface.go index fc15082f18..5cefd54d7a 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -35,6 +35,8 @@ type StateDB interface { SetNonce(common.Address, uint64) GetCodeHash(common.Address) common.Hash + GetProof(common.Address) [][]byte + GetStorageProof(common.Address, common.Hash) [][]byte GetCode(common.Address) []byte SetCode(common.Address, []byte) GetCodeSize(common.Address) int diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f7379e228c..a0480bc5f7 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -502,6 +502,55 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add return (*hexutil.Big)(state.GetBalance(address)), state.Error() } +// GetBalance returns the amount of wei for the given address in the state of the +// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta +// block numbers are also allowed. +func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNr rpc.BlockNumber) (map[string]interface{}, error) { + state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return nil, err + } + + storageTrie := state.StorageTrie(address) + storageHash := types.EmptyRootHash + codeHash := state.GetCodeHash(address) + storageProof := make([]map[string]interface{}, len(storageKeys)) + + if storageTrie != nil { + storageHash = storageTrie.Hash() + } else { + codeHash = crypto.Keccak256Hash(nil) + } + + for i := range storageKeys { + if storageTrie != nil { + storageProof[i] = map[string]interface{}{ + "key": storageKeys[i], + "value": state.GetState(address, common.HexToHash(storageKeys[i])), + "proof": common.ToHexArray(state.GetStorageProof(address, common.HexToHash(storageKeys[i]))), + } + } else { + storageProof[i] = map[string]interface{}{ + "key": storageKeys[i], + "value": common.Hash{}, + "proof": []string{}, + } + } + } + + fields := map[string]interface{}{ + "address": address, + "accountProof": common.ToHexArray(state.GetProof(address)), + "balance": (*hexutil.Big)(state.GetBalance(address)), + "codeHash": codeHash, + "nonce": hexutil.Uint64(state.GetNonce(address)), + "storageHash": storageHash, + "storageProof": storageProof, + } + + return fields, state.Error() +} + // GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all // transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {