From cb56808a9c8c23d95570c2eb4e5a10aa6c15695d Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Fri, 23 May 2014 17:49:28 -0700 Subject: [PATCH] persistence --- README.md | 2 - merkle/db.go => db/level_db.go | 26 +++--- db/mem_db.go | 32 +++++++ merkle/binary.go | 52 +++++++++++ merkle/iavl.go | 153 +++++++++++++++++++++++++++++---- merkle/iavl_test.go | 47 ++++++---- merkle/int.go | 128 +++++++++++++-------------- merkle/string.go | 26 +++--- merkle/types.go | 16 ++-- merkle/util.go | 16 ++++ 10 files changed, 361 insertions(+), 137 deletions(-) rename merkle/db.go => db/level_db.go (51%) create mode 100644 db/mem_db.go create mode 100644 merkle/binary.go diff --git a/README.md b/README.md index ad963f461..398ccfb63 100644 --- a/README.md +++ b/README.md @@ -1,3 +1 @@ TenderMint - proof of concept - -* merklelized AVL tree based on [timtadh's code](https://github.com/timtadh/data-structures) diff --git a/merkle/db.go b/db/level_db.go similarity index 51% rename from merkle/db.go rename to db/level_db.go index 74c180a3a..b2346130b 100644 --- a/merkle/db.go +++ b/db/level_db.go @@ -1,4 +1,4 @@ -package merkle +package db import ( "fmt" @@ -6,49 +6,49 @@ import ( "path" ) -type LDBDatabase struct { +type LevelDB struct { db *leveldb.DB } -func NewLDBDatabase(name string) (*LDBDatabase, error) { +func NewLevelDB(name string) (*LevelDB, error) { dbPath := path.Join(name) db, err := leveldb.OpenFile(dbPath, nil) if err != nil { return nil, err } - database := &LDBDatabase{db: db} + database := &LevelDB{db: db} return database, nil } -func (db *LDBDatabase) Put(key []byte, value []byte) { +func (db *LevelDB) Put(key []byte, value []byte) { err := db.db.Put(key, value, nil) if err != nil { panic(err) } } -func (db *LDBDatabase) Get(key []byte) ([]byte) { +func (db *LevelDB) Get(key []byte) ([]byte) { res, err := db.db.Get(key, nil) if err != nil { panic(err) } return res } -func (db *LDBDatabase) Delete(key []byte) error { - return db.db.Delete(key, nil) +func (db *LevelDB) Delete(key []byte) { + err := db.db.Delete(key, nil) + if err != nil { panic(err) } } -func (db *LDBDatabase) Db() *leveldb.DB { +func (db *LevelDB) Db() *leveldb.DB { return db.db } -func (db *LDBDatabase) Close() { +func (db *LevelDB) Close() { db.db.Close() } -func (db *LDBDatabase) Print() { +func (db *LevelDB) Print() { iter := db.db.NewIterator(nil, nil) for iter.Next() { key := iter.Key() value := iter.Value() - fmt.Printf("%x(%d): %v ", key, len(key), value) + fmt.Printf("[%x]:\t[%x]", key, value) } } - diff --git a/db/mem_db.go b/db/mem_db.go new file mode 100644 index 000000000..9a84882e0 --- /dev/null +++ b/db/mem_db.go @@ -0,0 +1,32 @@ +package db + +import ( + "fmt" +) + +type MemDB struct { + db map[string][]byte +} + +func NewMemDB() (*MemDB) { + database := &MemDB{db:make(map[string][]byte)} + return database +} + +func (db *MemDB) Put(key []byte, value []byte) { + db.db[string(key)] = value +} + +func (db *MemDB) Get(key []byte) ([]byte) { + return db.db[string(key)] +} + +func (db *MemDB) Delete(key []byte) { + delete(db.db, string(key)) +} + +func (db *MemDB) Print() { + for key, value := range db.db { + fmt.Printf("[%x]:\t[%x]", []byte(key), value) + } +} diff --git a/merkle/binary.go b/merkle/binary.go new file mode 100644 index 000000000..6d91c4761 --- /dev/null +++ b/merkle/binary.go @@ -0,0 +1,52 @@ +package merkle + +const ( + TYPE_BYTE = byte(0x00) + TYPE_INT8 = byte(0x02) + TYPE_UINT8 = byte(0x03) + TYPE_INT16 = byte(0x04) + TYPE_UINT16 = byte(0x05) + TYPE_INT32 = byte(0x06) + TYPE_UINT32 = byte(0x07) + TYPE_INT64 = byte(0x08) + TYPE_UINT64 = byte(0x09) + TYPE_STRING = byte(0x10) + TYPE_BYTESLICE = byte(0x11) +) + +func GetBinaryType(o Binary) byte { + switch o.(type) { + case Byte: return TYPE_BYTE + case Int8: return TYPE_INT8 + case UInt8: return TYPE_UINT8 + case Int16: return TYPE_INT16 + case UInt16: return TYPE_UINT16 + case Int32: return TYPE_INT32 + case UInt32: return TYPE_UINT32 + case Int64: return TYPE_INT64 + case UInt64: return TYPE_UINT64 + case Int: panic("Int not supported") + case UInt: panic("UInt not supported") + case String: return TYPE_STRING + case ByteSlice: return TYPE_BYTESLICE + default: panic("Unsupported type") + } +} + +func LoadBinary(buf []byte, start int) (Binary, int) { + typeByte := buf[start] + switch typeByte { + case TYPE_BYTE: return LoadByte(buf[start+1:]), start+2 + case TYPE_INT8: return LoadInt8(buf[start+1:]), start+2 + case TYPE_UINT8: return LoadUInt8(buf[start+1:]), start+2 + case TYPE_INT16: return LoadInt16(buf[start+1:]), start+3 + case TYPE_UINT16: return LoadUInt16(buf[start+1:]), start+3 + case TYPE_INT32: return LoadInt32(buf[start+1:]), start+5 + case TYPE_UINT32: return LoadUInt32(buf[start+1:]), start+5 + case TYPE_INT64: return LoadInt64(buf[start+1:]), start+9 + case TYPE_UINT64: return LoadUInt64(buf[start+1:]), start+9 + case TYPE_STRING: return LoadString(buf, start+1) + case TYPE_BYTESLICE:return LoadByteSlice(buf, start+1) + default: panic("Unsupported type") + } +} diff --git a/merkle/iavl.go b/merkle/iavl.go index fb9287b66..209900644 100644 --- a/merkle/iavl.go +++ b/merkle/iavl.go @@ -9,11 +9,21 @@ const HASH_BYTE_SIZE int = 4+32 // Immutable AVL Tree (wraps the Node root) type IAVLTree struct { - root *IAVLNode + db Db + root *IAVLNode } -func NewIAVLTree() *IAVLTree { - return &IAVLTree{} +func NewIAVLTree(db Db) *IAVLTree { + return &IAVLTree{db:db, root:nil} +} + +func NewIAVLTreeFromHash(db Db, hash ByteSlice) *IAVLTree { + root := &IAVLNode{ + hash: hash, + flags: IAVLNODE_FLAG_PERSISTED | IAVLNODE_FLAG_PLACEHOLDER, + } + root.fill(db) + return &IAVLTree{db:db, root:root} } func (self *IAVLTree) Root() Node { @@ -29,23 +39,30 @@ func (self *IAVLTree) Height() uint8 { } func (self *IAVLTree) Has(key Key) bool { - return self.root.Has(nil, key) + return self.root.Has(self.db, key) } func (self *IAVLTree) Put(key Key, value Value) { - self.root, _ = self.root.Put(nil, key, value) + self.root, _ = self.root.Put(self.db, key, value) } func (self *IAVLTree) Hash() (ByteSlice, uint64) { return self.root.Hash() } +func (self *IAVLTree) Save() { + if self.root.hash == nil { + self.root.Hash() + } + self.root.Save(self.db) +} + func (self *IAVLTree) Get(key Key) (value Value) { - return self.root.Get(nil, key) + return self.root.Get(self.db, key) } func (self *IAVLTree) Remove(key Key) (value Value, err error) { - new_root, value, err := self.root.Remove(nil, key) + new_root, value, err := self.root.Remove(self.db, key) if err != nil { return nil, err } @@ -60,7 +77,7 @@ type IAVLNode struct { value Value size uint64 height uint8 - hash []byte + hash ByteSlice left *IAVLNode right *IAVLNode @@ -68,6 +85,15 @@ type IAVLNode struct { flags byte } +const ( + IAVLNODE_FLAG_PERSISTED = byte(0x01) + IAVLNODE_FLAG_PLACEHOLDER = byte(0x02) + + IAVLNODE_DESC_HAS_VALUE = byte(0x01) + IAVLNODE_DESC_HAS_LEFT = byte(0x02) + IAVLNODE_DESC_HAS_RIGHT = byte(0x04) +) + func (self *IAVLNode) Copy() *IAVLNode { if self == nil { return nil @@ -84,6 +110,14 @@ func (self *IAVLNode) Copy() *IAVLNode { } } +func (self *IAVLNode) Equals(other Binary) bool { + if o, ok := other.(*IAVLNode); ok { + return self.hash.Equals(o.hash) + } else { + return false + } +} + func (self *IAVLNode) Key() Key { return self.key } @@ -150,6 +184,31 @@ func (self *IAVLNode) Hash() (ByteSlice, uint64) { return self.hash, hashCount+1 } +func (self *IAVLNode) Save(db Db) { + if self == nil { + return + } else if self.hash == nil { + panic("savee.hash can't be nil") + } + if self.flags & IAVLNODE_FLAG_PERSISTED > 0 || + self.flags & IAVLNODE_FLAG_PLACEHOLDER > 0 { + return + } + + // save self + buf := make([]byte, self.ByteSize(), self.ByteSize()) + self.SaveTo(buf) + db.Put([]byte(self.hash), buf) + + // save left + self.left.Save(db) + + // save right + self.right.Save(db) + + self.flags |= IAVLNODE_FLAG_PERSISTED +} + // TODO: don't clear the hash if the value hasn't changed. func (self *IAVLNode) Put(db Db, key Key, value Value) (_ *IAVLNode, updated bool) { if self == nil { @@ -235,12 +294,17 @@ func (self *IAVLNode) ByteSize() int { // 1 byte node neight // 8 bytes node size size := 10 + // key + size += 1 // type info size += self.key.ByteSize() + // value if self.value != nil { + size += 1 // type info size += self.value.ByteSize() } else { size += 1 } + // children if self.left != nil { size += HASH_BYTE_SIZE } @@ -261,9 +325,9 @@ func (self *IAVLNode) saveToCountHashes(buf []byte) (int, uint64) { // node descriptor nodeDesc := byte(0) - if self.value != nil { nodeDesc |= 0x01 } - if self.left != nil { nodeDesc |= 0x02 } - if self.right != nil { nodeDesc |= 0x04 } + if self.value != nil { nodeDesc |= IAVLNODE_DESC_HAS_VALUE } + if self.left != nil { nodeDesc |= IAVLNODE_DESC_HAS_LEFT } + if self.right != nil { nodeDesc |= IAVLNODE_DESC_HAS_RIGHT } cur += UInt8(nodeDesc).SaveTo(buf[cur:]) // node height & size @@ -271,13 +335,15 @@ func (self *IAVLNode) saveToCountHashes(buf []byte) (int, uint64) { cur += UInt64(self.size).SaveTo(buf[cur:]) // node key + buf[cur] = GetBinaryType(self.key) + cur += 1 cur += self.key.SaveTo(buf[cur:]) // node value if self.value != nil { + buf[cur] = GetBinaryType(self.value) + cur += 1 cur += self.value.SaveTo(buf[cur:]) - } else { - cur += UInt8(0).SaveTo(buf[cur:]) } // left child @@ -297,13 +363,68 @@ func (self *IAVLNode) saveToCountHashes(buf []byte) (int, uint64) { return cur, hashCount } +// Given a placeholder node which has only the hash set, +// load the rest of the data from db. +// Not threadsafe. +func (self *IAVLNode) fill(db Db) { + if self == nil { + panic("placeholder can't be nil") + } else if self.hash == nil { + panic("placeholder.hash can't be nil") + } + buf := db.Get(self.hash) + cur := 0 + // node header + nodeDesc := byte(LoadUInt8(buf)) + self.height = uint8(LoadUInt8(buf[1:])) + self.size = uint64(LoadUInt64(buf[2:])) + // key + key, cur := LoadBinary(buf, 10) + self.key = key.(Key) + // value + if nodeDesc & IAVLNODE_DESC_HAS_VALUE > 0 { + self.value, cur = LoadBinary(buf, cur) + } + // children + if nodeDesc & IAVLNODE_DESC_HAS_LEFT > 0 { + var leftHash ByteSlice + leftHash, cur = LoadByteSlice(buf, cur) + self.left = &IAVLNode{ + hash: leftHash, + flags: IAVLNODE_FLAG_PERSISTED | IAVLNODE_FLAG_PLACEHOLDER, + } + } + if nodeDesc & IAVLNODE_DESC_HAS_RIGHT > 0 { + var rightHash ByteSlice + rightHash, cur = LoadByteSlice(buf, cur) + self.right = &IAVLNode{ + hash: rightHash, + flags: IAVLNODE_FLAG_PERSISTED | IAVLNODE_FLAG_PLACEHOLDER, + } + } + if cur != len(buf) { + panic("buf not all consumed") + } + self.flags &= ^IAVLNODE_FLAG_PLACEHOLDER +} + func (self *IAVLNode) leftFilled(db Db) *IAVLNode { - // XXX + if self.left == nil { + return nil + } + if self.left.flags & IAVLNODE_FLAG_PLACEHOLDER > 0 { + self.left.fill(db) + } return self.left } func (self *IAVLNode) rightFilled(db Db) *IAVLNode { - // XXX + if self.right == nil { + return nil + } + if self.right.flags & IAVLNODE_FLAG_PLACEHOLDER > 0 { + self.right.fill(db) + } return self.right } @@ -315,7 +436,7 @@ func (self *IAVLNode) popNode(db Db, node *IAVLNode) (newSelf, new_node *IAVLNod } else if node == nil { panic("node can't be nil") } else if node.left != nil && node.right != nil { - panic("node must not have both left and right") + panic("node hnot have both left and right") } if self == node { diff --git a/merkle/iavl_test.go b/merkle/iavl_test.go index d3d24e825..fa94e6879 100644 --- a/merkle/iavl_test.go +++ b/merkle/iavl_test.go @@ -8,6 +8,7 @@ import ( "bytes" "math/rand" "encoding/binary" + "github.com/tendermint/tendermint/db" ) @@ -26,20 +27,6 @@ func init() { } } -func randstr(length int) String { - if urandom, err := os.Open("/dev/urandom"); err != nil { - panic(err) - } else { - slice := make([]byte, length) - if _, err := urandom.Read(slice); err != nil { - panic(err) - } - urandom.Close() - return String(slice) - } - panic("unreachable") -} - func TestImmutableAvlPutHasGetRemove(t *testing.T) { type record struct { @@ -121,7 +108,7 @@ func BenchmarkImmutableAvlTree(b *testing.B) { return &record{ randstr(32), randstr(32) } } - t := NewIAVLTree() + t := NewIAVLTree(nil) for i:=0; i<1000000; i++ { r := randomRecord() t.Put(r.key, r.value) @@ -159,7 +146,7 @@ func TestTraversals(t *testing.T) { j += 1 } } - test(NewIAVLTree()) + test(NewIAVLTree(nil)) } // from http://stackoverflow.com/questions/3955680/how-to-check-if-my-avl-tree-implementation-is-correct @@ -279,5 +266,33 @@ func TestGriffin(t *testing.T) { if P(n6) != "((1 2 (- 3 4)) 5 ((6 7 -) 8 (9 10 (- 11 12))))" { t.Fatalf("Got %v", P(n6)) } expectRemove(n6, 1, "(((2 3 4) 5 (6 7 -)) 8 (9 10 (- 11 12)))", 4) +} + +func TestPersistence(t *testing.T) { + db := db.NewMemDB() + + // Create some random key value pairs + records := make(map[String]String) + for i:=0; i<10000; i++ { + records[String(randstr(20))] = String(randstr(20)) + } + + // Construct some tree and save it + t1 := NewIAVLTree(db) + for key, value := range records { + t1.Put(key, value) + } + t1.Save() + + hash, _ := t1.Hash() + + // Load a tree + t2 := NewIAVLTreeFromHash(db, hash) + for key, value := range records { + t2value := t2.Get(key) + if !t2value.Equals(value) { + t.Fatalf("Invalid value. Expected %v, got %v", value, t2value) + } + } } diff --git a/merkle/int.go b/merkle/int.go index d6d6a09a7..0c757ba80 100644 --- a/merkle/int.go +++ b/merkle/int.go @@ -4,6 +4,7 @@ import ( "encoding/binary" ) +type Byte byte type Int8 int8 type UInt8 uint8 type Int16 int16 @@ -16,21 +17,46 @@ type Int int type UInt uint -// Int8 +// Byte -func (self Int8) Equals(other Key) bool { - if o, ok := other.(Int8); ok { - return self == o +func (self Byte) Equals(other Binary) bool { + return self == other +} + +func (self Byte) Less(other Key) bool { + if o, ok := other.(Byte); ok { + return self < o } else { - return false + panic("Cannot compare unequal types") } } +func (self Byte) ByteSize() int { + return 1 +} + +func (self Byte) SaveTo(b []byte) int { + if cap(b) < 1 { panic("buf too small") } + b[0] = byte(self) + return 1 +} + +func LoadByte(bytes []byte) Byte { + return Byte(bytes[0]) +} + + +// Int8 + +func (self Int8) Equals(other Binary) bool { + return self == other +} + func (self Int8) Less(other Key) bool { if o, ok := other.(Int8); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -51,19 +77,15 @@ func LoadInt8(bytes []byte) Int8 { // UInt8 -func (self UInt8) Equals(other Key) bool { - if o, ok := other.(UInt8); ok { - return self == o - } else { - return false - } +func (self UInt8) Equals(other Binary) bool { + return self == other } func (self UInt8) Less(other Key) bool { if o, ok := other.(UInt8); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -84,19 +106,15 @@ func LoadUInt8(bytes []byte) UInt8 { // Int16 -func (self Int16) Equals(other Key) bool { - if o, ok := other.(Int16); ok { - return self == o - } else { - return false - } +func (self Int16) Equals(other Binary) bool { + return self == other } func (self Int16) Less(other Key) bool { if o, ok := other.(Int16); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -117,19 +135,15 @@ func LoadInt16(bytes []byte) Int16 { // UInt16 -func (self UInt16) Equals(other Key) bool { - if o, ok := other.(UInt16); ok { - return self == o - } else { - return false - } +func (self UInt16) Equals(other Binary) bool { + return self == other } func (self UInt16) Less(other Key) bool { if o, ok := other.(UInt16); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -150,19 +164,15 @@ func LoadUInt16(bytes []byte) UInt16 { // Int32 -func (self Int32) Equals(other Key) bool { - if o, ok := other.(Int32); ok { - return self == o - } else { - return false - } +func (self Int32) Equals(other Binary) bool { + return self == other } func (self Int32) Less(other Key) bool { if o, ok := other.(Int32); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -183,19 +193,15 @@ func LoadInt32(bytes []byte) Int32 { // UInt32 -func (self UInt32) Equals(other Key) bool { - if o, ok := other.(UInt32); ok { - return self == o - } else { - return false - } +func (self UInt32) Equals(other Binary) bool { + return self == other } func (self UInt32) Less(other Key) bool { if o, ok := other.(UInt32); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -216,19 +222,15 @@ func LoadUInt32(bytes []byte) UInt32 { // Int64 -func (self Int64) Equals(other Key) bool { - if o, ok := other.(Int64); ok { - return self == o - } else { - return false - } +func (self Int64) Equals(other Binary) bool { + return self == other } func (self Int64) Less(other Key) bool { if o, ok := other.(Int64); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -249,19 +251,15 @@ func LoadInt64(bytes []byte) Int64 { // UInt64 -func (self UInt64) Equals(other Key) bool { - if o, ok := other.(UInt64); ok { - return self == o - } else { - return false - } +func (self UInt64) Equals(other Binary) bool { + return self == other } func (self UInt64) Less(other Key) bool { if o, ok := other.(UInt64); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -282,19 +280,15 @@ func LoadUInt64(bytes []byte) UInt64 { // Int -func (self Int) Equals(other Key) bool { - if o, ok := other.(Int); ok { - return self == o - } else { - return false - } +func (self Int) Equals(other Binary) bool { + return self == other } func (self Int) Less(other Key) bool { if o, ok := other.(Int); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -314,19 +308,15 @@ func LoadInt(bytes []byte) Int { // UInt -func (self UInt) Equals(other Key) bool { - if o, ok := other.(UInt); ok { - return self == o - } else { - return false - } +func (self UInt) Equals(other Binary) bool { + return self == other } func (self UInt) Less(other Key) bool { if o, ok := other.(UInt); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } diff --git a/merkle/string.go b/merkle/string.go index e52cc59f9..370b2efbe 100644 --- a/merkle/string.go +++ b/merkle/string.go @@ -7,19 +7,15 @@ type ByteSlice []byte // String -func (self String) Equals(other Key) bool { - if o, ok := other.(String); ok { - return self == o - } else { - return false - } +func (self String) Equals(other Binary) bool { + return self == other } func (self String) Less(other Key) bool { if o, ok := other.(String); ok { return self < o } else { - return false + panic("Cannot compare unequal types") } } @@ -34,15 +30,15 @@ func (self String) SaveTo(buf []byte) int { return len(self)+4 } -func LoadString(bytes []byte) String { - length := LoadUInt32(bytes) - return String(bytes[4:4+length]) +func LoadString(bytes []byte, start int) (String, int) { + length := int(LoadUInt32(bytes[start:])) + return String(bytes[start+4:start+4+length]), start+4+length } // ByteSlice -func (self ByteSlice) Equals(other Key) bool { +func (self ByteSlice) Equals(other Binary) bool { if o, ok := other.(ByteSlice); ok { return bytes.Equal(self, o) } else { @@ -54,7 +50,7 @@ func (self ByteSlice) Less(other Key) bool { if o, ok := other.(ByteSlice); ok { return bytes.Compare(self, o) < 0 // -1 if a < b } else { - return false + panic("Cannot compare unequal types") } } @@ -69,7 +65,7 @@ func (self ByteSlice) SaveTo(buf []byte) int { return len(self)+4 } -func LoadByteSlice(bytes []byte) ByteSlice { - length := LoadUInt32(bytes) - return ByteSlice(bytes[4:4+length]) +func LoadByteSlice(bytes []byte, start int) (ByteSlice, int) { + length := int(LoadUInt32(bytes[start:])) + return ByteSlice(bytes[start+4:start+4+length]), start+4+length } diff --git a/merkle/types.go b/merkle/types.go index 00ffdff18..84d4953a5 100644 --- a/merkle/types.go +++ b/merkle/types.go @@ -7,6 +7,7 @@ import ( type Binary interface { ByteSize() int SaveTo([]byte) int + Equals(Binary) bool } type Value interface { @@ -16,10 +17,14 @@ type Value interface { type Key interface { Binary - Equals(b Key) bool Less(b Key) bool } +type Db interface { + Get([]byte) []byte + Put([]byte, []byte) +} + type Tree interface { Root() Node @@ -27,17 +32,14 @@ type Tree interface { Height() uint8 Has(key Key) bool Get(key Key) Value + Hash() (ByteSlice, uint64) + Save() Put(Key, Value) Remove(Key) (Value, error) } -type Db interface { - Get([]byte) []byte - Put([]byte, []byte) -} - type Node interface { Binary @@ -50,7 +52,9 @@ type Node interface { Height() uint8 Has(Db, Key) bool Get(Db, Key) Value + Hash() (ByteSlice, uint64) + Save(Db) Put(Db, Key, Value) (*IAVLNode, bool) Remove(Db, Key) (*IAVLNode, Value, error) diff --git a/merkle/util.go b/merkle/util.go index 75fbb4f67..a5a3c0378 100644 --- a/merkle/util.go +++ b/merkle/util.go @@ -1,6 +1,7 @@ package merkle import ( + "os" "fmt" ) @@ -52,3 +53,18 @@ func printIAVLNode(node *IAVLNode, indent int) { printIAVLNode(node.rightFilled(nil), indent+1) } } + +func randstr(length int) String { + if urandom, err := os.Open("/dev/urandom"); err != nil { + panic(err) + } else { + slice := make([]byte, length) + if _, err := urandom.Read(slice); err != nil { + panic(err) + } + urandom.Close() + return String(slice) + } + panic("unreachable") +} +