mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-13 07:23:46 +00:00
feat: add validation for contract storage and contract bytecode
This commit is contained in:
parent
df86d197c1
commit
5679398f40
3 changed files with 102 additions and 7 deletions
|
|
@ -107,9 +107,9 @@ func (h *StateNetwork) validateContent(contentKey []byte, content []byte) error
|
||||||
case AccountTrieNodeType:
|
case AccountTrieNodeType:
|
||||||
return h.validateAccountTrieNode(contentKey[1:], content)
|
return h.validateAccountTrieNode(contentKey[1:], content)
|
||||||
case ContractStorageTrieNodeType:
|
case ContractStorageTrieNodeType:
|
||||||
return validateContractStorageTrieNode(h.spec, contentKey[1:], content)
|
return h.validateContractStorageTrieNode(contentKey[1:], content)
|
||||||
case ContractByteCodeType:
|
case ContractByteCodeType:
|
||||||
return validateContractByteCode(h.spec, contentKey[1:], content)
|
return h.validateContractByteCode(contentKey[1:], content)
|
||||||
}
|
}
|
||||||
return errors.New("unknown content type")
|
return errors.New("unknown content type")
|
||||||
}
|
}
|
||||||
|
|
@ -135,11 +135,53 @@ func (h *StateNetwork) validateAccountTrieNode(contentKey []byte, content []byte
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateContractStorageTrieNode(spec *common.Spec, contentKey []byte, content []byte) error {
|
func (h *StateNetwork) validateContractStorageTrieNode(contentKey []byte, content []byte) error {
|
||||||
return nil
|
contractStorageKey := &ContractStorageTrieNodeKey{}
|
||||||
|
err := contractStorageKey.Deserialize(codec.NewDecodingReader(bytes.NewReader(contentKey), uint64(len(contentKey))))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
contractProof := &ContractStorageTrieNodeWithProof{}
|
||||||
|
err = contractProof.Deserialize(codec.NewDecodingReader(bytes.NewReader(content), uint64(len(content))))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stateRoot, err := h.getStateRoot(contractProof.BlockHash)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountState, err := validateAccountState(stateRoot, contractStorageKey.AddressHash, &contractProof.AccountProof)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = validateNodeTrieProof(common.Bytes32(accountState.Root), contractStorageKey.NodeHash, &contractStorageKey.Path, &contractProof.StoregeProof)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateContractByteCode(spec *common.Spec, contentKey []byte, content []byte) error {
|
func (h *StateNetwork) validateContractByteCode(contentKey []byte, content []byte) error {
|
||||||
|
contractByteCodeKey := &ContractBytecodeKey{}
|
||||||
|
err := contractByteCodeKey.Deserialize(codec.NewDecodingReader(bytes.NewReader(contentKey), uint64(len(contentKey))))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
contractBytecodeWithProof := &ContractBytecodeWithProof{}
|
||||||
|
err = contractBytecodeWithProof.Deserialize(codec.NewDecodingReader(bytes.NewReader(content), uint64(len(content))))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stateRoot, err := h.getStateRoot(contractBytecodeWithProof.BlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accountState, err := validateAccountState(stateRoot, contractByteCodeKey.AddressHash, &contractBytecodeWithProof.AccountProof)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(accountState.CodeHash, contractByteCodeKey.CodeHash[:]) {
|
||||||
|
return errors.New("account state is invalid")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,6 +229,27 @@ func validateNodeTrieProof(rootHash common.Bytes32, nodeHash common.Bytes32, pat
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateAccountState(rootHash common.Bytes32, addrrssHash common.Bytes32, proof *TrieProof) (*types.StateAccount, error) {
|
||||||
|
path := make([]byte, 0, len(addrrssHash)*2)
|
||||||
|
for _, item := range addrrssHash {
|
||||||
|
before, after := unpackNibblePair(item)
|
||||||
|
path = append(path, before, after)
|
||||||
|
}
|
||||||
|
lastProof, p, err := validateTrieProof(rootHash, path, proof)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n, err := trie.DecodeTrieNode(nil, lastProof)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stateBytes, _, err := trie.TraverseTrieNode(n, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return types.FullAccount(stateBytes)
|
||||||
|
}
|
||||||
|
|
||||||
func validateTrieProof(rootHash common.Bytes32, path []byte, proof *TrieProof) (EncodedTrieNode, []byte, error) {
|
func validateTrieProof(rootHash common.Bytes32, path []byte, proof *TrieProof) (EncodedTrieNode, []byte, error) {
|
||||||
if len(*proof) == 0 {
|
if len(*proof) == 0 {
|
||||||
return nil, nil, errors.New("proof should be empty")
|
return nil, nil, errors.New("proof should be empty")
|
||||||
|
|
|
||||||
|
|
@ -71,3 +71,35 @@ func TestValidateAccountTrieNode(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateContractStorage(t *testing.T) {
|
||||||
|
cases, err := getTestCases("contract_storage_trie_node.yaml")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, tt := range cases {
|
||||||
|
server := rpc.NewServer()
|
||||||
|
api := &MockAPI{
|
||||||
|
header: tt.BlockHeader,
|
||||||
|
}
|
||||||
|
server.RegisterName("portal", api)
|
||||||
|
bn := NewStateNetwork(nil, server)
|
||||||
|
err = bn.validateContent(hexutil.MustDecode(tt.ContentKey), hexutil.MustDecode(tt.ContentValueOffer))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateContractByte(t *testing.T) {
|
||||||
|
cases, err := getTestCases("contract_bytecode.yaml")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, tt := range cases {
|
||||||
|
server := rpc.NewServer()
|
||||||
|
api := &MockAPI{
|
||||||
|
header: tt.BlockHeader,
|
||||||
|
}
|
||||||
|
server.RegisterName("portal", api)
|
||||||
|
bn := NewStateNetwork(nil, server)
|
||||||
|
err = bn.validateContent(hexutil.MustDecode(tt.ContentKey), hexutil.MustDecode(tt.ContentValueOffer))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func DecodeTrieNode(hash, buf []byte) (node, error) {
|
||||||
return decodeNodeUnsafe(hash, buf)
|
return decodeNodeUnsafe(hash, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TraverseTrieNode(node node, path []byte) (hashNode, []byte, error) {
|
func TraverseTrieNode(node node, path []byte) ([]byte, []byte, error) {
|
||||||
switch v := node.(type) {
|
switch v := node.(type) {
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
first := path[0]
|
first := path[0]
|
||||||
|
|
@ -33,7 +33,7 @@ func TraverseTrieNode(node node, path []byte) (hashNode, []byte, error) {
|
||||||
if !bytes.Equal(prePath, path) {
|
if !bytes.Equal(prePath, path) {
|
||||||
return nil, nil, ErrDifferentLeafPrefix
|
return nil, nil, ErrDifferentLeafPrefix
|
||||||
}
|
}
|
||||||
return (v.Val).(hashNode), path, nil
|
return (v.Val).(valueNode), path, nil
|
||||||
} else {
|
} else {
|
||||||
for index, key := range v.Key {
|
for index, key := range v.Key {
|
||||||
if path[index] != key {
|
if path[index] != key {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue