aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Jentzsch <simon@slock.it>2018-10-19 03:41:22 +0800
committerMartin Holst Swende <martin@swende.se>2018-10-19 03:41:22 +0800
commit97fb08342d227bbd126516083b0ddaf74e6e8468 (patch)
treeb47c7d9d8b6c1f7afdf2767d1099265a39a5fce0
parentcdf5982cfca2cd7d5fea85c226af5e48fde837df (diff)
downloaddexon-97fb08342d227bbd126516083b0ddaf74e6e8468.tar
dexon-97fb08342d227bbd126516083b0ddaf74e6e8468.tar.gz
dexon-97fb08342d227bbd126516083b0ddaf74e6e8468.tar.bz2
dexon-97fb08342d227bbd126516083b0ddaf74e6e8468.tar.lz
dexon-97fb08342d227bbd126516083b0ddaf74e6e8468.tar.xz
dexon-97fb08342d227bbd126516083b0ddaf74e6e8468.tar.zst
dexon-97fb08342d227bbd126516083b0ddaf74e6e8468.zip
EIP-1186 eth_getProof (#17737)
* first impl of eth_getProof * fixed docu * added comments and refactored based on comments from holiman * created structs * handle errors correctly * change Value to *hexutil.Big in order to have the same output as parity * use ProofList as return type
-rw-r--r--common/bytes.go9
-rw-r--r--core/state/statedb.go20
-rw-r--r--core/vm/interface.go10
-rw-r--r--internal/ethapi/api.go66
4 files changed, 105 insertions, 0 deletions
diff --git a/common/bytes.go b/common/bytes.go
index 0c257a1ee..c82e61624 100644
--- a/common/bytes.go
+++ b/common/bytes.go
@@ -31,6 +31,15 @@ func ToHex(b []byte) string {
return "0x" + hex
}
+// ToHexArray 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 216667ce9..b1bb53ed0 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -18,6 +18,7 @@
package state
import (
+ "errors"
"fmt"
"math/big"
"sort"
@@ -25,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
@@ -256,6 +258,24 @@ func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash
return common.Hash{}
}
+// GetProof returns the MerkleProof for a given Account
+func (self *StateDB) GetProof(a common.Address) (vm.ProofList, error) {
+ var proof vm.ProofList
+ err := self.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof)
+ return proof, err
+}
+
+// GetProof returns the StorageProof for given key
+func (self *StateDB) GetStorageProof(a common.Address, key common.Hash) (vm.ProofList, error) {
+ var proof vm.ProofList
+ trie := self.StorageTrie(a)
+ if trie == nil {
+ return proof, errors.New("storage trie for requested address does not exist")
+ }
+ err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
+ return proof, err
+}
+
// 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 fc15082f1..1ae98cf7a 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) (ProofList, error)
+ GetStorageProof(common.Address, common.Hash) (ProofList, error)
GetCode(common.Address) []byte
SetCode(common.Address, []byte)
GetCodeSize(common.Address) int
@@ -78,3 +80,11 @@ type CallContext interface {
// Create a new contract
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
}
+
+// MerkleProof
+type ProofList [][]byte
+
+func (n *ProofList) Put(key []byte, value []byte) error {
+ *n = append(*n, value)
+ return nil
+}
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 196be43e0..0c751c328 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -508,6 +508,72 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add
return (*hexutil.Big)(state.GetBalance(address)), state.Error()
}
+// Result structs for GetProof
+type AccountResult struct {
+ Address common.Address `json:"address"`
+ AccountProof []string `json:"accountProof"`
+ Balance *hexutil.Big `json:"balance"`
+ CodeHash common.Hash `json:"codeHash"`
+ Nonce hexutil.Uint64 `json:"nonce"`
+ StorageHash common.Hash `json:"storageHash"`
+ StorageProof []StorageResult `json:"storageProof"`
+}
+type StorageResult struct {
+ Key string `json:"key"`
+ Value *hexutil.Big `json:"value"`
+ Proof []string `json:"proof"`
+}
+
+// GetProof returns the Merkle-proof for a given account and optionally some storage keys.
+func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNr rpc.BlockNumber) (*AccountResult, 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([]StorageResult, len(storageKeys))
+
+ // if we have a storageTrie, (which means the account exists), we can update the storagehash
+ if storageTrie != nil {
+ storageHash = storageTrie.Hash()
+ } else {
+ // no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray.
+ codeHash = crypto.Keccak256Hash(nil)
+ }
+
+ // create the proof for the storageKeys
+ for i, key := range storageKeys {
+ if storageTrie != nil {
+ proof, storageError := state.GetStorageProof(address, common.HexToHash(key))
+ if storageError != nil {
+ return nil, storageError
+ }
+ storageProof[i] = StorageResult{key, (*hexutil.Big)(state.GetState(address, common.HexToHash(key)).Big()), common.ToHexArray(proof)}
+ } else {
+ storageProof[i] = StorageResult{key, &hexutil.Big{}, []string{}}
+ }
+ }
+
+ // create the accountProof
+ accountProof, proofErr := state.GetProof(address)
+ if proofErr != nil {
+ return nil, proofErr
+ }
+
+ return &AccountResult{
+ Address: address,
+ AccountProof: common.ToHexArray(accountProof),
+ Balance: (*hexutil.Big)(state.GetBalance(address)),
+ CodeHash: codeHash,
+ Nonce: hexutil.Uint64(state.GetNonce(address)),
+ StorageHash: storageHash,
+ StorageProof: storageProof,
+ }, 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) {