forked from cerc-io/plugeth
ca376ead88
This PR implements the new LES protocol version extensions: * new and more efficient Merkle proofs reply format (when replying to a multiple Merkle proofs request, we just send a single set of trie nodes containing all necessary nodes) * BBT (BloomBitsTrie) works similarly to the existing CHT and contains the bloombits search data to speed up log searches * GetTxStatusMsg returns the inclusion position or the pending/queued/unknown state of a transaction referenced by hash * an optional signature of new block data (number/hash/td) can be included in AnnounceMsg to provide an option for "very light clients" (mobile/embedded devices) to skip expensive Ethash check and accept multiple signatures of somewhat trusted servers (still a lot better than trusting a single server completely and retrieving everything through RPC). The new client mode is not implemented in this PR, just the protocol extension.
145 lines
4.1 KiB
Go
145 lines
4.1 KiB
Go
// Copyright 2015 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package trie
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
)
|
|
|
|
// Prove constructs a merkle proof for key. The result contains all
|
|
// encoded nodes on the path to the value at key. The value itself is
|
|
// also included in the last node and can be retrieved by verifying
|
|
// the proof.
|
|
//
|
|
// If the trie does not contain a value for key, the returned proof
|
|
// contains all nodes of the longest existing prefix of the key
|
|
// (at least the root node), ending with the node that proves the
|
|
// absence of the key.
|
|
func (t *Trie) Prove(key []byte, fromLevel uint, proofDb DatabaseWriter) error {
|
|
// Collect all nodes on the path to key.
|
|
key = keybytesToHex(key)
|
|
nodes := []node{}
|
|
tn := t.root
|
|
for len(key) > 0 && tn != nil {
|
|
switch n := tn.(type) {
|
|
case *shortNode:
|
|
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
|
|
// The trie doesn't contain the key.
|
|
tn = nil
|
|
} else {
|
|
tn = n.Val
|
|
key = key[len(n.Key):]
|
|
}
|
|
nodes = append(nodes, n)
|
|
case *fullNode:
|
|
tn = n.Children[key[0]]
|
|
key = key[1:]
|
|
nodes = append(nodes, n)
|
|
case hashNode:
|
|
var err error
|
|
tn, err = t.resolveHash(n, nil)
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
|
|
return err
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
|
|
}
|
|
}
|
|
hasher := newHasher(0, 0)
|
|
for i, n := range nodes {
|
|
// Don't bother checking for errors here since hasher panics
|
|
// if encoding doesn't work and we're not writing to any database.
|
|
n, _, _ = hasher.hashChildren(n, nil)
|
|
hn, _ := hasher.store(n, nil, false)
|
|
if hash, ok := hn.(hashNode); ok || i == 0 {
|
|
// If the node's database encoding is a hash (or is the
|
|
// root node), it becomes a proof element.
|
|
if fromLevel > 0 {
|
|
fromLevel--
|
|
} else {
|
|
enc, _ := rlp.EncodeToBytes(n)
|
|
if !ok {
|
|
hash = crypto.Keccak256(enc)
|
|
}
|
|
proofDb.Put(hash, enc)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VerifyProof checks merkle proofs. The given proof must contain the
|
|
// value for key in a trie with the given root hash. VerifyProof
|
|
// returns an error if the proof contains invalid trie nodes or the
|
|
// wrong value.
|
|
func VerifyProof(rootHash common.Hash, key []byte, proofDb DatabaseReader) (value []byte, err error, nodes int) {
|
|
key = keybytesToHex(key)
|
|
wantHash := rootHash[:]
|
|
for i := 0; ; i++ {
|
|
buf, _ := proofDb.Get(wantHash)
|
|
if buf == nil {
|
|
return nil, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash[:]), i
|
|
}
|
|
n, err := decodeNode(wantHash, buf, 0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("bad proof node %d: %v", i, err), i
|
|
}
|
|
keyrest, cld := get(n, key)
|
|
switch cld := cld.(type) {
|
|
case nil:
|
|
// The trie doesn't contain the key.
|
|
return nil, nil, i
|
|
case hashNode:
|
|
key = keyrest
|
|
wantHash = cld
|
|
case valueNode:
|
|
return cld, nil, i + 1
|
|
}
|
|
}
|
|
}
|
|
|
|
func get(tn node, key []byte) ([]byte, node) {
|
|
for {
|
|
switch n := tn.(type) {
|
|
case *shortNode:
|
|
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
|
|
return nil, nil
|
|
}
|
|
tn = n.Val
|
|
key = key[len(n.Key):]
|
|
case *fullNode:
|
|
tn = n.Children[key[0]]
|
|
key = key[1:]
|
|
case hashNode:
|
|
return key, n
|
|
case nil:
|
|
return key, nil
|
|
case valueNode:
|
|
return nil, n
|
|
default:
|
|
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
|
|
}
|
|
}
|
|
}
|