aboutsummaryrefslogtreecommitdiffstats
path: root/ethutil
diff options
context:
space:
mode:
Diffstat (limited to 'ethutil')
-rw-r--r--ethutil/.gitignore12
-rw-r--r--ethutil/.travil.yml3
-rw-r--r--ethutil/README.md137
-rw-r--r--ethutil/big.go37
-rw-r--r--ethutil/bytes.go64
-rw-r--r--ethutil/config.go106
-rw-r--r--ethutil/db.go10
-rw-r--r--ethutil/encoding.go62
-rw-r--r--ethutil/encoding_test.go37
-rw-r--r--ethutil/helpers.go61
-rw-r--r--ethutil/parsing.go108
-rw-r--r--ethutil/parsing_test.go32
-rw-r--r--ethutil/rand.go24
-rw-r--r--ethutil/rlp.go418
-rw-r--r--ethutil/rlp_test.go170
-rw-r--r--ethutil/trie.go354
-rw-r--r--ethutil/trie_test.go40
-rw-r--r--ethutil/value.go204
18 files changed, 1879 insertions, 0 deletions
diff --git a/ethutil/.gitignore b/ethutil/.gitignore
new file mode 100644
index 000000000..f725d58d1
--- /dev/null
+++ b/ethutil/.gitignore
@@ -0,0 +1,12 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile ~/.gitignore_global
+
+/tmp
+*/**/*un~
+*un~
+.DS_Store
+*/**/.DS_Store
+
diff --git a/ethutil/.travil.yml b/ethutil/.travil.yml
new file mode 100644
index 000000000..69359072d
--- /dev/null
+++ b/ethutil/.travil.yml
@@ -0,0 +1,3 @@
+language: go
+go:
+ - 1.2
diff --git a/ethutil/README.md b/ethutil/README.md
new file mode 100644
index 000000000..c98612e1e
--- /dev/null
+++ b/ethutil/README.md
@@ -0,0 +1,137 @@
+# ethutil
+
+[![Build
+Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
+
+The ethutil package contains the ethereum utility library.
+
+# Installation
+
+`go get github.com/ethereum/ethutil-go`
+
+# Usage
+
+## RLP (Recursive Linear Prefix) Encoding
+
+RLP Encoding is an encoding scheme utilized by the Ethereum project. It
+encodes any native value or list to string.
+
+More in depth information about the Encoding scheme see the [Wiki](http://wiki.ethereum.org/index.php/RLP)
+article.
+
+```go
+rlp := ethutil.Encode("doge")
+fmt.Printf("%q\n", rlp) // => "\0x83dog"
+
+rlp = ethutil.Encode([]interface{}{"dog", "cat"})
+fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat"
+decoded := ethutil.Decode(rlp)
+fmt.Println(decoded) // => ["dog" "cat"]
+```
+
+## Patricia Trie
+
+Patricie Tree is a merkle trie utilized by the Ethereum project.
+
+More in depth information about the (modified) Patricia Trie can be
+found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree).
+
+The patricia trie uses a db as backend and could be anything as long as
+it satisfies the Database interface found in `ethutil/db.go`.
+
+```go
+db := NewDatabase()
+
+// db, root
+trie := ethutil.NewTrie(db, "")
+
+trie.Put("puppy", "dog")
+trie.Put("horse", "stallion")
+trie.Put("do", "verb")
+trie.Put("doge", "coin")
+
+// Look up the key "do" in the trie
+out := trie.Get("do")
+fmt.Println(out) // => verb
+```
+
+The patricia trie, in combination with RLP, provides a robust,
+cryptographically authenticated data structure that can be used to store
+all (key, value) bindings.
+
+```go
+// ... Create db/trie
+
+// Note that RLP uses interface slices as list
+value := ethutil.Encode([]interface{}{"one", 2, "three", []interface{}{42}})
+// Store the RLP encoded value of the list
+trie.Put("mykey", value)
+```
+
+## Value
+
+Value is a Generic Value which is used in combination with RLP data or
+`([])interface{}` structures. It may serve as a bridge between RLP data
+and actual real values and takes care of all the type checking and
+casting. Unlike Go's `reflect.Value` it does not panic if it's unable to
+cast to the requested value. It simple returns the base value of that
+type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
+
+### Creating a new Value
+
+`NewEmptyValue()` returns a new \*Value with it's initial value set to a
+`[]interface{}`
+
+`AppendLint()` appends a list to the current value.
+
+`Append(v)` appends the value (v) to the current value/list.
+
+```go
+val := ethutil.NewEmptyValue().Append(1).Append("2")
+val.AppendList().Append(3)
+```
+
+### Retrieving values
+
+`Get(i)` returns the `i` item in the list.
+
+`Uint()` returns the value as an unsigned int64.
+
+`Slice()` returns the value as a interface slice.
+
+`Str()` returns the value as a string.
+
+`Bytes()` returns the value as a byte slice.
+
+`Len()` assumes current to be a slice and returns its length.
+
+`Byte()` returns the value as a single byte.
+
+```go
+val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}})
+val.Get(0).Uint() // => 1
+val.Get(1).Str() // => "2"
+s := val.Get(2) // => Value([]interface{}{3})
+s.Get(0).Uint() // => 3
+```
+
+## Decoding
+
+Decoding streams of RLP data is simplified
+
+```go
+val := ethutil.NewValueFromBytes(rlpData)
+val.Get(0).Uint()
+```
+
+## Encoding
+
+Encoding from Value to RLP is done with the `Encode` method. The
+underlying value can be anything RLP can encode (int, str, lists, bytes)
+
+```go
+val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}})
+rlp := val.Encode()
+// Store the rlp data
+Store(rlp)
+```
diff --git a/ethutil/big.go b/ethutil/big.go
new file mode 100644
index 000000000..979078bef
--- /dev/null
+++ b/ethutil/big.go
@@ -0,0 +1,37 @@
+package ethutil
+
+import (
+ "math/big"
+)
+
+var BigInt0 *big.Int = big.NewInt(0)
+
+// True
+var BigTrue *big.Int = big.NewInt(1)
+
+// False
+var BigFalse *big.Int = big.NewInt(0)
+
+// Returns the power of two integers
+func BigPow(a, b int) *big.Int {
+ c := new(big.Int)
+ c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0))
+
+ return c
+}
+
+// Like big.NewInt(uint64); this takes a string instead.
+func Big(num string) *big.Int {
+ n := new(big.Int)
+ n.SetString(num, 0)
+
+ return n
+}
+
+// Like big.NewInt(uint64); this takes a byte buffer instead.
+func BigD(data []byte) *big.Int {
+ n := new(big.Int)
+ n.SetBytes(data)
+
+ return n
+}
diff --git a/ethutil/bytes.go b/ethutil/bytes.go
new file mode 100644
index 000000000..40903a5f1
--- /dev/null
+++ b/ethutil/bytes.go
@@ -0,0 +1,64 @@
+package ethutil
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+)
+
+func NumberToBytes(num interface{}, bits int) []byte {
+ buf := new(bytes.Buffer)
+ err := binary.Write(buf, binary.BigEndian, num)
+ if err != nil {
+ fmt.Println("NumberToBytes failed:", err)
+ }
+
+ return buf.Bytes()[buf.Len()-(bits/8):]
+}
+
+func BytesToNumber(b []byte) uint64 {
+ var number uint64
+
+ // Make sure the buffer is 64bits
+ data := make([]byte, 8)
+ data = append(data[:len(b)], b...)
+
+ buf := bytes.NewReader(data)
+ err := binary.Read(buf, binary.BigEndian, &number)
+ if err != nil {
+ fmt.Println("BytesToNumber failed:", err)
+ }
+
+ return number
+}
+
+// Read variable integer in big endian
+func ReadVarint(reader *bytes.Reader) (ret uint64) {
+ if reader.Len() == 8 {
+ var num uint64
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ } else if reader.Len() == 4 {
+ var num uint32
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ } else if reader.Len() == 2 {
+ var num uint16
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ } else {
+ var num uint8
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ }
+
+ return ret
+}
+
+func BinaryLength(num int) int {
+ if num == 0 {
+ return 0
+ }
+
+ return 1 + BinaryLength(num>>8)
+}
diff --git a/ethutil/config.go b/ethutil/config.go
new file mode 100644
index 000000000..7782e7daa
--- /dev/null
+++ b/ethutil/config.go
@@ -0,0 +1,106 @@
+package ethutil
+
+import (
+ "log"
+ "os"
+ "os/user"
+ "path"
+)
+
+type LogType byte
+
+const (
+ LogTypeStdIn = 1
+ LogTypeFile = 2
+)
+
+// Config struct isn't exposed
+type config struct {
+ Db Database
+
+ Log Logger
+ ExecPath string
+ Debug bool
+ Ver string
+ Pubkey []byte
+ Seed bool
+}
+
+var Config *config
+
+// Read config doesn't read anything yet.
+func ReadConfig(base string) *config {
+ if Config == nil {
+ usr, _ := user.Current()
+ path := path.Join(usr.HomeDir, base)
+
+ //Check if the logging directory already exists, create it if not
+ _, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ log.Printf("Debug logging directory %s doesn't exist, creating it", path)
+ os.Mkdir(path, 0777)
+ }
+ }
+
+ Config = &config{ExecPath: path, Debug: true, Ver: "0.2.1"}
+ Config.Log = NewLogger(LogFile|LogStd, 0)
+ }
+
+ return Config
+}
+
+type LoggerType byte
+
+const (
+ LogFile = 0x1
+ LogStd = 0x2
+)
+
+type Logger struct {
+ logSys []*log.Logger
+ logLevel int
+}
+
+func NewLogger(flag LoggerType, level int) Logger {
+ var loggers []*log.Logger
+
+ flags := log.LstdFlags | log.Lshortfile
+
+ if flag&LogFile > 0 {
+ file, err := os.OpenFile(path.Join(Config.ExecPath, "debug.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
+ if err != nil {
+ log.Panic("unable to create file logger", err)
+ }
+
+ log := log.New(file, "[ETH]", flags)
+
+ loggers = append(loggers, log)
+ }
+ if flag&LogStd > 0 {
+ log := log.New(os.Stdout, "[ETH]", flags)
+ loggers = append(loggers, log)
+ }
+
+ return Logger{logSys: loggers, logLevel: level}
+}
+
+func (log Logger) Debugln(v ...interface{}) {
+ if log.logLevel != 0 {
+ return
+ }
+
+ for _, logger := range log.logSys {
+ logger.Println(v...)
+ }
+}
+
+func (log Logger) Debugf(format string, v ...interface{}) {
+ if log.logLevel != 0 {
+ return
+ }
+
+ for _, logger := range log.logSys {
+ logger.Printf(format, v...)
+ }
+}
diff --git a/ethutil/db.go b/ethutil/db.go
new file mode 100644
index 000000000..3681c4b05
--- /dev/null
+++ b/ethutil/db.go
@@ -0,0 +1,10 @@
+package ethutil
+
+// Database interface
+type Database interface {
+ Put(key []byte, value []byte)
+ Get(key []byte) ([]byte, error)
+ LastKnownTD() []byte
+ Close()
+ Print()
+}
diff --git a/ethutil/encoding.go b/ethutil/encoding.go
new file mode 100644
index 000000000..207548c93
--- /dev/null
+++ b/ethutil/encoding.go
@@ -0,0 +1,62 @@
+package ethutil
+
+import (
+ "bytes"
+ "encoding/hex"
+ _ "fmt"
+ "strings"
+)
+
+func CompactEncode(hexSlice []int) string {
+ terminator := 0
+ if hexSlice[len(hexSlice)-1] == 16 {
+ terminator = 1
+ }
+
+ if terminator == 1 {
+ hexSlice = hexSlice[:len(hexSlice)-1]
+ }
+
+ oddlen := len(hexSlice) % 2
+ flags := 2*terminator + oddlen
+ if oddlen != 0 {
+ hexSlice = append([]int{flags}, hexSlice...)
+ } else {
+ hexSlice = append([]int{flags, 0}, hexSlice...)
+ }
+
+ var buff bytes.Buffer
+ for i := 0; i < len(hexSlice); i += 2 {
+ buff.WriteByte(byte(16*hexSlice[i] + hexSlice[i+1]))
+ }
+
+ return buff.String()
+}
+
+func CompactDecode(str string) []int {
+ base := CompactHexDecode(str)
+ base = base[:len(base)-1]
+ if base[0] >= 2 { // && base[len(base)-1] != 16 {
+ base = append(base, 16)
+ }
+ if base[0]%2 == 1 {
+ base = base[1:]
+ } else {
+ base = base[2:]
+ }
+
+ return base
+}
+
+func CompactHexDecode(str string) []int {
+ base := "0123456789abcdef"
+ hexSlice := make([]int, 0)
+
+ enc := hex.EncodeToString([]byte(str))
+ for _, v := range enc {
+ hexSlice = append(hexSlice, strings.IndexByte(base, byte(v)))
+ }
+ hexSlice = append(hexSlice, 16)
+
+ return hexSlice
+}
diff --git a/ethutil/encoding_test.go b/ethutil/encoding_test.go
new file mode 100644
index 000000000..bcabab0b1
--- /dev/null
+++ b/ethutil/encoding_test.go
@@ -0,0 +1,37 @@
+package ethutil
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestCompactEncode(t *testing.T) {
+ test1 := []int{1, 2, 3, 4, 5}
+ if res := CompactEncode(test1); res != "\x11\x23\x45" {
+ t.Error(fmt.Sprintf("even compact encode failed. Got: %q", res))
+ }
+
+ test2 := []int{0, 1, 2, 3, 4, 5}
+ if res := CompactEncode(test2); res != "\x00\x01\x23\x45" {
+ t.Error(fmt.Sprintf("odd compact encode failed. Got: %q", res))
+ }
+
+ test3 := []int{0, 15, 1, 12, 11, 8 /*term*/, 16}
+ if res := CompactEncode(test3); res != "\x20\x0f\x1c\xb8" {
+ t.Error(fmt.Sprintf("odd terminated compact encode failed. Got: %q", res))
+ }
+
+ test4 := []int{15, 1, 12, 11, 8 /*term*/, 16}
+ if res := CompactEncode(test4); res != "\x3f\x1c\xb8" {
+ t.Error(fmt.Sprintf("even terminated compact encode failed. Got: %q", res))
+ }
+}
+
+func TestCompactHexDecode(t *testing.T) {
+ exp := []int{7, 6, 6, 5, 7, 2, 6, 2, 16}
+ res := CompactHexDecode("verb")
+
+ if !CompareIntSlice(res, exp) {
+ t.Error("Error compact hex decode. Expected", exp, "got", res)
+ }
+}
diff --git a/ethutil/helpers.go b/ethutil/helpers.go
new file mode 100644
index 000000000..1c6adf256
--- /dev/null
+++ b/ethutil/helpers.go
@@ -0,0 +1,61 @@
+package ethutil
+
+import (
+ "code.google.com/p/go.crypto/ripemd160"
+ "crypto/sha256"
+ "encoding/hex"
+ "github.com/obscuren/sha3"
+ "strconv"
+)
+
+func Uitoa(i uint32) string {
+ return strconv.FormatUint(uint64(i), 10)
+}
+
+func Sha256Bin(data []byte) []byte {
+ hash := sha256.Sum256(data)
+
+ return hash[:]
+}
+
+func Ripemd160(data []byte) []byte {
+ ripemd := ripemd160.New()
+ ripemd.Write(data)
+
+ return ripemd.Sum(nil)
+}
+
+func Sha3Bin(data []byte) []byte {
+ d := sha3.NewKeccak256()
+ d.Reset()
+ d.Write(data)
+
+ return d.Sum(nil)
+}
+
+// Helper function for comparing slices
+func CompareIntSlice(a, b []int) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, v := range a {
+ if v != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// Returns the amount of nibbles that match each other from 0 ...
+func MatchingNibbleLength(a, b []int) int {
+ i := 0
+ for CompareIntSlice(a[:i+1], b[:i+1]) && i < len(b) {
+ i += 1
+ }
+
+ return i
+}
+
+func Hex(d []byte) string {
+ return hex.EncodeToString(d)
+}
diff --git a/ethutil/parsing.go b/ethutil/parsing.go
new file mode 100644
index 000000000..2c41fb4df
--- /dev/null
+++ b/ethutil/parsing.go
@@ -0,0 +1,108 @@
+package ethutil
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "strconv"
+ "strings"
+)
+
+// Op codes
+var OpCodes = map[string]string{
+ "STOP": "0",
+ "ADD": "1",
+ "MUL": "2",
+ "SUB": "3",
+ "DIV": "4",
+ "SDIV": "5",
+ "MOD": "6",
+ "SMOD": "7",
+ "EXP": "8",
+ "NEG": "9",
+ "LT": "10",
+ "LE": "11",
+ "GT": "12",
+ "GE": "13",
+ "EQ": "14",
+ "NOT": "15",
+ "MYADDRESS": "16",
+ "TXSENDER": "17",
+
+ "PUSH": "48",
+ "POP": "49",
+ "LOAD": "54",
+}
+
+func CompileInstr(s string) (string, error) {
+ tokens := strings.Split(s, " ")
+ if OpCodes[tokens[0]] == "" {
+ return s, errors.New(fmt.Sprintf("OP not found: %s", tokens[0]))
+ }
+
+ code := OpCodes[tokens[0]] // Replace op codes with the proper numerical equivalent
+ op := new(big.Int)
+ op.SetString(code, 0)
+
+ args := make([]*big.Int, 6)
+ for i, val := range tokens[1:len(tokens)] {
+ num := new(big.Int)
+ num.SetString(val, 0)
+ args[i] = num
+ }
+
+ // Big int equation = op + x * 256 + y * 256**2 + z * 256**3 + a * 256**4 + b * 256**5 + c * 256**6
+ base := new(big.Int)
+ x := new(big.Int)
+ y := new(big.Int)
+ z := new(big.Int)
+ a := new(big.Int)
+ b := new(big.Int)
+ c := new(big.Int)
+
+ if args[0] != nil {
+ x.Mul(args[0], big.NewInt(256))
+ }
+ if args[1] != nil {
+ y.Mul(args[1], BigPow(256, 2))
+ }
+ if args[2] != nil {
+ z.Mul(args[2], BigPow(256, 3))
+ }
+ if args[3] != nil {
+ a.Mul(args[3], BigPow(256, 4))
+ }
+ if args[4] != nil {
+ b.Mul(args[4], BigPow(256, 5))
+ }
+ if args[5] != nil {
+ c.Mul(args[5], BigPow(256, 6))
+ }
+
+ base.Add(op, x)
+ base.Add(base, y)
+ base.Add(base, z)
+ base.Add(base, a)
+ base.Add(base, b)
+ base.Add(base, c)
+
+ return base.String(), nil
+}
+
+func Instr(instr string) (int, []string, error) {
+ base := new(big.Int)
+ base.SetString(instr, 0)
+
+ args := make([]string, 7)
+ for i := 0; i < 7; i++ {
+ // int(int(val) / int(math.Pow(256,float64(i)))) % 256
+ exp := BigPow(256, i)
+ num := new(big.Int)
+ num.Div(base, exp)
+
+ args[i] = num.Mod(num, big.NewInt(256)).String()
+ }
+ op, _ := strconv.Atoi(args[0])
+
+ return op, args[1:7], nil
+}
diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go
new file mode 100644
index 000000000..482eef3ee
--- /dev/null
+++ b/ethutil/parsing_test.go
@@ -0,0 +1,32 @@
+package ethutil
+
+import (
+ "math"
+ "testing"
+)
+
+func TestCompile(t *testing.T) {
+ instr, err := CompileInstr("PUSH")
+
+ if err != nil {
+ t.Error("Failed compiling instruction")
+ }
+
+ calc := (48 + 0*256 + 0*int64(math.Pow(256, 2)))
+ if Big(instr).Int64() != calc {
+ t.Error("Expected", calc, ", got:", instr)
+ }
+}
+
+func TestValidInstr(t *testing.T) {
+ /*
+ op, args, err := Instr("68163")
+ if err != nil {
+ t.Error("Error decoding instruction")
+ }
+ */
+
+}
+
+func TestInvalidInstr(t *testing.T) {
+}
diff --git a/ethutil/rand.go b/ethutil/rand.go
new file mode 100644
index 000000000..91dafec7e
--- /dev/null
+++ b/ethutil/rand.go
@@ -0,0 +1,24 @@
+package ethutil
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "io"
+)
+
+func randomUint64(r io.Reader) (uint64, error) {
+ b := make([]byte, 8)
+ n, err := r.Read(b)
+ if n != len(b) {
+ return 0, io.ErrShortBuffer
+ }
+ if err != nil {
+ return 0, err
+ }
+ return binary.BigEndian.Uint64(b), nil
+}
+
+// RandomUint64 returns a cryptographically random uint64 value.
+func RandomUint64() (uint64, error) {
+ return randomUint64(rand.Reader)
+}
diff --git a/ethutil/rlp.go b/ethutil/rlp.go
new file mode 100644
index 000000000..0f88933b3
--- /dev/null
+++ b/ethutil/rlp.go
@@ -0,0 +1,418 @@
+package ethutil
+
+import (
+ "bytes"
+ _ "encoding/binary"
+ "fmt"
+ _ "log"
+ _ "math"
+ "math/big"
+ "reflect"
+)
+
+///////////////////////////////////////
+type EthEncoder interface {
+ EncodeData(rlpData interface{}) []byte
+}
+type EthDecoder interface {
+ Get(idx int) *RlpValue
+}
+
+//////////////////////////////////////
+
+type RlpEncoder struct {
+ rlpData []byte
+}
+
+func NewRlpEncoder() *RlpEncoder {
+ encoder := &RlpEncoder{}
+
+ return encoder
+}
+func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
+ return Encode(rlpData)
+}
+
+// Data rlpValueutes are returned by the rlp decoder. The data rlpValueutes represents
+// one item within the rlp data structure. It's responsible for all the casting
+// It always returns something rlpValueid
+type RlpValue struct {
+ Value interface{}
+ kind reflect.Value
+}
+
+func (rlpValue *RlpValue) String() string {
+ return fmt.Sprintf("%q", rlpValue.Value)
+}
+
+func Conv(rlpValue interface{}) *RlpValue {
+ return &RlpValue{Value: rlpValue, kind: reflect.ValueOf(rlpValue)}
+}
+
+func NewRlpValue(rlpValue interface{}) *RlpValue {
+ return &RlpValue{Value: rlpValue}
+}
+
+func (rlpValue *RlpValue) Type() reflect.Kind {
+ return reflect.TypeOf(rlpValue.Value).Kind()
+}
+
+func (rlpValue *RlpValue) IsNil() bool {
+ return rlpValue.Value == nil
+}
+
+func (rlpValue *RlpValue) Length() int {
+ //return rlpValue.kind.Len()
+ if data, ok := rlpValue.Value.([]interface{}); ok {
+ return len(data)
+ }
+
+ return 0
+}
+
+func (rlpValue *RlpValue) AsRaw() interface{} {
+ return rlpValue.Value
+}
+
+func (rlpValue *RlpValue) AsUint() uint64 {
+ if Value, ok := rlpValue.Value.(uint8); ok {
+ return uint64(Value)
+ } else if Value, ok := rlpValue.Value.(uint16); ok {
+ return uint64(Value)
+ } else if Value, ok := rlpValue.Value.(uint32); ok {
+ return uint64(Value)
+ } else if Value, ok := rlpValue.Value.(uint64); ok {
+ return Value
+ }
+
+ return 0
+}
+
+func (rlpValue *RlpValue) AsByte() byte {
+ if Value, ok := rlpValue.Value.(byte); ok {
+ return Value
+ }
+
+ return 0x0
+}
+
+func (rlpValue *RlpValue) AsBigInt() *big.Int {
+ if a, ok := rlpValue.Value.([]byte); ok {
+ b := new(big.Int)
+ b.SetBytes(a)
+ return b
+ }
+
+ return big.NewInt(0)
+}
+
+func (rlpValue *RlpValue) AsString() string {
+ if a, ok := rlpValue.Value.([]byte); ok {
+ return string(a)
+ } else if a, ok := rlpValue.Value.(string); ok {
+ return a
+ } else {
+ //panic(fmt.Sprintf("not string %T: %v", rlpValue.Value, rlpValue.Value))
+ }
+
+ return ""
+}
+
+func (rlpValue *RlpValue) AsBytes() []byte {
+ if a, ok := rlpValue.Value.([]byte); ok {
+ return a
+ }
+
+ return make([]byte, 0)
+}
+
+func (rlpValue *RlpValue) AsSlice() []interface{} {
+ if d, ok := rlpValue.Value.([]interface{}); ok {
+ return d
+ }
+
+ return []interface{}{}
+}
+
+func (rlpValue *RlpValue) AsSliceFrom(from int) *RlpValue {
+ slice := rlpValue.AsSlice()
+
+ return NewRlpValue(slice[from:])
+}
+
+func (rlpValue *RlpValue) AsSliceTo(to int) *RlpValue {
+ slice := rlpValue.AsSlice()
+
+ return NewRlpValue(slice[:to])
+}
+
+func (rlpValue *RlpValue) AsSliceFromTo(from, to int) *RlpValue {
+ slice := rlpValue.AsSlice()
+
+ return NewRlpValue(slice[from:to])
+}
+
+// Threat the rlpValueute as a slice
+func (rlpValue *RlpValue) Get(idx int) *RlpValue {
+ if d, ok := rlpValue.Value.([]interface{}); ok {
+ // Guard for oob
+ if len(d) <= idx {
+ return NewRlpValue(nil)
+ }
+
+ if idx < 0 {
+ panic("negative idx for Rlp Get")
+ }
+
+ return NewRlpValue(d[idx])
+ }
+
+ // If this wasn't a slice you probably shouldn't be using this function
+ return NewRlpValue(nil)
+}
+
+func (rlpValue *RlpValue) Cmp(o *RlpValue) bool {
+ return reflect.DeepEqual(rlpValue.Value, o.Value)
+}
+
+func (rlpValue *RlpValue) Encode() []byte {
+ return Encode(rlpValue.Value)
+}
+
+func NewRlpValueFromBytes(rlpData []byte) *RlpValue {
+ if len(rlpData) != 0 {
+ data, _ := Decode(rlpData, 0)
+ return NewRlpValue(data)
+ }
+
+ return NewRlpValue(nil)
+}
+
+// RlpValue value setters
+// An empty rlp value is always a list
+func EmptyRlpValue() *RlpValue {
+ return NewRlpValue([]interface{}{})
+}
+
+func (rlpValue *RlpValue) AppendList() *RlpValue {
+ list := EmptyRlpValue()
+ rlpValue.Value = append(rlpValue.AsSlice(), list)
+
+ return list
+}
+
+func (rlpValue *RlpValue) Append(v interface{}) *RlpValue {
+ rlpValue.Value = append(rlpValue.AsSlice(), v)
+
+ return rlpValue
+}
+
+/*
+func FromBin(data []byte) uint64 {
+ if len(data) == 0 {
+ return 0
+ }
+
+ return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1])
+}
+*/
+
+const (
+ RlpEmptyList = 0x80
+ RlpEmptyStr = 0x40
+)
+
+func Char(c []byte) int {
+ if len(c) > 0 {
+ return int(c[0])
+ }
+
+ return 0
+}
+
+func DecodeWithReader(reader *bytes.Buffer) interface{} {
+ var slice []interface{}
+
+ // Read the next byte
+ char := Char(reader.Next(1))
+ switch {
+ case char == 0:
+ return nil
+ case char <= 0x7c:
+ return char
+
+ case char <= 0xb7:
+ return reader.Next(int(char - 0x80))
+
+ case char <= 0xbf:
+ buff := bytes.NewReader(reader.Next(int(char - 0xb8)))
+ length := ReadVarint(buff)
+
+ return reader.Next(int(length))
+
+ case char <= 0xf7:
+ length := int(char - 0xc0)
+ for i := 0; i < length; i++ {
+ obj := DecodeWithReader(reader)
+ if obj != nil {
+ slice = append(slice, obj)
+ } else {
+ break
+ }
+ }
+
+ return slice
+
+ }
+
+ return slice
+}
+
+// TODO Use a bytes.Buffer instead of a raw byte slice.
+// Cleaner code, and use draining instead of seeking the next bytes to read
+func Decode(data []byte, pos uint64) (interface{}, uint64) {
+ /*
+ if pos > uint64(len(data)-1) {
+ log.Println(data)
+ log.Panicf("index out of range %d for data %q, l = %d", pos, data, len(data))
+ }
+ */
+
+ var slice []interface{}
+ char := int(data[pos])
+ switch {
+ case char <= 0x7f:
+ return data[pos], pos + 1
+
+ case char <= 0xb7:
+ b := uint64(data[pos]) - 0x80
+
+ return data[pos+1 : pos+1+b], pos + 1 + b
+
+ case char <= 0xbf:
+ b := uint64(data[pos]) - 0xb7
+
+ b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b]))
+
+ return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
+
+ case char <= 0xf7:
+ b := uint64(data[pos]) - 0xc0
+ prevPos := pos
+ pos++
+ for i := uint64(0); i < b; {
+ var obj interface{}
+
+ // Get the next item in the data list and append it
+ obj, prevPos = Decode(data, pos)
+ slice = append(slice, obj)
+
+ // Increment i by the amount bytes read in the previous
+ // read
+ i += (prevPos - pos)
+ pos = prevPos
+ }
+ return slice, pos
+
+ case char <= 0xff:
+ l := uint64(data[pos]) - 0xf7
+ //b := BigD(data[pos+1 : pos+1+l]).Uint64()
+ b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
+
+ pos = pos + l + 1
+
+ prevPos := b
+ for i := uint64(0); i < uint64(b); {
+ var obj interface{}
+
+ obj, prevPos = Decode(data, pos)
+ slice = append(slice, obj)
+
+ i += (prevPos - pos)
+ pos = prevPos
+ }
+ return slice, pos
+
+ default:
+ panic(fmt.Sprintf("byte not supported: %q", char))
+ }
+
+ return slice, 0
+}
+
+var (
+ directRlp = big.NewInt(0x7f)
+ numberRlp = big.NewInt(0xb7)
+ zeroRlp = big.NewInt(0x0)
+)
+
+func Encode(object interface{}) []byte {
+ var buff bytes.Buffer
+
+ if object != nil {
+ switch t := object.(type) {
+ case *RlpValue:
+ buff.Write(Encode(t.AsRaw()))
+ // Code dup :-/
+ case int:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case uint:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int8:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int16:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int32:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int64:
+ buff.Write(Encode(big.NewInt(t)))
+ case uint16:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case uint32:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case uint64:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case byte:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case *big.Int:
+ buff.Write(Encode(t.Bytes()))
+ case []byte:
+ if len(t) == 1 && t[0] <= 0x7f {
+ buff.Write(t)
+ } else if len(t) < 56 {
+ buff.WriteByte(byte(len(t) + 0x80))
+ buff.Write(t)
+ } else {
+ b := big.NewInt(int64(len(t)))
+ buff.WriteByte(byte(len(b.Bytes()) + 0xb7))
+ buff.Write(b.Bytes())
+ buff.Write(t)
+ }
+ case string:
+ buff.Write(Encode([]byte(t)))
+ case []interface{}:
+ // Inline function for writing the slice header
+ WriteSliceHeader := func(length int) {
+ if length < 56 {
+ buff.WriteByte(byte(length + 0xc0))
+ } else {
+ b := big.NewInt(int64(length))
+ buff.WriteByte(byte(len(b.Bytes()) + 0xf7))
+ buff.Write(b.Bytes())
+ }
+ }
+
+ var b bytes.Buffer
+ for _, val := range t {
+ b.Write(Encode(val))
+ }
+ WriteSliceHeader(len(b.Bytes()))
+ buff.Write(b.Bytes())
+ }
+ } else {
+ // Empty list for nil
+ buff.WriteByte(0xc0)
+ }
+
+ return buff.Bytes()
+}
diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go
new file mode 100644
index 000000000..32bcbdce1
--- /dev/null
+++ b/ethutil/rlp_test.go
@@ -0,0 +1,170 @@
+package ethutil
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "math/big"
+ "reflect"
+ "testing"
+)
+
+func TestRlpValueEncoding(t *testing.T) {
+ val := EmptyRlpValue()
+ val.AppendList().Append(1).Append(2).Append(3)
+ val.Append("4").AppendList().Append(5)
+
+ res := val.Encode()
+ exp := Encode([]interface{}{[]interface{}{1, 2, 3}, "4", []interface{}{5}})
+ if bytes.Compare(res, exp) != 0 {
+ t.Errorf("expected %q, got %q", res, exp)
+ }
+}
+
+func TestValueSlice(t *testing.T) {
+ val := []interface{}{
+ "value1",
+ "valeu2",
+ "value3",
+ }
+
+ value := NewValue(val)
+ splitVal := value.SliceFrom(1)
+
+ if splitVal.Len() != 2 {
+ t.Error("SliceFrom: Expected len", 2, "got", splitVal.Len())
+ }
+
+ splitVal = value.SliceTo(2)
+ if splitVal.Len() != 2 {
+ t.Error("SliceTo: Expected len", 2, "got", splitVal.Len())
+ }
+
+ splitVal = value.SliceFromTo(1, 3)
+ if splitVal.Len() != 2 {
+ t.Error("SliceFromTo: Expected len", 2, "got", splitVal.Len())
+ }
+}
+
+func TestValue(t *testing.T) {
+ value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01"))
+ if value.Get(0).Str() != "dog" {
+ t.Errorf("expected '%v', got '%v'", value.Get(0).Str(), "dog")
+ }
+
+ if value.Get(3).Uint() != 1 {
+ t.Errorf("expected '%v', got '%v'", value.Get(3).Uint(), 1)
+ }
+}
+
+func TestEncode(t *testing.T) {
+ strRes := "\x83dog"
+ bytes := Encode("dog")
+
+ str := string(bytes)
+ if str != strRes {
+ t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str))
+ }
+
+ sliceRes := "\xcc\x83dog\x83god\x83cat"
+ strs := []interface{}{"dog", "god", "cat"}
+ bytes = Encode(strs)
+ slice := string(bytes)
+ if slice != sliceRes {
+ t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice))
+ }
+
+ intRes := "\x82\x04\x00"
+ bytes = Encode(1024)
+ if string(bytes) != intRes {
+ t.Errorf("Expected %q, got %q", intRes, bytes)
+ }
+}
+
+func TestDecode(t *testing.T) {
+ single := []byte("\x01")
+ b, _ := Decode(single, 0)
+
+ if b.(uint8) != 1 {
+ t.Errorf("Expected 1, got %q", b)
+ }
+
+ str := []byte("\x83dog")
+ b, _ = Decode(str, 0)
+ if bytes.Compare(b.([]byte), []byte("dog")) != 0 {
+ t.Errorf("Expected dog, got %q", b)
+ }
+
+ slice := []byte("\xcc\x83dog\x83god\x83cat")
+ res := []interface{}{"dog", "god", "cat"}
+ b, _ = Decode(slice, 0)
+ if reflect.DeepEqual(b, res) {
+ t.Errorf("Expected %q, got %q", res, b)
+ }
+}
+
+func TestEncodeDecodeBigInt(t *testing.T) {
+ bigInt := big.NewInt(1391787038)
+ encoded := Encode(bigInt)
+
+ value := NewValueFromBytes(encoded)
+ fmt.Println(value.BigInt(), bigInt)
+ if value.BigInt().Cmp(bigInt) != 0 {
+ t.Errorf("Expected %v, got %v", bigInt, value.BigInt())
+ }
+
+ dec, _ := hex.DecodeString("52f4fc1e")
+ fmt.Println(NewValueFromBytes(dec).BigInt())
+}
+
+func TestEncodeDecodeBytes(t *testing.T) {
+ b := NewValue([]interface{}{[]byte{1, 2, 3, 4, 5}, byte(6)})
+ val := NewValueFromBytes(b.Encode())
+ if !b.Cmp(val) {
+ t.Errorf("Expected %v, got %v", val, b)
+ }
+}
+
+/*
+var ZeroHash256 = make([]byte, 32)
+var ZeroHash160 = make([]byte, 20)
+var EmptyShaList = Sha3Bin(Encode([]interface{}{}))
+
+var GenisisHeader = []interface{}{
+ // Previous hash (none)
+ //"",
+ ZeroHash256,
+ // Sha of uncles
+ Sha3Bin(Encode([]interface{}{})),
+ // Coinbase
+ ZeroHash160,
+ // Root state
+ "",
+ // Sha of transactions
+ //EmptyShaList,
+ Sha3Bin(Encode([]interface{}{})),
+ // Difficulty
+ BigPow(2, 22),
+ // Time
+ //big.NewInt(0),
+ int64(0),
+ // extra
+ "",
+ // Nonce
+ big.NewInt(42),
+}
+
+func TestEnc(t *testing.T) {
+ //enc := Encode(GenisisHeader)
+ //fmt.Printf("%x (%d)\n", enc, len(enc))
+ h, _ := hex.DecodeString("f8a0a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06d076baa9c4074fb2df222dd16a96b0155a1e6686b3e5748b4e9ca0a208a425ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493478340000080802a")
+ fmt.Printf("%x\n", Sha3Bin(h))
+}
+*/
+
+func BenchmarkEncodeDecode(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ bytes := Encode([]interface{}{"dog", "god", "cat"})
+ Decode(bytes, 0)
+ }
+}
diff --git a/ethutil/trie.go b/ethutil/trie.go
new file mode 100644
index 000000000..44d2d5774
--- /dev/null
+++ b/ethutil/trie.go
@@ -0,0 +1,354 @@
+package ethutil
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type Node struct {
+ Key []byte
+ Value *Value
+ Dirty bool
+}
+
+func NewNode(key []byte, val *Value, dirty bool) *Node {
+ return &Node{Key: key, Value: val, Dirty: dirty}
+}
+
+func (n *Node) Copy() *Node {
+ return NewNode(n.Key, n.Value, n.Dirty)
+}
+
+type Cache struct {
+ nodes map[string]*Node
+ db Database
+}
+
+func NewCache(db Database) *Cache {
+ return &Cache{db: db, nodes: make(map[string]*Node)}
+}
+
+func (cache *Cache) Put(v interface{}) interface{} {
+ value := NewValue(v)
+
+ enc := value.Encode()
+ if len(enc) >= 32 {
+ sha := Sha3Bin(enc)
+
+ cache.nodes[string(sha)] = NewNode(sha, value, true)
+
+ return sha
+ }
+
+ return v
+}
+
+func (cache *Cache) Get(key []byte) *Value {
+ // First check if the key is the cache
+ if cache.nodes[string(key)] != nil {
+ return cache.nodes[string(key)].Value
+ }
+
+ // Get the key of the database instead and cache it
+ data, _ := cache.db.Get(key)
+ // Create the cached value
+ value := NewValueFromBytes(data)
+ // Create caching node
+ cache.nodes[string(key)] = NewNode(key, value, false)
+
+ return value
+}
+
+func (cache *Cache) Commit() {
+ for key, node := range cache.nodes {
+ if node.Dirty {
+ cache.db.Put([]byte(key), node.Value.Encode())
+ node.Dirty = false
+ }
+ }
+
+ // If the nodes grows beyond the 200 entries we simple empty it
+ // FIXME come up with something better
+ if len(cache.nodes) > 200 {
+ cache.nodes = make(map[string]*Node)
+ }
+}
+
+func (cache *Cache) Undo() {
+ for key, node := range cache.nodes {
+ if node.Dirty {
+ delete(cache.nodes, key)
+ }
+ }
+}
+
+// A (modified) Radix Trie implementation
+type Trie struct {
+ Root interface{}
+ //db Database
+ cache *Cache
+}
+
+func NewTrie(db Database, Root interface{}) *Trie {
+ return &Trie{cache: NewCache(db), Root: Root}
+}
+
+func (t *Trie) Sync() {
+ t.cache.Commit()
+}
+
+/*
+ * Public (query) interface functions
+ */
+func (t *Trie) Update(key string, value string) {
+ k := CompactHexDecode(key)
+
+ t.Root = t.UpdateState(t.Root, k, value)
+}
+
+func (t *Trie) Get(key string) string {
+ k := CompactHexDecode(key)
+ c := NewValue(t.GetState(t.Root, k))
+
+ return c.Str()
+}
+
+func (t *Trie) GetState(node interface{}, key []int) interface{} {
+ n := NewValue(node)
+ // Return the node if key is empty (= found)
+ if len(key) == 0 || n.IsNil() || n.Len() == 0 {
+ return node
+ }
+
+ currentNode := t.GetNode(node)
+ length := currentNode.Len()
+
+ if length == 0 {
+ return ""
+ } else if length == 2 {
+ // Decode the key
+ k := CompactDecode(currentNode.Get(0).Str())
+ v := currentNode.Get(1).Raw()
+
+ if len(key) >= len(k) && CompareIntSlice(k, key[:len(k)]) {
+ return t.GetState(v, key[len(k):])
+ } else {
+ return ""
+ }
+ } else if length == 17 {
+ return t.GetState(currentNode.Get(key[0]).Raw(), key[1:])
+ }
+
+ // It shouldn't come this far
+ fmt.Println("GetState unexpected return")
+ return ""
+}
+
+func (t *Trie) GetNode(node interface{}) *Value {
+ n := NewValue(node)
+
+ if !n.Get(0).IsNil() {
+ return n
+ }
+
+ str := n.Str()
+ if len(str) == 0 {
+ return n
+ } else if len(str) < 32 {
+ return NewValueFromBytes([]byte(str))
+ }
+ /*
+ else {
+ // Fetch the encoded node from the db
+ o, err := t.db.Get(n.Bytes())
+ if err != nil {
+ fmt.Println("Error InsertState", err)
+ return NewValue("")
+ }
+
+ return NewValueFromBytes(o)
+ }
+ */
+ return t.cache.Get(n.Bytes())
+
+}
+
+func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} {
+ if value != "" {
+ return t.InsertState(node, key, value)
+ } else {
+ // delete it
+ }
+
+ return ""
+}
+
+func (t *Trie) Put(node interface{}) interface{} {
+ /*
+ enc := Encode(node)
+ if len(enc) >= 32 {
+ var sha []byte
+ sha = Sha3Bin(enc)
+ //t.db.Put([]byte(sha), enc)
+
+ return sha
+ }
+ return node
+ */
+
+ /*
+ TODO?
+ c := Conv(t.Root)
+ fmt.Println(c.Type(), c.Length())
+ if c.Type() == reflect.String && c.AsString() == "" {
+ return enc
+ }
+ */
+
+ return t.cache.Put(node)
+
+}
+
+func EmptyStringSlice(l int) []interface{} {
+ slice := make([]interface{}, l)
+ for i := 0; i < l; i++ {
+ slice[i] = ""
+ }
+ return slice
+}
+
+func (t *Trie) InsertState(node interface{}, key []int, value interface{}) interface{} {
+ if len(key) == 0 {
+ return value
+ }
+
+ // New node
+ n := NewValue(node)
+ if node == nil || (n.Type() == reflect.String && (n.Str() == "" || n.Get(0).IsNil())) || n.Len() == 0 {
+ newNode := []interface{}{CompactEncode(key), value}
+
+ return t.Put(newNode)
+ }
+
+ currentNode := t.GetNode(node)
+ // Check for "special" 2 slice type node
+ if currentNode.Len() == 2 {
+ // Decode the key
+ k := CompactDecode(currentNode.Get(0).Str())
+ v := currentNode.Get(1).Raw()
+
+ // Matching key pair (ie. there's already an object with this key)
+ if CompareIntSlice(k, key) {
+ newNode := []interface{}{CompactEncode(key), value}
+ return t.Put(newNode)
+ }
+
+ var newHash interface{}
+ matchingLength := MatchingNibbleLength(key, k)
+ if matchingLength == len(k) {
+ // Insert the hash, creating a new node
+ newHash = t.InsertState(v, key[matchingLength:], value)
+ } else {
+ // Expand the 2 length slice to a 17 length slice
+ oldNode := t.InsertState("", k[matchingLength+1:], v)
+ newNode := t.InsertState("", key[matchingLength+1:], value)
+ // Create an expanded slice
+ scaledSlice := EmptyStringSlice(17)
+ // Set the copied and new node
+ scaledSlice[k[matchingLength]] = oldNode
+ scaledSlice[key[matchingLength]] = newNode
+
+ newHash = t.Put(scaledSlice)
+ }
+
+ if matchingLength == 0 {
+ // End of the chain, return
+ return newHash
+ } else {
+ newNode := []interface{}{CompactEncode(key[:matchingLength]), newHash}
+ return t.Put(newNode)
+ }
+ } else {
+
+ // Copy the current node over to the new node and replace the first nibble in the key
+ newNode := EmptyStringSlice(17)
+
+ for i := 0; i < 17; i++ {
+ cpy := currentNode.Get(i).Raw()
+ if cpy != nil {
+ newNode[i] = cpy
+ }
+ }
+
+ newNode[key[0]] = t.InsertState(currentNode.Get(key[0]).Raw(), key[1:], value)
+
+ return t.Put(newNode)
+ }
+
+ return ""
+}
+
+// Simple compare function which creates a rlp value out of the evaluated objects
+func (t *Trie) Cmp(trie *Trie) bool {
+ return NewValue(t.Root).Cmp(NewValue(trie.Root))
+}
+
+// Returns a copy of this trie
+func (t *Trie) Copy() *Trie {
+ trie := NewTrie(t.cache.db, t.Root)
+ for key, node := range t.cache.nodes {
+ trie.cache.nodes[key] = node.Copy()
+ }
+
+ return trie
+}
+
+/*
+ * Trie helper functions
+ */
+// Helper function for printing out the raw contents of a slice
+func PrintSlice(slice []string) {
+ fmt.Printf("[")
+ for i, val := range slice {
+ fmt.Printf("%q", val)
+ if i != len(slice)-1 {
+ fmt.Printf(",")
+ }
+ }
+ fmt.Printf("]\n")
+}
+
+func PrintSliceT(slice interface{}) {
+ c := Conv(slice)
+ for i := 0; i < c.Length(); i++ {
+ val := c.Get(i)
+ if val.Type() == reflect.Slice {
+ PrintSliceT(val.AsRaw())
+ } else {
+ fmt.Printf("%q", val)
+ if i != c.Length()-1 {
+ fmt.Printf(",")
+ }
+ }
+ }
+}
+
+// RLP Decodes a node in to a [2] or [17] string slice
+func DecodeNode(data []byte) []string {
+ dec, _ := Decode(data, 0)
+ if slice, ok := dec.([]interface{}); ok {
+ strSlice := make([]string, len(slice))
+
+ for i, s := range slice {
+ if str, ok := s.([]byte); ok {
+ strSlice[i] = string(str)
+ }
+ }
+
+ return strSlice
+ } else {
+ fmt.Printf("It wasn't a []. It's a %T\n", dec)
+ }
+
+ return nil
+}
diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go
new file mode 100644
index 000000000..b87d35e1a
--- /dev/null
+++ b/ethutil/trie_test.go
@@ -0,0 +1,40 @@
+package ethutil
+
+import (
+ _ "encoding/hex"
+ _ "fmt"
+ "testing"
+)
+
+type MemDatabase struct {
+ db map[string][]byte
+}
+
+func NewMemDatabase() (*MemDatabase, error) {
+ db := &MemDatabase{db: make(map[string][]byte)}
+ return db, nil
+}
+func (db *MemDatabase) Put(key []byte, value []byte) {
+ db.db[string(key)] = value
+}
+func (db *MemDatabase) Get(key []byte) ([]byte, error) {
+ return db.db[string(key)], nil
+}
+func (db *MemDatabase) Print() {}
+func (db *MemDatabase) Close() {}
+func (db *MemDatabase) LastKnownTD() []byte { return nil }
+
+func TestTrieSync(t *testing.T) {
+ db, _ := NewMemDatabase()
+ trie := NewTrie(db, "")
+
+ trie.Update("dog", "kindofalongsentencewhichshouldbeencodedinitsentirety")
+ if len(db.db) != 0 {
+ t.Error("Expected no data in database")
+ }
+
+ trie.Sync()
+ if len(db.db) == 0 {
+ t.Error("Expected data to be persisted")
+ }
+}
diff --git a/ethutil/value.go b/ethutil/value.go
new file mode 100644
index 000000000..2a990783e
--- /dev/null
+++ b/ethutil/value.go
@@ -0,0 +1,204 @@
+package ethutil
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "reflect"
+)
+
+// Data values are returned by the rlp decoder. The data values represents
+// one item within the rlp data structure. It's responsible for all the casting
+// It always returns something valid
+type Value struct {
+ Val interface{}
+ kind reflect.Value
+}
+
+func (val *Value) String() string {
+ return fmt.Sprintf("%q", val.Val)
+}
+
+func NewValue(val interface{}) *Value {
+ return &Value{Val: val}
+}
+
+func (val *Value) Type() reflect.Kind {
+ return reflect.TypeOf(val.Val).Kind()
+}
+
+func (val *Value) IsNil() bool {
+ return val.Val == nil
+}
+
+func (val *Value) Len() int {
+ //return val.kind.Len()
+ if data, ok := val.Val.([]interface{}); ok {
+ return len(data)
+ } else if data, ok := val.Val.([]byte); ok {
+ // FIXME
+ return len(data)
+ }
+
+ return 0
+}
+
+func (val *Value) Raw() interface{} {
+ return val.Val
+}
+
+func (val *Value) Interface() interface{} {
+ return val.Val
+}
+
+func (val *Value) Uint() uint64 {
+ if Val, ok := val.Val.(uint8); ok {
+ return uint64(Val)
+ } else if Val, ok := val.Val.(uint16); ok {
+ return uint64(Val)
+ } else if Val, ok := val.Val.(uint32); ok {
+ return uint64(Val)
+ } else if Val, ok := val.Val.(uint64); ok {
+ return Val
+ } else if Val, ok := val.Val.([]byte); ok {
+ return ReadVarint(bytes.NewReader(Val))
+ }
+
+ return 0
+}
+
+func (val *Value) Byte() byte {
+ if Val, ok := val.Val.(byte); ok {
+ return Val
+ }
+
+ return 0x0
+}
+
+func (val *Value) BigInt() *big.Int {
+ if a, ok := val.Val.([]byte); ok {
+ b := new(big.Int).SetBytes(a)
+
+ return b
+ } else {
+ return big.NewInt(int64(val.Uint()))
+ }
+
+ return big.NewInt(0)
+}
+
+func (val *Value) Str() string {
+ if a, ok := val.Val.([]byte); ok {
+ return string(a)
+ } else if a, ok := val.Val.(string); ok {
+ return a
+ }
+
+ return ""
+}
+
+func (val *Value) Bytes() []byte {
+ if a, ok := val.Val.([]byte); ok {
+ return a
+ }
+
+ return make([]byte, 0)
+}
+
+func (val *Value) Slice() []interface{} {
+ if d, ok := val.Val.([]interface{}); ok {
+ return d
+ }
+
+ return []interface{}{}
+}
+
+func (val *Value) SliceFrom(from int) *Value {
+ slice := val.Slice()
+
+ return NewValue(slice[from:])
+}
+
+func (val *Value) SliceTo(to int) *Value {
+ slice := val.Slice()
+
+ return NewValue(slice[:to])
+}
+
+func (val *Value) SliceFromTo(from, to int) *Value {
+ slice := val.Slice()
+
+ return NewValue(slice[from:to])
+}
+
+// Threat the value as a slice
+func (val *Value) Get(idx int) *Value {
+ if d, ok := val.Val.([]interface{}); ok {
+ // Guard for oob
+ if len(d) <= idx {
+ return NewValue(nil)
+ }
+
+ if idx < 0 {
+ panic("negative idx for Rlp Get")
+ }
+
+ return NewValue(d[idx])
+ }
+
+ // If this wasn't a slice you probably shouldn't be using this function
+ return NewValue(nil)
+}
+
+func (val *Value) Cmp(o *Value) bool {
+ return reflect.DeepEqual(val.Val, o.Val)
+}
+
+func (val *Value) Encode() []byte {
+ return Encode(val.Val)
+}
+
+func NewValueFromBytes(rlpData []byte) *Value {
+ if len(rlpData) != 0 {
+ data, _ := Decode(rlpData, 0)
+ return NewValue(data)
+ }
+
+ return NewValue(nil)
+}
+
+// Value setters
+func NewSliceValue(s interface{}) *Value {
+ list := EmptyValue()
+
+ if s != nil {
+ if slice, ok := s.([]interface{}); ok {
+ for _, val := range slice {
+ list.Append(val)
+ }
+ } else if slice, ok := s.([]string); ok {
+ for _, val := range slice {
+ list.Append(val)
+ }
+ }
+ }
+
+ return list
+}
+
+func EmptyValue() *Value {
+ return NewValue([]interface{}{})
+}
+
+func (val *Value) AppendList() *Value {
+ list := EmptyValue()
+ val.Val = append(val.Slice(), list)
+
+ return list
+}
+
+func (val *Value) Append(v interface{}) *Value {
+ val.Val = append(val.Slice(), v)
+
+ return val
+}