Commit 07a7b4d3 authored by protolambda's avatar protolambda

mipsevm: rm trie.go

parent a4336f8e
package main
import (
"encoding/binary"
"encoding/json"
"fmt"
"sort"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
type PreimageKeyValueWriter struct{}
var Preimages = make(map[common.Hash][]byte)
type Jtree struct {
Root common.Hash `json:"root"`
Step int `json:"step"`
Preimages map[common.Hash][]byte `json:"preimages"`
}
func TrieToJson(root common.Hash, step int) []byte {
b, err := json.Marshal(Jtree{Preimages: Preimages, Step: step, Root: root})
check(err)
return b
}
func TrieFromJson(dat []byte) (common.Hash, int) {
var j Jtree
err := json.Unmarshal(dat, &j)
check(err)
Preimages = j.Preimages
return j.Root, j.Step
}
// TODO: this is copied from the oracle
func (kw PreimageKeyValueWriter) Put(key []byte, value []byte) error {
hash := crypto.Keccak256Hash(value)
if hash != common.BytesToHash(key) {
panic("bad preimage value write")
}
Preimages[hash] = common.CopyBytes(value)
return nil
}
func (kw PreimageKeyValueWriter) Delete(key []byte) error {
delete(Preimages, common.BytesToHash(key))
return nil
}
func ParseNodeInternal(elems []byte, depth int, callback func(common.Hash) []byte) {
sprefix := strings.Repeat(" ", depth)
c, _ := rlp.CountValues(elems)
fmt.Println(sprefix, "parsing", depth, "elements", c)
rest := elems
for i := 0; i < c; i++ {
kind, val, lrest, err := rlp.Split(rest)
rest = lrest
check(err)
if len(val) > 0 {
fmt.Println(sprefix, i, kind, val, len(val))
}
if len(val) == 32 {
hh := common.BytesToHash(val)
//fmt.Println(sprefix, "node found with len", len(Preimages[hh]))
ParseNode(hh, depth+1, callback)
}
if kind == rlp.List && len(val) > 0 && len(val) < 32 {
ParseNodeInternal(val, depth+1, callback)
}
}
}
// full nodes / BRANCH_NODE have 17 values, each a hash
// LEAF or EXTENSION nodes have 2 values, a path and value
func ParseNode(node common.Hash, depth int, callback func(common.Hash) []byte) {
if depth > 4 {
return
}
buf := callback(node)
//fmt.Println("callback", node, len(buf), hex.EncodeToString(buf))
elems, _, err := rlp.SplitList(buf)
check(err)
ParseNodeInternal(elems, depth, callback)
}
func RamFromTrie(root common.Hash) map[uint32](uint32) {
ram := make(map[uint32](uint32))
// load into oracle
// TODO: broken, need pre-image integration with fault-proof-program
//pp := oracle.Preimages()
//for k, v := range Preimages {
// pp[k] = v
//}
triedb := trie.Database{Root: root}
tt, err := trie.New(root, &triedb)
check(err)
tni := tt.NodeIterator([]byte{})
for tni.Next(true) {
if tni.Leaf() {
tk := binary.BigEndian.Uint32(tni.LeafKey())
tv := binary.BigEndian.Uint32(tni.LeafBlob())
ram[tk*4] = tv
}
}
return ram
}
func RamToTrie(ram map[uint32](uint32)) common.Hash {
mt := trie.NewStackTrie(PreimageKeyValueWriter{})
sram := make([]uint64, len(ram))
i := 0
for k, v := range ram {
sram[i] = (uint64(k) << 32) | uint64(v)
i += 1
}
sort.Slice(sram, func(i, j int) bool { return sram[i] < sram[j] })
for _, kv := range sram {
k, v := uint32(kv>>32), uint32(kv)
k >>= 2
//fmt.Printf("insert %x = %x\n", k, v)
tk := make([]byte, 4)
tv := make([]byte, 4)
binary.BigEndian.PutUint32(tk, k)
binary.BigEndian.PutUint32(tv, v)
mt.Update(tk, tv)
}
mt.Commit()
/*fmt.Println("ram hash", mt.Hash())
fmt.Println("hash count", len(Preimages))
parseNode(mt.Hash(), 0)*/
return mt.Hash()
}
package main
import (
"fmt"
"io/ioutil"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
)
// go test -run TestTrie
func TestToTrie(t *testing.T) {
ram := make(map[uint32](uint32))
LoadMappedFile("test/bin/oracle.bin", ram, 0)
ZeroRegisters(ram)
ram[0xC000007C] = 0x5EAD0000
root := RamToTrie(ram)
dat := TrieToJson(root, -1)
fmt.Println("serialized length is", len(dat))
ioutil.WriteFile("/tmp/cannon/oracletest.json", dat, 0644)
}
func TestTrie(t *testing.T) {
ram := make(map[uint32](uint32))
LoadMappedFile("../mipigo/test/test.bin", ram, 0)
ZeroRegisters(ram)
root := RamToTrie(ram)
//ParseNode(root, 0)
dat := TrieToJson(root, -1)
fmt.Println("serialized length is", len(dat))
ioutil.WriteFile("/tmp/cannon/ramtrie.json", dat, 0644)
// load the trie
oldPreLen := len(Preimages)
Preimages = make(map[common.Hash][]byte)
dat, err := ioutil.ReadFile("/tmp/cannon/ramtrie.json")
check(err)
newroot, _ := TrieFromJson(dat)
if root != newroot {
t.Fatal("loaded root mismatch")
}
if len(Preimages) != oldPreLen {
t.Fatal("preimage length mismatch")
}
// load memory
newram := RamFromTrie(newroot)
if !reflect.DeepEqual(ram, newram) {
t.Fatal("ram to/from mismatch")
}
}
func printRoot(ram map[uint32](uint32)) {
root := RamToTrie(ram)
fmt.Println("root =", root)
}
func printTrie(ram map[uint32](uint32)) {
root := RamToTrie(ram)
fmt.Println("root =", root)
ParseNode(root, 0, func(t common.Hash) []byte {
return Preimages[t]
})
}
func TestToFromTrie(t *testing.T) {
ram := make(map[uint32](uint32))
ram[0] = 1
ram[4] = 2
trie := RamToTrie(ram)
newram := RamFromTrie(trie)
if !reflect.DeepEqual(ram, newram) {
t.Fatal("ram to/from mismatch")
}
}
func TestBuggedTrie(t *testing.T) {
ram := make(map[uint32](uint32))
ram[0] = 1
ram[4] = 2
printTrie(ram)
ram[0x40] = 3
printTrie(ram)
ram = make(map[uint32](uint32))
ram[0x7fffd00c] = 1
ram[0x7fffd010] = 2
printTrie(ram)
ram[0x7fffcffc] = 3
printTrie(ram)
}
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