先实现一个rlp-decoder
package rlp
import(
"fmt"
"errors"
"encoding/hex"
)
const(
NIL = iota
BYTES
GLIST
)
type Atom interface{}
type Glist struct {
dat Atom
typeId int
}
func makeGlistContentGlist() *Glist {
return &Glist{dat: make([]Atom, 0, 65535), typeId: 2}
}
func makeGlistContentBytes(data []byte) *Glist {
return &Glist{dat: data, typeId: 1}
}
func (g *Glist) Type() int{
return g.typeId
}
func _Unserialize(dtr []byte) []Atom {
atomList := make([]Atom, 0, 65535)
if len(dtr) == 0 {
return atomList
}
if(dtr[0] < 128){
return append(append(atomList, makeGlistContentBytes(dtr[0 : 1])), _Unserialize(dtr[1 : ])...)
}else if dtr[0] < 184 {
return append(append(atomList, makeGlistContentBytes(dtr[1 : dtr[0] - 127])), _Unserialize(dtr[dtr[0] - 127 : ])...)
}else if dtr[0] < 192 {
// dlen = truelen + 1
dlen := int(dtr[0]) - 182
tlen := decodelength(dtr[1 : dlen])
return append(append(atomList, makeGlistContentBytes(dtr[dlen : dlen + tlen])), _Unserialize(dtr[dlen + tlen : ])...)
//return &Glist{dat:, typeId: 1}
}else if dtr[0] < 248 {
// dlen = truelen + 1
return append(append(atomList, Unserialize(dtr[0 : dtr[0] - 191])), _Unserialize(dtr[dtr[0] - 191 : ])...)
}else {
// dlen = truelen + 1
dlen := int(dtr[0]) - 246
tlen := decodelength(dtr[1 : dlen])
// fmt.Println("here",dlen,decodelength(dtr[1 : dlen]))
return append(append(atomList, Unserialize(dtr[0 : dlen + tlen])), _Unserialize(dtr[dlen + tlen : ])...)
}
return atomList
}
func Unserialize(dtr []byte) *Glist {
if len(dtr) == 0 {
return makeGlistContentGlist()
}
if(dtr[0] < 128){
if len(dtr) > 1 {
err := errors.New("superflours")
fmt.Println(err)
return nil
}
return makeGlistContentBytes(dtr[0 : ])
}else if dtr[0] < 184 {
if len(dtr) > int(dtr[0] - 127) {
err := errors.New("superflours")
fmt.Println(err)
return nil
}
return makeGlistContentBytes(dtr[1 : ])
}else if dtr[0] < 192 {
// dlen = truelen + 1
//must be uint32, but int for convenience
dlen := int(dtr[0]) - 182
tlen := decodelength(dtr[1 : dlen])
if len(dtr) > tlen + dlen {
err := errors.New("superflours")
fmt.Println(err)
return nil
}
//fmt.Println(decodelength(dtr[1 : dlen]))
return makeGlistContentBytes(dtr[dlen : ])
//return &Glist{dat:, typeId: 1}
}else if dtr[0] < 248 {
// dlen = truelen + 1
// fmt.Println(dtr[0] - 191)
if len(dtr) > int(dtr[0] - 191) {
err := errors.New("superflours")
fmt.Println(err)
return nil
}
return &Glist{dat: _Unserialize(dtr[1 : ]), typeId: 2}
}else {
// dlen = truelen + 1
//must be uint32, but int for convenience
dlen := int(dtr[0]) - 246
tlen := decodelength(dtr[1 : dlen])
if len(dtr) > tlen + dlen {
err := errors.New("superflours")
fmt.Println(err)
return nil
}
return &Glist{dat: _Unserialize(dtr[dlen : ]), typeId: 2}
}
}
func decodelength(dtr []byte) int {
var len int
for _, t := range dtr {
len = (len<<4) | int(t)
}
return len
}
func PrintList(g *Glist){
if g == nil {
fmt.Print("[]")
return ;
}
switch g.typeId {
case 1:{
fmt.Print(g.dat.([]byte))
break ;
}
case 2:{
fmt.Print("[")
for i, v := range(g.dat.([]Atom)) {
if i != 0 {
fmt.Print(",")
}
PrintList(v.(*Glist))
}
fmt.Print("]")
break;
}
default :{
break ;
}
}
}
func PrintListInString(g *Glist){
if g == nil {
fmt.Print("~")
return ;
}
switch g.typeId {
case 1:{
fmt.Print("\"0x"+hex.EncodeToString(g.dat.([]byte))+"\"")
break ;
}
case 2:{
fmt.Print("[")
for i, v := range(g.dat.([]Atom)) {
if i != 0 {
fmt.Print(",")
}
PrintListInString(v.(*Glist))
}
fmt.Print("]")
break;
}
default :{
break ;
}
}
}
func (g *Glist) Get(ref int) *Glist {
return g.dat.([]Atom)[ref].(*Glist)
}
func (g *Glist) AsBytes() []byte {
return g.dat.([]byte)
}
func (g *Glist) AsString() string {
return hex.EncodeToString(g.dat.([]byte))
}
func (g *Glist) Length() int {
if g.typeId == 1 {
return len(g.dat.([]byte))
}else if g.typeId == 2 {
return len(g.dat.([]Atom))
}else {
return -1
}
}
func main(){}
下面是用go-leveldb连接到"/geth/chaindata"数据库完成verifyproof的主文件
//link to Goleveldb
package main
import (
"github.com/syndtr/goleveldb/leveldb"
"fmt"
"errors"
"./rlp"
)
const EDB_PATH = "D:/Go Ethereum/data/geth/chaindata"
var (
//Got LastHeader
headHeaderKey = []byte("LastHeader")
//Got LastBlock
headBlockKey = []byte("LastBlock")
//Got a BlockHeader
headerPrefix = []byte("h")
)
//Hash byte-Array's length
const HashLength = 32
//char maps to bit integer
var hexmaps [128]uint64
//common.Hash
type Hash [HashLength]byte
func (h Hash) bytes() []byte { return h[ : ] }
//TODO - iterator
func checkAll(db *leveldb.DB){
iter := db.NewIterator(nil, nil)
for iter.Next(){
key := iter.Key()
value := iter.Value()
fmt.Println([]byte(key),[]byte(value))
}
}
//unint64(8-bytes) to bytes
func uint64toslice(number uint64) []byte {
enc := make([]byte, 8)
for idx := uint64(0); idx < 8; idx++ {
enc[7 - idx] = byte((number >> (idx << 3) ) & 0xff)
}
//fmt.Println(enc);
return enc
}
//input a string and return common.Hash
func stringtohash(hashstr string) Hash {
var hres Hash
ofs := uint64(0)
if hashstr[1] == 'x' {
ofs = 1
}
for idx := uint64(0); idx < 32; idx++ {
hres[idx] |= byte(hexmaps[hashstr[ (idx + ofs) << 1 ]] << 4)
hres[idx] |= byte(hexmaps[hashstr[((idx + ofs) << 1) | 1]])
}
return hres
}
//input a string and return common.Hash
func stringtobytes(bytes string) []byte {
glen := len(bytes)
if glen <= 1 || ((glen & 1) == 1) {
return nil
}
glen >>= 1
bres ,ofs := make([]byte, glen, glen), 0
if bytes[1] == 'x' {
ofs = 2
}
for idx := int(ofs); idx < glen; idx++ {
bres[idx] |= byte(hexmaps[bytes[(idx + ofs) << 1 ]] << 4)
bres[idx] |= byte(hexmaps[bytes[(idx + ofs) << 1 | 1]])
}
return bres
}
func stringtoheaderkey(number uint64, hashstr string) []byte {
return append(append(headerPrefix, uint64toslice(number)...), stringtohash(hashstr).bytes()...);
}
func Cook(db *leveldb.DB, query []byte) ([]byte, error) {
binfo, err := db.Get(query, nil)
if err == nil {
return binfo, nil
}else {
return nil, err
}
}
func findPath(db *leveldb.DB, rootHashStr string, key []byte) ([]byte, error) {
roothash := stringtohash(rootHashStr)
querynode, err := Cook(db, roothash[ : ])
if err != nil {
return nil, err
}else {
if len(key) == 0 {
return querynode, nil
}
node := rlp.Unserialize(querynode)
switch node.Length() {
case 2: {
if len(key) < len(node.Get(0).AsBytes()) || Compare(key, node.Get(0).AsBytes()) != 0 {
return nil, errors.New("No exists")
}
return findPath(db, node.Get(1).AsString(), key[len(node.Get(0).AsBytes()) : ])
break ;
}
case 17: {
tryquery := node.Get(int(key[0])).AsString()
if len(tryquery) == 64 {
return findPath(db, tryquery, key[1 : ])
}else{
err = errors.New("No exists")
return nil, err
}
break ;
}
default: {
err := errors.New("Unknown node types")
return nil, err
}
}
}
err = errors.New("Impossible approach")
return nil, err
}
func VerifyProof(db *leveldb.DB, rootHashStr string, key []byte) {
toval, err := findPath(db, rootHashStr, key)
if err != nil {
fmt.Println(err)
}else {
rlp.PrintListInString(rlp.Unserialize(toval))
}
}
func init() {
for idx := '0'; idx <= '9'; idx++ {
hexmaps[idx] = uint64(idx - '0')
}
for idx := 'a'; idx <= 'f'; idx++ {
hexmaps[idx] = uint64(idx - 'a' + 10)
}
for idx := 'A'; idx <= 'F'; idx++ {
hexmaps[idx] = uint64(idx - 'A' + 10)
}
}
func main() {
StorageHash := "0x11e91152ab237ceff29728c03999ef2debadd7db0fc45b280657c6f7cc4c1ffa"
StoragePath := append(make([]byte,0), 2, 9)
db, err := leveldb.OpenFile(EDB_PATH, nil)
if err != nil {
fmt.Println("link error")
fmt.Println(err)
}else {
defer db.Close()
//fmt.Println(StorageHash,StoragePath)
VerifyProof(db, StorageHash, StoragePath)
}
}