Commit a15f4a34 authored by George Hotz's avatar George Hotz Committed by GitHub

Merge pull request #6 from han0110/fix/delete-node-sister-not-resolved

Fix issue #1
parents 57f989f2 069a7e1e
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/oracle" "github.com/ethereum/go-ethereum/oracle"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
) )
var emptyCodeHash = crypto.Keccak256(nil) var emptyCodeHash = crypto.Keccak256(nil)
...@@ -248,7 +249,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has ...@@ -248,7 +249,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
meter = &s.db.StorageReads meter = &s.db.StorageReads
} }
oracle.PrefetchStorage(db.BlockNumber, s.address, key) oracle.PrefetchStorage(db.BlockNumber, s.address, key, nil)
if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil { if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil {
s.setError(err) s.setError(err)
return common.Hash{} return common.Hash{}
...@@ -354,10 +355,10 @@ func (s *stateObject) updateTrie(db Database) Trie { ...@@ -354,10 +355,10 @@ func (s *stateObject) updateTrie(db Database) Trie {
s.originStorage[key] = value s.originStorage[key] = value
var v []byte var v []byte
oracle.PrefetchStorage(db.BlockNumber, s.address, key)
//oracle.PrefetchStorage(big.NewInt(db.BlockNumber.Int64()+1), s.address, key)
if (value == common.Hash{}) { if (value == common.Hash{}) {
//fmt.Println("delete", s.address, key) //fmt.Println("delete", s.address, key)
// Get absense proof of key in case the deletion needs the sister node.
oracle.PrefetchStorage(big.NewInt(db.BlockNumber.Int64()+1), s.address, key, trie.GenPossibleShortNodePreimage)
s.setError(tr.TryDelete(key[:])) s.setError(tr.TryDelete(key[:]))
} else { } else {
//fmt.Println("update", s.address, key, value) //fmt.Println("update", s.address, key, value)
......
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/oracle" "github.com/ethereum/go-ethereum/oracle"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
) )
// for includes we don't have // for includes we don't have
...@@ -456,6 +457,8 @@ func (s *StateDB) deleteStateObject(obj *stateObject) { ...@@ -456,6 +457,8 @@ func (s *StateDB) deleteStateObject(obj *stateObject) {
} }
// Delete the account from the trie // Delete the account from the trie
addr := obj.Address() addr := obj.Address()
// Get absense proof of account in case the deletion needs the sister node.
oracle.PrefetchAccount(big.NewInt(s.db.BlockNumber.Int64()+1), addr, trie.GenPossibleShortNodePreimage)
if err := s.trie.TryDelete(addr[:]); err != nil { if err := s.trie.TryDelete(addr[:]); err != nil {
s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err)) s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err))
} }
...@@ -513,7 +516,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { ...@@ -513,7 +516,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now()) defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
} }
oracle.PrefetchAccount(s.db.BlockNumber, addr) oracle.PrefetchAccount(s.db.BlockNumber, addr, nil)
enc, err := s.trie.TryGet(addr.Bytes()) enc, err := s.trie.TryGet(addr.Bytes())
if err != nil { if err != nil {
s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr.Bytes(), err)) s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr.Bytes(), err))
......
...@@ -76,7 +76,7 @@ func Preimage(hash common.Hash) []byte { ...@@ -76,7 +76,7 @@ func Preimage(hash common.Hash) []byte {
} }
// these are stubs in embedded world // these are stubs in embedded world
func PrefetchStorage(blockNumber *big.Int, addr common.Address, skey common.Hash) {} func PrefetchStorage(*big.Int, common.Address, common.Hash, func(map[common.Hash][]byte)) {}
func PrefetchAccount(blockNumber *big.Int, addr common.Address) {} func PrefetchAccount(*big.Int, common.Address, func(map[common.Hash][]byte)) {}
func PrefetchCode(blockNumber *big.Int, addrHash common.Hash) {} func PrefetchCode(blockNumber *big.Int, addrHash common.Hash) {}
func PrefetchBlock(blockNumber *big.Int, startBlock bool, hasher types.TrieHasher) {} func PrefetchBlock(blockNumber *big.Int, startBlock bool, hasher types.TrieHasher) {}
...@@ -116,7 +116,7 @@ func unhash(addrHash common.Hash) common.Address { ...@@ -116,7 +116,7 @@ func unhash(addrHash common.Hash) common.Address {
var cached = make(map[string]bool) var cached = make(map[string]bool)
func PrefetchStorage(blockNumber *big.Int, addr common.Address, skey common.Hash) { func PrefetchStorage(blockNumber *big.Int, addr common.Address, skey common.Hash, postProcess func(map[common.Hash][]byte)) {
key := fmt.Sprintf("proof_%d_%s_%s", blockNumber, addr, skey) key := fmt.Sprintf("proof_%d_%s_%s", blockNumber, addr, skey)
if cached[key] { if cached[key] {
return return
...@@ -125,15 +125,24 @@ func PrefetchStorage(blockNumber *big.Int, addr common.Address, skey common.Hash ...@@ -125,15 +125,24 @@ func PrefetchStorage(blockNumber *big.Int, addr common.Address, skey common.Hash
ap := getProofAccount(blockNumber, addr, skey, true) ap := getProofAccount(blockNumber, addr, skey, true)
//fmt.Println("PrefetchStorage", blockNumber, addr, skey, len(ap)) //fmt.Println("PrefetchStorage", blockNumber, addr, skey, len(ap))
newPreimages := make(map[common.Hash][]byte)
for _, s := range ap { for _, s := range ap {
ret, _ := hex.DecodeString(s[2:]) ret, _ := hex.DecodeString(s[2:])
hash := crypto.Keccak256Hash(ret) hash := crypto.Keccak256Hash(ret)
//fmt.Println(" ", i, hash) //fmt.Println(" ", i, hash)
preimages[hash] = ret newPreimages[hash] = ret
}
if postProcess != nil {
postProcess(newPreimages)
}
for hash, val := range newPreimages {
preimages[hash] = val
} }
} }
func PrefetchAccount(blockNumber *big.Int, addr common.Address) { func PrefetchAccount(blockNumber *big.Int, addr common.Address, postProcess func(map[common.Hash][]byte)) {
key := fmt.Sprintf("proof_%d_%s", blockNumber, addr) key := fmt.Sprintf("proof_%d_%s", blockNumber, addr)
if cached[key] { if cached[key] {
return return
...@@ -141,10 +150,19 @@ func PrefetchAccount(blockNumber *big.Int, addr common.Address) { ...@@ -141,10 +150,19 @@ func PrefetchAccount(blockNumber *big.Int, addr common.Address) {
cached[key] = true cached[key] = true
ap := getProofAccount(blockNumber, addr, common.Hash{}, false) ap := getProofAccount(blockNumber, addr, common.Hash{}, false)
newPreimages := make(map[common.Hash][]byte)
for _, s := range ap { for _, s := range ap {
ret, _ := hex.DecodeString(s[2:]) ret, _ := hex.DecodeString(s[2:])
hash := crypto.Keccak256Hash(ret) hash := crypto.Keccak256Hash(ret)
preimages[hash] = ret newPreimages[hash] = ret
}
if postProcess != nil {
postProcess(newPreimages)
}
for hash, val := range newPreimages {
preimages[hash] = val
} }
} }
......
...@@ -18,7 +18,11 @@ func Preimage(hash common.Hash) []byte { ...@@ -18,7 +18,11 @@ func Preimage(hash common.Hash) []byte {
ioutil.WriteFile(key, val, 0644) ioutil.WriteFile(key, val, 0644)
if !ok { if !ok {
fmt.Println("can't find preimage", hash) fmt.Println("can't find preimage", hash)
panic("preimage missing")
} }
return val return val
} }
// TODO: Maybe we will want to have a seperate preimages for next block's preimages?
func Preimages() map[common.Hash][]byte {
return preimages
}
package trie package trie
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
...@@ -8,7 +9,9 @@ import ( ...@@ -8,7 +9,9 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/oracle" "github.com/ethereum/go-ethereum/oracle"
"github.com/ethereum/go-ethereum/rlp"
) )
// rawNode is a simple binary blob used to differentiate between collapsed trie // rawNode is a simple binary blob used to differentiate between collapsed trie
...@@ -34,7 +37,7 @@ func NewDatabase(header types.Header) Database { ...@@ -34,7 +37,7 @@ func NewDatabase(header types.Header) Database {
triedb := Database{BlockNumber: header.Number, Root: header.Root} triedb := Database{BlockNumber: header.Number, Root: header.Root}
//triedb.preimages = make(map[common.Hash][]byte) //triedb.preimages = make(map[common.Hash][]byte)
fmt.Println("init database") fmt.Println("init database")
oracle.PrefetchAccount(header.Number, common.Address{}) oracle.PrefetchAccount(header.Number, common.Address{}, nil)
//panic("preseed") //panic("preseed")
return triedb return triedb
...@@ -50,7 +53,10 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) { ...@@ -50,7 +53,10 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
// found in the memory cache. // found in the memory cache.
func (db *Database) node(hash common.Hash) node { func (db *Database) node(hash common.Hash) node {
//fmt.Println("node", hash) //fmt.Println("node", hash)
return mustDecodeNode(hash[:], oracle.Preimage(hash)) if val := oracle.Preimage(hash); val != nil {
return mustDecodeNode(hash[:], val)
}
return nil
} }
// insert inserts a collapsed trie node into the memory database. // insert inserts a collapsed trie node into the memory database.
...@@ -61,3 +67,36 @@ func (db *Database) insert(hash common.Hash, size int, node node) { ...@@ -61,3 +67,36 @@ func (db *Database) insert(hash common.Hash, size int, node node) {
// can put things in the oracle here if we care // can put things in the oracle here if we care
//fmt.Println("insert", hash, size) //fmt.Println("insert", hash, size)
} }
func GenPossibleShortNodePreimage(preimages map[common.Hash][]byte) {
newPreimages := make(map[common.Hash][]byte)
for _, val := range preimages {
node, err := decodeNode(nil, val)
if err != nil {
continue
}
if node, ok := node.(*shortNode); ok {
for i := len(node.Key) - 1; i > 0; i-- {
n := shortNode{
Key: hexToCompact(node.Key[i:]),
Val: node.Val,
}
buf := new(bytes.Buffer)
if err := rlp.Encode(buf, n); err != nil {
panic("encode error: " + err.Error())
}
preimage := buf.Bytes()
if len(preimage) < 32 {
continue
}
newPreimages[crypto.Keccak256Hash(preimage)] = preimage
}
}
}
for hash, val := range newPreimages {
preimages[hash] = val
}
}
...@@ -443,11 +443,10 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { ...@@ -443,11 +443,10 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
// might not be loaded yet, resolve it just for this // might not be loaded yet, resolve it just for this
// check. // check.
// remove this optimisticly? if it's not a shortNode, it doesn't do anything // When node is not resolved in next block's absence proof,
cnode, err := t.resolve(n.Children[pos], prefix) // it must be an extension node if the state transition is
if err != nil { // valid, so we ignore the error here.
return false, nil, err cnode, _ := t.resolve(n.Children[pos], prefix)
}
if cnode, ok := cnode.(*shortNode); ok { if cnode, ok := cnode.(*shortNode); ok {
k := append([]byte{byte(pos)}, cnode.Key...) k := append([]byte{byte(pos)}, cnode.Key...)
return true, &shortNode{k, cnode.Val, t.newFlag()}, nil return true, &shortNode{k, cnode.Val, t.newFlag()}, nil
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment