The great merge
This commit is contained in:
12
ethutil/.gitignore
vendored
Normal file
12
ethutil/.gitignore
vendored
Normal file
@ -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
|
||||
|
3
ethutil/.travil.yml
Normal file
3
ethutil/.travil.yml
Normal file
@ -0,0 +1,3 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.2
|
137
ethutil/README.md
Normal file
137
ethutil/README.md
Normal file
@ -0,0 +1,137 @@
|
||||
# ethutil
|
||||
|
||||
[](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)
|
||||
```
|
37
ethutil/big.go
Normal file
37
ethutil/big.go
Normal file
@ -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
|
||||
}
|
64
ethutil/bytes.go
Normal file
64
ethutil/bytes.go
Normal file
@ -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)
|
||||
}
|
106
ethutil/config.go
Normal file
106
ethutil/config.go
Normal file
@ -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...)
|
||||
}
|
||||
}
|
10
ethutil/db.go
Normal file
10
ethutil/db.go
Normal file
@ -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()
|
||||
}
|
62
ethutil/encoding.go
Normal file
62
ethutil/encoding.go
Normal file
@ -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
|
||||
}
|
37
ethutil/encoding_test.go
Normal file
37
ethutil/encoding_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
61
ethutil/helpers.go
Normal file
61
ethutil/helpers.go
Normal file
@ -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)
|
||||
}
|
108
ethutil/parsing.go
Normal file
108
ethutil/parsing.go
Normal file
@ -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
|
||||
}
|
32
ethutil/parsing_test.go
Normal file
32
ethutil/parsing_test.go
Normal file
@ -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) {
|
||||
}
|
24
ethutil/rand.go
Normal file
24
ethutil/rand.go
Normal file
@ -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)
|
||||
}
|
418
ethutil/rlp.go
Normal file
418
ethutil/rlp.go
Normal file
@ -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()
|
||||
}
|
170
ethutil/rlp_test.go
Normal file
170
ethutil/rlp_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
354
ethutil/trie.go
Normal file
354
ethutil/trie.go
Normal file
@ -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
|
||||
}
|
40
ethutil/trie_test.go
Normal file
40
ethutil/trie_test.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
204
ethutil/value.go
Normal file
204
ethutil/value.go
Normal file
@ -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
|
||||
}
|
Reference in New Issue
Block a user