From 9bcb3d22168f5f02cd32ff23df60239156ddc899 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 15 Feb 2014 12:40:19 +0100 Subject: [PATCH 01/78] Fixed test --- ethutil/rlp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 32bcbdce1..2bddeec8f 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -10,7 +10,7 @@ import ( ) func TestRlpValueEncoding(t *testing.T) { - val := EmptyRlpValue() + val := EmptyValue() val.AppendList().Append(1).Append(2).Append(3) val.Append("4").AppendList().Append(5) From 5883446b219a2980d67ff604c7f227089e5c8619 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 15 Feb 2014 12:41:17 +0100 Subject: [PATCH 02/78] Fixed test --- ethutil/rlp_test.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 2bddeec8f..54f929ebd 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -2,8 +2,6 @@ package ethutil import ( "bytes" - "encoding/hex" - "fmt" "math/big" "reflect" "testing" @@ -63,7 +61,7 @@ func TestEncode(t *testing.T) { str := string(bytes) if str != strRes { - t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str)) + t.Errorf("Expected %q, got %q", strRes, str) } sliceRes := "\xcc\x83dog\x83god\x83cat" @@ -71,7 +69,7 @@ func TestEncode(t *testing.T) { bytes = Encode(strs) slice := string(bytes) if slice != sliceRes { - t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice)) + t.Error("Expected %q, got %q", sliceRes, slice) } intRes := "\x82\x04\x00" @@ -108,13 +106,9 @@ func TestEncodeDecodeBigInt(t *testing.T) { 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) { From 07c12f0b921a05aec668ae8ce63b6dfac51d76a6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 15 Feb 2014 13:21:11 +0100 Subject: [PATCH 03/78] Added trie tests, value tests --- ethutil/trie_test.go | 64 +++++++++++++++++++++++++++++++++++++++++-- ethutil/value.go | 4 +++ ethutil/value_test.go | 38 +++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 ethutil/value_test.go diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index b87d35e1a..94414b82e 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -6,6 +6,8 @@ import ( "testing" ) +const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ" + type MemDatabase struct { db map[string][]byte } @@ -24,11 +26,15 @@ func (db *MemDatabase) Print() {} func (db *MemDatabase) Close() {} func (db *MemDatabase) LastKnownTD() []byte { return nil } -func TestTrieSync(t *testing.T) { +func New() (*MemDatabase, *Trie) { db, _ := NewMemDatabase() - trie := NewTrie(db, "") + return db, NewTrie(db, "") +} - trie.Update("dog", "kindofalongsentencewhichshouldbeencodedinitsentirety") +func TestTrieSync(t *testing.T) { + db, trie := New() + + trie.Update("dog", LONG_WORD) if len(db.db) != 0 { t.Error("Expected no data in database") } @@ -38,3 +44,55 @@ func TestTrieSync(t *testing.T) { t.Error("Expected data to be persisted") } } + +func TestTrieReset(t *testing.T) { + _, trie := New() + + trie.Update("cat", LONG_WORD) + if len(trie.cache.nodes) == 0 { + t.Error("Expected cached nodes") + } + + trie.cache.Undo() + + if len(trie.cache.nodes) != 0 { + t.Error("Expected no nodes after undo") + } +} + +func TestTrieGet(t *testing.T) { + _, trie := New() + + trie.Update("cat", LONG_WORD) + x := trie.Get("cat") + if x != LONG_WORD { + t.Error("expected %s, got %s", LONG_WORD, x) + } +} + +func TestTrieUpdating(t *testing.T) { + _, trie := New() + trie.Update("cat", LONG_WORD) + trie.Update("cat", LONG_WORD+"1") + x := trie.Get("cat") + if x != LONG_WORD+"1" { + t.Error("expected %S, got %s", LONG_WORD+"1", x) + } +} + +func TestTrieCmp(t *testing.T) { + _, trie1 := New() + _, trie2 := New() + + trie1.Update("doge", LONG_WORD) + trie2.Update("doge", LONG_WORD) + if !trie1.Cmp(trie2) { + t.Error("Expected tries to be equal") + } + + trie1.Update("dog", LONG_WORD) + trie2.Update("cat", LONG_WORD) + if trie1.Cmp(trie2) { + t.Errorf("Expected tries not to be equal %x %x", trie1.Root, trie2.Root) + } +} diff --git a/ethutil/value.go b/ethutil/value.go index 2a990783e..f91c33983 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -60,6 +60,10 @@ func (val *Value) Uint() uint64 { return uint64(Val) } else if Val, ok := val.Val.(uint64); ok { return Val + } else if Val, ok := val.Val.(int); ok { + return uint64(Val) + } else if Val, ok := val.Val.(uint); ok { + return uint64(Val) } else if Val, ok := val.Val.([]byte); ok { return ReadVarint(bytes.NewReader(Val)) } diff --git a/ethutil/value_test.go b/ethutil/value_test.go new file mode 100644 index 000000000..7d41eb1c9 --- /dev/null +++ b/ethutil/value_test.go @@ -0,0 +1,38 @@ +package ethutil + +import ( + "testing" +) + +func TestValueCmp(t *testing.T) { + val1 := NewValue("hello") + val2 := NewValue("world") + if val1.Cmp(val2) { + t.Error("Expected values not to be equal") + } + + val3 := NewValue("hello") + val4 := NewValue("hello") + if !val3.Cmp(val4) { + t.Error("Expected values to be equal") + } +} + +func TestValueTypes(t *testing.T) { + str := NewValue("str") + num := NewValue(1) + inter := NewValue([]interface{}{1}) + + if str.Str() != "str" { + t.Errorf("expected Str to return 'str', got %s", str.Str()) + } + + if num.Uint() != 1 { + t.Errorf("expected Uint to return '1', got %d", num.Uint()) + } + + exp := []interface{}{1} + if !NewValue(inter.Interface()).Cmp(NewValue(exp)) { + t.Errorf("expected Interface to return '%v', got %v", exp, num.Interface()) + } +} From 066940f134170ab4b0901887b69f824418c322fc Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 16 Feb 2014 20:30:21 +0100 Subject: [PATCH 04/78] Defer undo on the current block's state --- ethchain/block_manager.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 8b237a29a..d9cdcd2d9 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -103,6 +103,11 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { // Processing a blocks may never happen simultaneously bm.mutex.Lock() defer bm.mutex.Unlock() + // Defer the Undo on the Trie. If the block processing happened + // we don't want to undo but since undo only happens on dirty + // nodes this won't happen because Commit would have been called + // before that. + defer bm.bc.CurrentBlock.State().Undo() hash := block.Hash() From c95a27e39485eeeebd8608537115a4fd246c246c Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 16 Feb 2014 20:30:33 +0100 Subject: [PATCH 05/78] Added more tests --- ethutil/value.go | 12 +++++++----- ethutil/value_test.go | 20 +++++++++++++++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/ethutil/value.go b/ethutil/value.go index f91c33983..701ece5bb 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -84,6 +84,8 @@ func (val *Value) BigInt() *big.Int { b := new(big.Int).SetBytes(a) return b + } else if a, ok := val.Val.(*big.Int); ok { + return a } else { return big.NewInt(int64(val.Uint())) } @@ -106,7 +108,7 @@ func (val *Value) Bytes() []byte { return a } - return make([]byte, 0) + return []byte{} } func (val *Value) Slice() []interface{} { @@ -144,7 +146,7 @@ func (val *Value) Get(idx int) *Value { } if idx < 0 { - panic("negative idx for Rlp Get") + panic("negative idx for Value Get") } return NewValue(d[idx]) @@ -162,9 +164,9 @@ func (val *Value) Encode() []byte { return Encode(val.Val) } -func NewValueFromBytes(rlpData []byte) *Value { - if len(rlpData) != 0 { - data, _ := Decode(rlpData, 0) +func NewValueFromBytes(data []byte) *Value { + if len(data) != 0 { + data, _ := Decode(data, 0) return NewValue(data) } diff --git a/ethutil/value_test.go b/ethutil/value_test.go index 7d41eb1c9..0e2da5328 100644 --- a/ethutil/value_test.go +++ b/ethutil/value_test.go @@ -1,6 +1,8 @@ package ethutil import ( + "bytes" + "math/big" "testing" ) @@ -22,6 +24,8 @@ func TestValueTypes(t *testing.T) { str := NewValue("str") num := NewValue(1) inter := NewValue([]interface{}{1}) + byt := NewValue([]byte{1, 2, 3, 4}) + bigInt := NewValue(big.NewInt(10)) if str.Str() != "str" { t.Errorf("expected Str to return 'str', got %s", str.Str()) @@ -31,8 +35,18 @@ func TestValueTypes(t *testing.T) { t.Errorf("expected Uint to return '1', got %d", num.Uint()) } - exp := []interface{}{1} - if !NewValue(inter.Interface()).Cmp(NewValue(exp)) { - t.Errorf("expected Interface to return '%v', got %v", exp, num.Interface()) + interExp := []interface{}{1} + if !NewValue(inter.Interface()).Cmp(NewValue(interExp)) { + t.Errorf("expected Interface to return '%v', got %v", interExp, num.Interface()) + } + + bytExp := []byte{1, 2, 3, 4} + if bytes.Compare(byt.Bytes(), bytExp) != 0 { + t.Errorf("expected Bytes to return '%v', got %v", bytExp, byt.Bytes()) + } + + bigExp := big.NewInt(10) + if bigInt.BigInt().Cmp(bigExp) != 0 { + t.Errorf("expected BigInt to return '%v', got %v", bigExp, bigInt.BigInt()) } } From f1d6f1bd1793767a5596d4c277651e2264c1cf8e Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 16 Feb 2014 20:30:50 +0100 Subject: [PATCH 06/78] Removed Reset --- ethutil/helpers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ethutil/helpers.go b/ethutil/helpers.go index 1c6adf256..2e3aeb9a3 100644 --- a/ethutil/helpers.go +++ b/ethutil/helpers.go @@ -27,7 +27,6 @@ func Ripemd160(data []byte) []byte { func Sha3Bin(data []byte) []byte { d := sha3.NewKeccak256() - d.Reset() d.Write(data) return d.Sum(nil) From 7264044122d2ceef413667ad8473746a40a44782 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 16 Feb 2014 20:31:02 +0100 Subject: [PATCH 07/78] Added a few tests --- ethutil/rlp_test.go | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 54f929ebd..2a58bfc0f 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -119,43 +119,6 @@ func TestEncodeDecodeBytes(t *testing.T) { } } -/* -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"}) From e4a6ee3d7f8a3876387f837f6fec6822bd97951e Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 16 Feb 2014 20:32:56 +0100 Subject: [PATCH 08/78] Added dirty tracking on the cache --- ethutil/trie.go | 17 +++++++++++++++-- ethutil/trie_test.go | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/ethutil/trie.go b/ethutil/trie.go index 0a3f73136..7140a1b36 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -20,8 +20,9 @@ func (n *Node) Copy() *Node { } type Cache struct { - nodes map[string]*Node - db Database + nodes map[string]*Node + db Database + IsDirty bool } func NewCache(db Database) *Cache { @@ -36,6 +37,7 @@ func (cache *Cache) Put(v interface{}) interface{} { sha := Sha3Bin(enc) cache.nodes[string(sha)] = NewNode(sha, value, true) + cache.IsDirty = true return sha } @@ -60,12 +62,18 @@ func (cache *Cache) Get(key []byte) *Value { } func (cache *Cache) Commit() { + // Don't try to commit if it isn't dirty + if !cache.IsDirty { + return + } + for key, node := range cache.nodes { if node.Dirty { cache.db.Put([]byte(key), node.Value.Encode()) node.Dirty = false } } + cache.IsDirty = false // If the nodes grows beyond the 200 entries we simple empty it // FIXME come up with something better @@ -80,6 +88,7 @@ func (cache *Cache) Undo() { delete(cache.nodes, key) } } + cache.IsDirty = false } // A (modified) Radix Trie implementation. The Trie implements @@ -103,6 +112,10 @@ func (t *Trie) Sync() { t.cache.Commit() } +func (t *Trie) Undo() { + t.cache.Undo() +} + /* * Public (query) interface functions */ diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index 94414b82e..fa60c8cfc 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -45,6 +45,26 @@ func TestTrieSync(t *testing.T) { } } +func TestTrieDirtyTracking(t *testing.T) { + _, trie := New() + trie.Update("dog", LONG_WORD) + if !trie.cache.IsDirty { + t.Error("Expected trie to be dirty") + } + + trie.Sync() + if trie.cache.IsDirty { + t.Error("Expected trie not to be dirty") + } + + trie.Update("test", LONG_WORD) + trie.cache.Undo() + if trie.cache.IsDirty { + t.Error("Expected trie not to be dirty") + } + +} + func TestTrieReset(t *testing.T) { _, trie := New() From 2ea05292c0c7947deb90c37378eb36ea78b6cf20 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 16 Feb 2014 20:33:07 +0100 Subject: [PATCH 09/78] Added proper name :) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 803f2ef4e..b77f7909a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Geff Obscura +Copyright (c) 2013 Jeffrey Wilcke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From e5b97fe03e8789bf4e113946a1935c0ba270ad2b Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 16 Feb 2014 20:33:23 +0100 Subject: [PATCH 10/78] Added proper error message --- peer.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/peer.go b/peer.go index 7b17b8b09..926d34eb3 100644 --- a/peer.go +++ b/peer.go @@ -293,7 +293,9 @@ func (p *Peer) HandleInbound() { err = p.ethereum.BlockManager.ProcessBlock(block) if err != nil { - log.Println("bckmsg", err) + if ethutil.Config.Debug { + log.Printf("[PEER] Block (%x) err %v", block.Hash()[:4], err) + } break } else { lastBlock = block From b7a636b8949a2270ae030f56791c88060fa5483a Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 17 Feb 2014 20:29:54 +0100 Subject: [PATCH 11/78] Values should accept bytes as valid string output --- ethutil/value.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ethutil/value.go b/ethutil/value.go index 701ece5bb..d3a38f87f 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -98,6 +98,8 @@ func (val *Value) Str() string { return string(a) } else if a, ok := val.Val.(string); ok { return a + } else if a, ok := val.Val.(byte); ok { + return string(a) } return "" From 7413552a28c538ca4f33d0dafe28907b7b761863 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 17 Feb 2014 20:40:33 +0100 Subject: [PATCH 12/78] Root should reset on undo --- ethutil/trie.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ethutil/trie.go b/ethutil/trie.go index 7140a1b36..95abca602 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -98,22 +98,25 @@ func (cache *Cache) Undo() { // Please note that the data isn't persisted unless `Sync` is // explicitly called. type Trie struct { - Root interface{} + prevRoot interface{} + Root interface{} //db Database cache *Cache } func NewTrie(db Database, Root interface{}) *Trie { - return &Trie{cache: NewCache(db), Root: Root} + return &Trie{cache: NewCache(db), Root: Root, prevRoot: Root} } // Save the cached value to the database. func (t *Trie) Sync() { t.cache.Commit() + t.prevRoot = t.Root } func (t *Trie) Undo() { t.cache.Undo() + t.Root = t.prevRoot } /* @@ -181,6 +184,7 @@ func (t *Trie) GetNode(node interface{}) *Value { } func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} { + if value != "" { return t.InsertState(node, key, value) } else { @@ -241,6 +245,7 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter // 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() From e72a782bf0e0a0059e8dff83fa5fc80cc6f678e5 Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Mon, 17 Feb 2014 15:46:16 -0800 Subject: [PATCH 13/78] adding compact decode tests --- ethutil/encoding_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ethutil/encoding_test.go b/ethutil/encoding_test.go index bcabab0b1..cbfbc0eaf 100644 --- a/ethutil/encoding_test.go +++ b/ethutil/encoding_test.go @@ -35,3 +35,33 @@ func TestCompactHexDecode(t *testing.T) { t.Error("Error compact hex decode. Expected", exp, "got", res) } } + +func TestCompactDecode(t *testing.T) { + exp := []int{1, 2, 3, 4, 5} + res := CompactDecode("\x11\x23\x45") + + if !CompareIntSlice(res, exp) { + t.Error("odd compact decode. Expected", exp, "got", res) + } + + exp = []int{0, 1, 2, 3, 4, 5} + res = CompactDecode("\x00\x01\x23\x45") + + if !CompareIntSlice(res, exp) { + t.Error("even compact decode. Expected", exp, "got", res) + } + + exp = []int{0, 15, 1, 12, 11, 8 /*term*/, 16} + res = CompactDecode("\x20\x0f\x1c\xb8") + + if !CompareIntSlice(res, exp) { + t.Error("even terminated compact decode. Expected", exp, "got", res) + } + + exp = []int{15, 1, 12, 11, 8 /*term*/, 16} + res = CompactDecode("\x3f\x1c\xb8") + + if !CompareIntSlice(res, exp) { + t.Error("even terminated compact decode. Expected", exp, "got", res) + } +} \ No newline at end of file From c5b009ba6f153aa65fa25126bea41d899a436299 Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Mon, 17 Feb 2014 15:47:33 -0800 Subject: [PATCH 14/78] new line --- ethutil/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethutil/config.go b/ethutil/config.go index 2a239f8e2..b361a3079 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -38,7 +38,7 @@ func ReadConfig(base string) *config { _, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { - log.Printf("Debug logging directory %s doesn't exist, creating it", path) + log.Printf("Debug logging directory %s doesn't exist, creating it\n", path) os.Mkdir(path, 0777) } } From a5b7279cb507de93fde39d86c414e417f2d0b3e3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 01:31:31 +0100 Subject: [PATCH 15/78] Changed uncle block fee as to what it should be --- ethchain/fees.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ethchain/fees.go b/ethchain/fees.go index 8f1646ab4..57017cdc9 100644 --- a/ethchain/fees.go +++ b/ethchain/fees.go @@ -13,7 +13,13 @@ var DataFee *big.Int = new(big.Int) var CryptoFee *big.Int = new(big.Int) var ExtroFee *big.Int = new(big.Int) -var BlockReward *big.Int = big.NewInt(1500000000000000000) +var BlockReward *big.Int = big.NewInt(1.5e+18) + +var UncleReward *big.Int = big.NewInt(1.125e+18) + +//var UncleReward *big.Int = big.NewInt(2e18) +var UncleInclusionReward *big.Int = big.NewInt(1.875e+17) + var Period1Reward *big.Int = new(big.Int) var Period2Reward *big.Int = new(big.Int) var Period3Reward *big.Int = new(big.Int) From bb3e28310ee3c2cfc5b3153510d4a1d220a22e81 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 01:31:51 +0100 Subject: [PATCH 16/78] If sender is receiver only subtract the fee --- ethchain/transaction_pool.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index c2d65a2a7..75a8aa5d1 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -106,17 +106,25 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error } } - // Subtract the amount from the senders account - sender.Amount.Sub(sender.Amount, totAmount) - sender.Nonce += 1 - // Get the receiver receiver := block.GetAddr(tx.Recipient) - // Add the amount to receivers account which should conclude this transaction - receiver.Amount.Add(receiver.Amount, tx.Value) + sender.Nonce += 1 + + // Send Tx to self + if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { + // Subtract the fee + sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) + } else { + // Subtract the amount from the senders account + sender.Amount.Sub(sender.Amount, totAmount) + + // Add the amount to receivers account which should conclude this transaction + receiver.Amount.Add(receiver.Amount, tx.Value) + + block.UpdateAddr(tx.Recipient, receiver) + } block.UpdateAddr(tx.Sender(), sender) - block.UpdateAddr(tx.Recipient, receiver) return } From ba95849097f7a35d9f315a4f2e340d3ef944a306 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 01:32:20 +0100 Subject: [PATCH 17/78] Added hex method --- ethutil/helpers.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ethutil/helpers.go b/ethutil/helpers.go index 2e3aeb9a3..11a474081 100644 --- a/ethutil/helpers.go +++ b/ethutil/helpers.go @@ -58,3 +58,7 @@ func MatchingNibbleLength(a, b []int) int { func Hex(d []byte) string { return hex.EncodeToString(d) } +func ToHex(str string) []byte { + h, _ := hex.DecodeString(str) + return h +} From c7623c31650d078184511be796f7a00dde2a25a1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 01:32:39 +0100 Subject: [PATCH 18/78] Changed debug messages --- peer.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/peer.go b/peer.go index 926d34eb3..53fdfa8fc 100644 --- a/peer.go +++ b/peer.go @@ -294,7 +294,9 @@ func (p *Peer) HandleInbound() { if err != nil { if ethutil.Config.Debug { - log.Printf("[PEER] Block (%x) err %v", block.Hash()[:4], err) + log.Printf("[PEER] Block %x failed\n", block.Hash()) + log.Printf("[PEER] %v\n", err) + log.Println(block) } break } else { @@ -467,7 +469,7 @@ func (p *Peer) pushHandshake() error { pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes() msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ - uint32(4), uint32(0), p.Version, byte(p.caps), p.port, pubkey, + uint32(5), uint32(0), p.Version, byte(p.caps), p.port, pubkey, }) p.QueueMessage(msg) @@ -494,7 +496,7 @@ func (p *Peer) pushPeers() { func (p *Peer) handleHandshake(msg *ethwire.Msg) { c := msg.Data - if c.Get(0).Uint() != 4 { + if c.Get(0).Uint() != 5 { log.Println("Invalid peer version. Require protocol v4") p.Stop() return @@ -527,7 +529,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { // Get a reference to the peers version p.Version = c.Get(2).Str() - log.Println(p) + log.Println("[PEER]", p) } func (p *Peer) String() string { @@ -544,7 +546,7 @@ func (p *Peer) String() string { strConnectType = "disconnected" } - return fmt.Sprintf("peer [%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps) + return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps) } From 8629d9a4187c2027e565a50d763f949993ab169c Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 01:33:15 +0100 Subject: [PATCH 19/78] String changed and removed some debugging code --- ethchain/block.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index 0678f64e2..34ddf9fec 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -135,7 +135,7 @@ func (block *Block) GetContract(addr []byte) *Contract { } func (block *Block) UpdateContract(addr []byte, contract *Contract) { // Make sure the state is synced - contract.State().Sync() + //contract.State().Sync() block.state.Update(string(addr), string(contract.RlpEncode())) } @@ -298,12 +298,6 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) { tx := NewTransactionFromValue(txes.Get(i)) block.transactions[i] = tx - - /* - if ethutil.Config.Debug { - ethutil.Config.Db.Put(tx.Hash(), ethutil.Encode(tx)) - } - */ } } @@ -335,7 +329,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block { } func (block *Block) String() string { - return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce) + return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x\nTxs:%d\n", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce, len(block.transactions)) } //////////// UNEXPORTED ///////////////// From 68028f492f092f0546c2c084c1694ee6bf43b34e Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 01:33:26 +0100 Subject: [PATCH 20/78] Fixed block handling --- ethchain/block_chain.go | 7 +++---- ethchain/block_manager.go | 42 ++++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 54f48bc60..5b55782a9 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -104,7 +104,6 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} { currentHash = block.PrevHash chain = append(chain, block.Value().Val) - //chain = append([]interface{}{block.RlpValue().Value}, chain...) num-- } @@ -141,7 +140,9 @@ func (bc *BlockChain) Add(block *Block) { bc.CurrentBlock = block bc.LastBlockHash = block.Hash() - ethutil.Config.Db.Put(block.Hash(), block.RlpEncode()) + encodedBlock := block.RlpEncode() + ethutil.Config.Db.Put(block.Hash(), encodedBlock) + ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock) } func (bc *BlockChain) GetBlock(hash []byte) *Block { @@ -177,8 +178,6 @@ func (bc *BlockChain) writeBlockInfo(block *Block) { func (bc *BlockChain) Stop() { if bc.CurrentBlock != nil { - ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode()) - log.Println("[CHAIN] Stopped") } } diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index d9cdcd2d9..092e3dea5 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" "github.com/obscuren/secp256k1-go" "log" "math" @@ -18,10 +19,6 @@ type BlockProcessor interface { ProcessBlock(block *Block) } -func CalculateBlockReward(block *Block, uncleLength int) *big.Int { - return BlockReward -} - type BlockManager struct { // Mutex for locking the block processor. Blocks can only be handled one at a time mutex sync.Mutex @@ -48,7 +45,7 @@ func AddTestNetFunds(block *Block) { "8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin "93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit - "80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex } { //log.Println("2^200 Wei to", addr) codedAddr, _ := hex.DecodeString(addr) @@ -70,14 +67,17 @@ func NewBlockManager(speaker PublicSpeaker) *BlockManager { if bm.bc.CurrentBlock == nil { AddTestNetFunds(bm.bc.genesisBlock) + + bm.bc.genesisBlock.State().Sync() // Prepare the genesis block bm.bc.Add(bm.bc.genesisBlock) - log.Printf("Genesis: %x\n", bm.bc.genesisBlock.Hash()) //log.Printf("root %x\n", bm.bc.genesisBlock.State().Root) //bm.bc.genesisBlock.PrintHash() } + log.Printf("Last block: %x\n", bm.bc.CurrentBlock.Hash()) + return bm } @@ -115,12 +115,6 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { return nil } - /* - if ethutil.Config.Debug { - log.Printf("[BMGR] Processing block(%x)\n", hash) - } - */ - // Check if we have the parent hash, if it isn't known we discard it // Reasons might be catching up or simply an invalid block if !bm.bc.HasBlock(block.PrevHash) && bm.bc.CurrentBlock != nil { @@ -142,13 +136,12 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { } if !block.State().Cmp(bm.bc.CurrentBlock.State()) { - //if block.State().Root != state.Root { return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().Root, bm.bc.CurrentBlock.State().Root) } // Calculate the new total difficulty and sync back to the db if bm.CalculateTD(block) { - // Sync the current block's state to the database + // Sync the current block's state to the database and cancelling out the deferred Undo bm.bc.CurrentBlock.State().Sync() // Add the block to the chain bm.bc.Add(block) @@ -172,7 +165,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { */ // Broadcast the valid block back to the wire - //bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.RlpValue().Value}) + bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val}) // If there's a block processor present, pass in the block for further // processing @@ -251,6 +244,18 @@ func (bm *BlockManager) ValidateBlock(block *Block) error { return nil } +func CalculateBlockReward(block *Block, uncleLength int) *big.Int { + base := new(big.Int) + for i := 0; i < uncleLength; i++ { + base.Add(base, UncleInclusionReward) + } + return base.Add(base, BlockReward) +} + +func CalculateUncleReward(block *Block) *big.Int { + return UncleReward +} + func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error { // Get the coinbase rlp data addr := processor.GetAddr(block.Coinbase) @@ -259,7 +264,12 @@ func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error processor.UpdateAddr(block.Coinbase, addr) - // TODO Reward each uncle + for _, uncle := range block.Uncles { + uncleAddr := processor.GetAddr(uncle.Coinbase) + uncleAddr.AddFee(CalculateUncleReward(uncle)) + + processor.UpdateAddr(uncle.Coinbase, uncleAddr) + } return nil } From d7eca7bcc12e940f0aa80d45e6e802ba68143b5c Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 01:34:06 +0100 Subject: [PATCH 21/78] Rlp update --- ethereum.go | 39 +++++++++++++++++++++------------------ ethutil/encoding.go | 3 +-- ethutil/rlp.go | 8 -------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/ethereum.go b/ethereum.go index bd6caac08..c54303795 100644 --- a/ethereum.go +++ b/ethereum.go @@ -85,7 +85,6 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { Nonce: nonce, serverCaps: caps, nat: nat, - MaxPeers: 5, } ethereum.TxPool = ethchain.NewTxPool() ethereum.TxPool.Speaker = ethereum @@ -114,29 +113,33 @@ func (s *Ethereum) ProcessPeerList(addrs []string) { } func (s *Ethereum) ConnectToPeer(addr string) error { - var alreadyConnected bool + if s.peers.Len() < s.MaxPeers { + var alreadyConnected bool - eachPeer(s.peers, func(p *Peer, v *list.Element) { - if p.conn == nil { - return + eachPeer(s.peers, func(p *Peer, v *list.Element) { + if p.conn == nil { + return + } + phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String()) + ahost, _, _ := net.SplitHostPort(addr) + + if phost == ahost { + alreadyConnected = true + return + } + }) + + if alreadyConnected { + return nil } - phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String()) - ahost, _, _ := net.SplitHostPort(addr) - if phost == ahost { - alreadyConnected = true - return - } - }) + peer := NewOutboundPeer(addr, s, s.serverCaps) - if alreadyConnected { - return nil + s.peers.PushBack(peer) + + log.Printf("[SERV] Adding peer %d / %d\n", s.peers.Len(), s.MaxPeers) } - peer := NewOutboundPeer(addr, s, s.serverCaps) - - s.peers.PushBack(peer) - return nil } diff --git a/ethutil/encoding.go b/ethutil/encoding.go index 207548c93..1f661947a 100644 --- a/ethutil/encoding.go +++ b/ethutil/encoding.go @@ -3,7 +3,6 @@ package ethutil import ( "bytes" "encoding/hex" - _ "fmt" "strings" ) @@ -36,7 +35,7 @@ func CompactEncode(hexSlice []int) string { func CompactDecode(str string) []int { base := CompactHexDecode(str) base = base[:len(base)-1] - if base[0] >= 2 { // && base[len(base)-1] != 16 { + if base[0] >= 2 { base = append(base, 16) } if base[0]%2 == 1 { diff --git a/ethutil/rlp.go b/ethutil/rlp.go index 025d269a0..e633f5f1d 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -86,13 +86,6 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { // 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 { @@ -131,7 +124,6 @@ func Decode(data []byte, pos uint64) (interface{}, uint64) { 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 From c866fcc5b37b53e0d11c1fd7e6cb971859537f2c Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 18 Feb 2014 12:10:21 +0100 Subject: [PATCH 22/78] Added new address --- ethchain/block_manager.go | 2 +- ethutil/config.go | 2 +- ethutil/helpers.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 092e3dea5..4e72f51ba 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -43,7 +43,7 @@ type BlockManager struct { func AddTestNetFunds(block *Block) { for _, addr := range []string{ "8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin - "93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey + "e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex } { diff --git a/ethutil/config.go b/ethutil/config.go index 2a239f8e2..df1772d81 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -43,7 +43,7 @@ func ReadConfig(base string) *config { } } - Config = &config{ExecPath: path, Debug: true, Ver: "0.2.2"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.2.3"} Config.Log = NewLogger(LogFile|LogStd, 0) } diff --git a/ethutil/helpers.go b/ethutil/helpers.go index 11a474081..aa0f79a04 100644 --- a/ethutil/helpers.go +++ b/ethutil/helpers.go @@ -58,7 +58,7 @@ func MatchingNibbleLength(a, b []int) int { func Hex(d []byte) string { return hex.EncodeToString(d) } -func ToHex(str string) []byte { +func FromHex(str string) []byte { h, _ := hex.DecodeString(str) return h } From 4d405f665480fc9d0a0133161909856230f2f7a9 Mon Sep 17 00:00:00 2001 From: Sam Boyer Date: Tue, 18 Feb 2014 10:40:58 -0500 Subject: [PATCH 23/78] s/GenisisHeader/GenesisHeader/ --- ethchain/genesis.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethchain/genesis.go b/ethchain/genesis.go index 060d347e4..935978a69 100644 --- a/ethchain/genesis.go +++ b/ethchain/genesis.go @@ -13,7 +13,7 @@ var ZeroHash256 = make([]byte, 32) var ZeroHash160 = make([]byte, 20) var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{})) -var GenisisHeader = []interface{}{ +var GenesisHeader = []interface{}{ // Previous hash (none) //"", ZeroHash256, @@ -36,4 +36,4 @@ var GenisisHeader = []interface{}{ ethutil.Sha3Bin(big.NewInt(42).Bytes()), } -var Genesis = []interface{}{GenisisHeader, []interface{}{}, []interface{}{}} +var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}} From 6dac014978d30883aa556c004f948e66a314d2de Mon Sep 17 00:00:00 2001 From: Sam Boyer Date: Tue, 18 Feb 2014 10:47:09 -0500 Subject: [PATCH 24/78] Fix a couple errors from go vet --- peer.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/peer.go b/peer.go index 53fdfa8fc..95b526c67 100644 --- a/peer.go +++ b/peer.go @@ -285,7 +285,6 @@ func (p *Peer) HandleInbound() { p.lastPong = time.Now().Unix() case ethwire.MsgBlockTy: // Get all blocks and process them - msg.Data = msg.Data var block, lastBlock *ethchain.Block var err error for i := msg.Data.Len() - 1; i >= 0; i-- { @@ -438,7 +437,7 @@ func (p *Peer) Start() { err := p.pushHandshake() if err != nil { - log.Printf("Peer can't send outbound version ack", err) + log.Println("Peer can't send outbound version ack", err) p.Stop() From 5adbd399463edc5ec800bdcf3524d64313c8add5 Mon Sep 17 00:00:00 2001 From: Sam Boyer Date: Tue, 18 Feb 2014 17:20:41 -0500 Subject: [PATCH 25/78] Express bit flag constants using iota bitshift. --- peer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peer.go b/peer.go index 53fdfa8fc..e64b5b3bd 100644 --- a/peer.go +++ b/peer.go @@ -56,9 +56,9 @@ func (d DiscReason) String() string { type Caps byte const ( - CapPeerDiscTy = 0x01 - CapTxTy = 0x02 - CapChainTy = 0x04 + CapPeerDiscTy = 1 << iota + CapTxTy + CapChainTy CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy ) From 357b4bc14c82d206a8c813291fb3ead01ed29041 Mon Sep 17 00:00:00 2001 From: Sam Boyer Date: Tue, 18 Feb 2014 17:24:44 -0500 Subject: [PATCH 26/78] Add comment explaining why iota is not used. --- ethwire/messaging.go | 3 +++ peer.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ethwire/messaging.go b/ethwire/messaging.go index 651bf4710..185faa341 100644 --- a/ethwire/messaging.go +++ b/ethwire/messaging.go @@ -19,6 +19,9 @@ var MagicToken = []byte{34, 64, 8, 145} type MsgType byte const ( + // Values are given explicitly instead of by iota because these values are + // defined by the wire protocol spec; it is easier for humans to ensure + // correctness when values are explicit. MsgHandshakeTy = 0x00 MsgDiscTy = 0x01 MsgPingTy = 0x02 diff --git a/peer.go b/peer.go index e64b5b3bd..a6481e10c 100644 --- a/peer.go +++ b/peer.go @@ -23,6 +23,9 @@ const ( type DiscReason byte const ( + // Values are given explicitly instead of by iota because these values are + // defined by the wire protocol spec; it is easier for humans to ensure + // correctness when values are explicit. DiscReRequested = 0x00 DiscReTcpSysErr = 0x01 DiscBadProto = 0x02 From 24f2b2afc3a848190822c382e6aa31c8ab120f07 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 19 Feb 2014 11:35:17 +0100 Subject: [PATCH 27/78] Running contracts fixed --- ethchain/block.go | 53 +++++++++++++++++++----- ethchain/block_manager.go | 35 ++++++++++------ ethchain/block_manager_test.go | 74 +++++++--------------------------- ethchain/stack.go | 9 ++++- ethchain/transaction.go | 5 ++- ethchain/transaction_test.go | 16 ++++---- ethereum.go | 2 +- ethutil/big.go | 6 +++ ethutil/config.go | 14 ++++--- ethutil/trie.go | 4 ++ 10 files changed, 120 insertions(+), 98 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index 34ddf9fec..ae654b7d8 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -46,6 +46,8 @@ type Block struct { // List of transactions and/or contracts transactions []*Transaction TxSha []byte + + contractStates map[string]*ethutil.Trie } // New block takes a raw encoded string @@ -79,14 +81,15 @@ func CreateBlock(root interface{}, block := &Block{ // Slice of transactions to include in this block - transactions: txes, - PrevHash: prevHash, - Coinbase: base, - Difficulty: Difficulty, - Nonce: Nonce, - Time: time.Now().Unix(), - Extra: extra, - UncleSha: EmptyShaList, + transactions: txes, + PrevHash: prevHash, + Coinbase: base, + Difficulty: Difficulty, + Nonce: Nonce, + Time: time.Now().Unix(), + Extra: extra, + UncleSha: EmptyShaList, + contractStates: make(map[string]*ethutil.Trie), } block.SetTransactions(txes) block.SetUncles([]*Block{}) @@ -131,6 +134,13 @@ func (block *Block) GetContract(addr []byte) *Contract { contract := &Contract{} contract.RlpDecode([]byte(data)) + cachedState := block.contractStates[string(addr)] + if cachedState != nil { + contract.state = cachedState + } else { + block.contractStates[string(addr)] = contract.state + } + return contract } func (block *Block) UpdateContract(addr []byte, contract *Contract) { @@ -190,6 +200,25 @@ func (block *Block) BlockInfo() BlockInfo { return bi } +// Sync the block's state and contract respectively +func (block *Block) Sync() { + // Sync all contracts currently in cache + for _, val := range block.contractStates { + val.Sync() + } + // Sync the block state itself + block.state.Sync() +} + +func (block *Block) Undo() { + // Sync all contracts currently in cache + for _, val := range block.contractStates { + val.Undo() + } + // Sync the block state itself + block.state.Undo() +} + func (block *Block) MakeContract(tx *Transaction) { // Create contract if there's no recipient if tx.IsContract() { @@ -199,9 +228,14 @@ func (block *Block) MakeContract(tx *Transaction) { contract := NewContract(value, []byte("")) block.state.Update(string(addr), string(contract.RlpEncode())) for i, val := range tx.Data { - contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val) + if len(val) > 0 { + bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) + contract.state.Update(string(bytNum), val) + } } block.UpdateContract(addr, contract) + + block.contractStates[string(addr)] = contract.state } } @@ -288,6 +322,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) { block.Time = int64(header.Get(6).BigInt().Uint64()) block.Extra = header.Get(7).Str() block.Nonce = header.Get(8).Bytes() + block.contractStates = make(map[string]*ethutil.Trie) // Tx list might be empty if this is an uncle. Uncles only have their // header set. diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 4e72f51ba..33df338ff 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -107,7 +107,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { // we don't want to undo but since undo only happens on dirty // nodes this won't happen because Commit would have been called // before that. - defer bm.bc.CurrentBlock.State().Undo() + defer bm.bc.CurrentBlock.Undo() hash := block.Hash() @@ -142,7 +142,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { // Calculate the new total difficulty and sync back to the db if bm.CalculateTD(block) { // Sync the current block's state to the database and cancelling out the deferred Undo - bm.bc.CurrentBlock.State().Sync() + bm.bc.CurrentBlock.Sync() // Add the block to the chain bm.bc.Add(block) @@ -280,11 +280,13 @@ func (bm *BlockManager) Stop() { func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) { // Recovering function in case the VM had any errors - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered from VM execution with err =", r) - } - }() + /* + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered from VM execution with err =", r) + } + }() + */ // Process contract bm.ProcContract(tx, block, func(opType OpType) bool { @@ -305,6 +307,7 @@ func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallbac blockInfo := bm.bc.BlockInfo(block) contract := block.GetContract(tx.Hash()) + if contract == nil { fmt.Println("Contract not found") return @@ -313,7 +316,7 @@ func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallbac Pow256 := ethutil.BigPow(2, 256) if ethutil.Config.Debug { - fmt.Printf("# op arg\n") + fmt.Printf("# op\n") } out: for { @@ -321,9 +324,11 @@ out: base := new(big.Int) // XXX Should Instr return big int slice instead of string slice? // Get the next instruction from the contract - //op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc))))) - nb := ethutil.NumberToBytes(uint64(pc), 32) - o, _, _ := ethutil.Instr(contract.State().Get(string(nb))) + nb := ethutil.BigToBytes(big.NewInt(int64(pc)), 256) + r := contract.State().Get(string(nb)) + v := ethutil.NewValueFromBytes([]byte(r)) + //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb) + o := v.Uint() op := OpCode(o) if !cb(0) { @@ -575,7 +580,10 @@ out: case oSSTORE: // Store Y at index X x, y := bm.stack.Popn() - contract.State().Update(x.String(), string(ethutil.Encode(y))) + idx := ethutil.BigToBytes(x, 256) + val := ethutil.NewValue(y) + //fmt.Printf("STORING VALUE: %v @ %v\n", val.BigInt(), ethutil.BigD(idx)) + contract.State().Update(string(idx), string(val.Encode())) case oJMP: x := int(bm.stack.Pop().Uint64()) // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) @@ -617,7 +625,10 @@ out: bm.TransactionPool.QueueTransaction(tx) case oSUICIDE: //addr := bm.stack.Pop() + default: + fmt.Println("Invalid OPCODE", op) } + //bm.stack.Print() pc++ } } diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go index 502c50b97..ae29e2e25 100644 --- a/ethchain/block_manager_test.go +++ b/ethchain/block_manager_test.go @@ -1,75 +1,29 @@ package ethchain -/* import ( _ "fmt" + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethutil" + "math/big" "testing" ) func TestVm(t *testing.T) { InitFees() + ethutil.ReadConfig("") - db, _ := NewMemDatabase() - Db = db + db, _ := ethdb.NewMemDatabase() + ethutil.Config.Db = db + bm := NewBlockManager(nil) - ctrct := NewTransaction("", 200000000, []string{ - "PUSH", "1a2f2e", - "PUSH", "hallo", - "POP", // POP hallo - "PUSH", "3", - "LOAD", // Load hallo back on the stack - - "PUSH", "1", - "PUSH", "2", - "ADD", - - "PUSH", "2", - "PUSH", "1", - "SUB", - - "PUSH", "100000000000000000000000", - "PUSH", "10000000000000", - "SDIV", - - "PUSH", "105", - "PUSH", "200", - "MOD", - - "PUSH", "100000000000000000000000", - "PUSH", "10000000000000", - "SMOD", - - "PUSH", "5", - "PUSH", "10", - "LT", - - "PUSH", "5", - "PUSH", "5", - "LE", - - "PUSH", "50", - "PUSH", "5", - "GT", - - "PUSH", "5", - "PUSH", "5", - "GE", - - "PUSH", "10", - "PUSH", "10", - "NOT", - - "MYADDRESS", - "TXSENDER", + block := bm.bc.genesisBlock + ctrct := NewTransaction(ContractAddr, big.NewInt(200000000), []string{ + "PUSH", + "1", + "PUSH", + "2", "STOP", }) - tx := NewTransaction("1e8a42ea8cce13", 100, []string{}) - - block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx}) - db.Put(block.Hash(), block.RlpEncode()) - - bm := NewBlockManager() - bm.ProcessBlock(block) + bm.ApplyTransactions(block, []*Transaction{ctrct}) } -*/ diff --git a/ethchain/stack.go b/ethchain/stack.go index c80d01e5c..02834bd78 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -163,5 +163,12 @@ func (st *Stack) Push(d *big.Int) { st.data = append(st.data, d) } func (st *Stack) Print() { - fmt.Println(st.data) + fmt.Println("# val (STACK)") + if len(st.data) > 0 { + for i, val := range st.data { + fmt.Printf("%-3d %v\n", i, val) + } + } else { + fmt.Println("-- empty --") + } } diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 1a9258201..46f5e7e4c 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -1,11 +1,14 @@ package ethchain import ( + "bytes" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/secp256k1-go" "math/big" ) +var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + type Transaction struct { Nonce uint64 Recipient []byte @@ -65,7 +68,7 @@ func (tx *Transaction) Hash() []byte { } func (tx *Transaction) IsContract() bool { - return len(tx.Recipient) == 0 + return bytes.Compare(tx.Recipient, ContractAddr) == 0 } func (tx *Transaction) Signature(key []byte) []byte { diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go index c9090b83d..a49768aea 100644 --- a/ethchain/transaction_test.go +++ b/ethchain/transaction_test.go @@ -2,8 +2,6 @@ package ethchain import ( "encoding/hex" - "fmt" - "github.com/ethereum/eth-go/ethutil" "math/big" "testing" ) @@ -42,13 +40,15 @@ func TestAddressRetrieval2(t *testing.T) { tx.Sign(key) //data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7") //tx := NewTransactionFromData(data) - fmt.Println(tx.RlpValue()) + /* + fmt.Println(tx.RlpValue()) - fmt.Printf("rlp %x\n", tx.RlpEncode()) - fmt.Printf("sha rlp %x\n", tx.Hash()) + fmt.Printf("rlp %x\n", tx.RlpEncode()) + fmt.Printf("sha rlp %x\n", tx.Hash()) - //tx.Sign(key) + //tx.Sign(key) - fmt.Printf("hex tx key %x\n", tx.PublicKey()) - fmt.Printf("seder %x\n", tx.Sender()) + fmt.Printf("hex tx key %x\n", tx.PublicKey()) + fmt.Printf("seder %x\n", tx.Sender()) + */ } diff --git a/ethereum.go b/ethereum.go index c54303795..e3038cbf9 100644 --- a/ethereum.go +++ b/ethereum.go @@ -252,7 +252,7 @@ func (s *Ethereum) Start() { if ethutil.Config.Seed { log.Println("Seeding") // Testnet seed bootstrapping - resp, err := http.Get("http://www.ethereum.org/servers.poc2.txt") + resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt") if err != nil { log.Println("Fetching seed failed:", err) return diff --git a/ethutil/big.go b/ethutil/big.go index 979078bef..c41d63add 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -35,3 +35,9 @@ func BigD(data []byte) *big.Int { return n } + +func BigToBytes(num *big.Int, base int) []byte { + ret := make([]byte, base/8) + + return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) +} diff --git a/ethutil/config.go b/ethutil/config.go index df1772d81..70553fb5b 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -34,12 +34,14 @@ func ReadConfig(base string) *config { 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) + if len(base) > 0 { + //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) + } } } diff --git a/ethutil/trie.go b/ethutil/trie.go index 95abca602..c25bd80cb 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -119,6 +119,10 @@ func (t *Trie) Undo() { t.Root = t.prevRoot } +func (t *Trie) Cache() *Cache { + return t.cache +} + /* * Public (query) interface functions */ From b3da104e569e15c710e6b95ac00c9c2fc80bc9ae Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 19 Feb 2014 16:26:35 +0100 Subject: [PATCH 28/78] Corrected contract addresses --- ethchain/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/block.go b/ethchain/block.go index ae654b7d8..0b4f93e8c 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -222,7 +222,7 @@ func (block *Block) Undo() { func (block *Block) MakeContract(tx *Transaction) { // Create contract if there's no recipient if tx.IsContract() { - addr := tx.Hash() + addr := tx.Hash()[12:] value := tx.Value contract := NewContract(value, []byte("")) From 8e7daec886aed591436be111f2b199932b35ddba Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 19 Feb 2014 16:26:55 +0100 Subject: [PATCH 29/78] Added fees and debugging --- ethchain/block_manager.go | 54 ++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 33df338ff..1fcc06be4 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -301,12 +301,12 @@ func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) { // Contract evaluation is done here. func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) { - + addr := tx.Hash()[12:] // Instruction pointer pc := 0 blockInfo := bm.bc.BlockInfo(block) - contract := block.GetContract(tx.Hash()) + contract := block.GetContract(addr) if contract == nil { fmt.Println("Contract not found") @@ -318,8 +318,12 @@ func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallbac if ethutil.Config.Debug { fmt.Printf("# op\n") } + + stepcount := 0 + totalFee := new(big.Int) out: for { + stepcount++ // The base big int for all calculations. Use this for any results. base := new(big.Int) // XXX Should Instr return big int slice instead of string slice? @@ -331,12 +335,40 @@ out: o := v.Uint() op := OpCode(o) + var fee *big.Int = new(big.Int) + if stepcount > 16 { + fee.Add(fee, StepFee) + } + + // Calculate the fees + switch op { + /* + FIXME (testnet requires no funds yet) + case oSSTORE: + fee.Add(fee, StoreFee) + case oSLOAD: + fee.Add(fee, StoreFee) + */ + case oEXTRO, oBALANCE: + fee.Add(fee, ExtroFee) + case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID: + fee.Add(fee, CryptoFee) + case oMKTX: + fee.Add(fee, ContractFee) + } + + if contract.Amount.Cmp(fee) < 0 { + break + } + // Add the fee to the total fee. It's subtracted when we're done looping + totalFee.Add(totalFee, fee) + if !cb(0) { break } if ethutil.Config.Debug { - fmt.Printf("%-3d %-4s\n", pc, op.String()) + fmt.Printf("%-3d %-4s", pc, op.String()) } switch op { @@ -453,10 +485,6 @@ out: } else { bm.stack.Push(ethutil.BigFalse) } - - // Please note that the following code contains some - // ugly string casting. This will have to change to big - // ints. TODO :) case oMYADDRESS: bm.stack.Push(ethutil.BigD(tx.Hash())) case oTXSENDER: @@ -579,11 +607,10 @@ out: } case oSSTORE: // Store Y at index X - x, y := bm.stack.Popn() + y, x := bm.stack.Popn() idx := ethutil.BigToBytes(x, 256) - val := ethutil.NewValue(y) - //fmt.Printf("STORING VALUE: %v @ %v\n", val.BigInt(), ethutil.BigD(idx)) - contract.State().Update(string(idx), string(val.Encode())) + fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(idx)) + contract.State().Update(string(idx), string(y.Bytes())) case oJMP: x := int(bm.stack.Pop().Uint64()) // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) @@ -628,9 +655,12 @@ out: default: fmt.Println("Invalid OPCODE", op) } - //bm.stack.Print() + fmt.Println("") + bm.stack.Print() pc++ } + + block.UpdateContract(addr, contract) } // Returns an address from the specified contract's address From dc994b35189950a15fc5397b7f17cda2ba62e0c3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 19 Feb 2014 16:27:08 +0100 Subject: [PATCH 30/78] Changed fee structure --- ethchain/fees.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ethchain/fees.go b/ethchain/fees.go index 57017cdc9..0096871d1 100644 --- a/ethchain/fees.go +++ b/ethchain/fees.go @@ -4,20 +4,18 @@ import ( "math/big" ) -var StepFee *big.Int = new(big.Int) var TxFeeRat *big.Int = big.NewInt(100000000000000) + var TxFee *big.Int = big.NewInt(100) -var ContractFee *big.Int = new(big.Int) -var MemFee *big.Int = new(big.Int) -var DataFee *big.Int = new(big.Int) -var CryptoFee *big.Int = new(big.Int) -var ExtroFee *big.Int = new(big.Int) +var StepFee *big.Int = big.NewInt(1) +var StoreFee *big.Int = big.NewInt(0) +var DataFee *big.Int = big.NewInt(20) +var ExtroFee *big.Int = big.NewInt(40) +var CryptoFee *big.Int = big.NewInt(20) +var ContractFee *big.Int = big.NewInt(100) var BlockReward *big.Int = big.NewInt(1.5e+18) - var UncleReward *big.Int = big.NewInt(1.125e+18) - -//var UncleReward *big.Int = big.NewInt(2e18) var UncleInclusionReward *big.Int = big.NewInt(1.875e+17) var Period1Reward *big.Int = new(big.Int) @@ -26,6 +24,12 @@ var Period3Reward *big.Int = new(big.Int) var Period4Reward *big.Int = new(big.Int) func InitFees() { + StepFee.Mul(StepFee, TxFeeRat) + StoreFee.Mul(StoreFee, TxFeeRat) + DataFee.Mul(DataFee, TxFeeRat) + ExtroFee.Mul(ExtroFee, TxFeeRat) + CryptoFee.Mul(CryptoFee, TxFeeRat) + ContractFee.Mul(ContractFee, TxFeeRat) /* // Base for 2**64 b60 := new(big.Int) From d4cc125456c2531f133aaac6ea73d5decbb80dc4 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 19 Feb 2014 16:27:22 +0100 Subject: [PATCH 31/78] Added more logging functions --- ethutil/config.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/ethutil/config.go b/ethutil/config.go index e61c347de..607152b5b 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -46,7 +46,7 @@ func ReadConfig(base string) *config { } Config = &config{ExecPath: path, Debug: true, Ver: "0.2.3"} - Config.Log = NewLogger(LogFile|LogStd, 0) + Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) } return Config @@ -67,7 +67,7 @@ type Logger struct { func NewLogger(flag LoggerType, level int) Logger { var loggers []*log.Logger - flags := log.LstdFlags | log.Lshortfile + flags := log.LstdFlags 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) @@ -75,20 +75,25 @@ func NewLogger(flag LoggerType, level int) Logger { log.Panic("unable to create file logger", err) } - log := log.New(file, "[ETH]", flags) + log := log.New(file, "", flags) loggers = append(loggers, log) } if flag&LogStd > 0 { - log := log.New(os.Stdout, "[ETH]", flags) + log := log.New(os.Stdout, "", flags) loggers = append(loggers, log) } return Logger{logSys: loggers, logLevel: level} } +const ( + LogLevelDebug = iota + LogLevelInfo +) + func (log Logger) Debugln(v ...interface{}) { - if log.logLevel != 0 { + if log.logLevel != LogLevelDebug { return } @@ -98,7 +103,27 @@ func (log Logger) Debugln(v ...interface{}) { } func (log Logger) Debugf(format string, v ...interface{}) { - if log.logLevel != 0 { + if log.logLevel != LogLevelDebug { + return + } + + for _, logger := range log.logSys { + logger.Printf(format, v...) + } +} + +func (log Logger) Infoln(v ...interface{}) { + if log.logLevel > LogLevelInfo { + return + } + + for _, logger := range log.logSys { + logger.Println(v...) + } +} + +func (log Logger) Infof(format string, v ...interface{}) { + if log.logLevel > LogLevelInfo { return } From 39b6eaf51cc278feb817570e7fe02fc7ceefeb9e Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 19 Feb 2014 16:27:35 +0100 Subject: [PATCH 32/78] Debug logging functions --- ethchain/stack.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index 02834bd78..74f3d7ec9 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -163,7 +163,7 @@ func (st *Stack) Push(d *big.Int) { st.data = append(st.data, d) } func (st *Stack) Print() { - fmt.Println("# val (STACK)") + fmt.Println("### STACK ###") if len(st.data) > 0 { for i, val := range st.data { fmt.Printf("%-3d %v\n", i, val) @@ -171,4 +171,5 @@ func (st *Stack) Print() { } else { fmt.Println("-- empty --") } + fmt.Println("#############") } From 4afb624c450229b5f0a3554d13de35fd2db8f682 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 19 Feb 2014 16:28:08 +0100 Subject: [PATCH 33/78] WIP state object --- ethutil/trie.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ethutil/trie.go b/ethutil/trie.go index c25bd80cb..e900a0a63 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -5,6 +5,15 @@ import ( "reflect" ) +// TODO +// A StateObject is an object that has a state root +// This is goig to be the object for the second level caching (the caching of object which have a state such as contracts) +type StateObject interface { + State() *Trie + Sync() + Undo() +} + type Node struct { Key []byte Value *Value From 9bc5c4a0c5116cf3b49a55fc2aceb5f5c3b3e34d Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Feb 2014 14:40:00 +0100 Subject: [PATCH 34/78] Long over due Trie delete implemented --- ethutil/trie.go | 88 +++++++++++++++++++++++++++++++++++++++++++- ethutil/trie_test.go | 36 +++++++++++++++++- 2 files changed, 121 insertions(+), 3 deletions(-) diff --git a/ethutil/trie.go b/ethutil/trie.go index e900a0a63..322f77647 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -148,6 +148,10 @@ func (t *Trie) Get(key string) string { return c.Str() } +func (t *Trie) Delete(key string) { + t.Update(key, "") +} + func (t *Trie) GetState(node interface{}, key []int) interface{} { n := NewValue(node) // Return the node if key is empty (= found) @@ -202,9 +206,10 @@ func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{ return t.InsertState(node, key, value) } else { // delete it + return t.DeleteState(node, key) } - return "" + return t.Root } func (t *Trie) Put(node interface{}) interface{} { @@ -313,6 +318,87 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter return "" } +func (t *Trie) DeleteState(node interface{}, key []int) interface{} { + if len(key) == 0 { + return "" + } + + // New node + n := NewValue(node) + if node == nil || (n.Type() == reflect.String && (n.Str() == "" || n.Get(0).IsNil())) || n.Len() == 0 { + return "" + } + + 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) { + return "" + } else if CompareIntSlice(key[:len(k)], k) { + hash := t.DeleteState(v, key[len(k):]) + child := t.GetNode(hash) + + var newNode []interface{} + if child.Len() == 2 { + newKey := append(k, CompactDecode(child.Get(0).Str())...) + newNode = []interface{}{CompactEncode(newKey), child.Get(1).Raw()} + } else { + newNode = []interface{}{currentNode.Get(0).Str(), hash} + } + + return t.Put(newNode) + } else { + return node + } + } else { + // Copy the current node over to the new node and replace the first nibble in the key + n := EmptyStringSlice(17) + var newNode []interface{} + + for i := 0; i < 17; i++ { + cpy := currentNode.Get(i).Raw() + if cpy != nil { + n[i] = cpy + } + } + + n[key[0]] = t.DeleteState(n[key[0]], key[1:]) + amount := -1 + for i := 0; i < 17; i++ { + if n[i] != "" { + if amount == -1 { + amount = i + } else { + amount = -2 + } + } + } + if amount == 16 { + newNode = []interface{}{CompactEncode([]int{16}), n[amount]} + } else if amount >= 0 { + child := t.GetNode(n[amount]) + if child.Len() == 17 { + newNode = []interface{}{CompactEncode([]int{amount}), n[amount]} + } else if child.Len() == 2 { + key := append([]int{amount}, CompactDecode(child.Get(0).Str())...) + newNode = []interface{}{CompactEncode(key), child.Get(1).Str()} + } + + } else { + newNode = n + } + + 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)) diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index fa60c8cfc..9d2c8e19f 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,8 +1,7 @@ package ethutil import ( - _ "encoding/hex" - _ "fmt" + "reflect" "testing" ) @@ -116,3 +115,36 @@ func TestTrieCmp(t *testing.T) { t.Errorf("Expected tries not to be equal %x %x", trie1.Root, trie2.Root) } } + +func TestTrieDelete(t *testing.T) { + _, trie := New() + trie.Update("cat", LONG_WORD) + exp := trie.Root + trie.Update("dog", LONG_WORD) + trie.Delete("dog") + if !reflect.DeepEqual(exp, trie.Root) { + t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root) + } + + trie.Update("dog", LONG_WORD) + exp = trie.Root + trie.Update("dude", LONG_WORD) + trie.Delete("dude") + if !reflect.DeepEqual(exp, trie.Root) { + t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root) + } +} + +func TestTrieDeleteWithValue(t *testing.T) { + _, trie := New() + trie.Update("c", LONG_WORD) + exp := trie.Root + trie.Update("ca", LONG_WORD) + trie.Update("cat", LONG_WORD) + trie.Delete("ca") + trie.Delete("cat") + if !reflect.DeepEqual(exp, trie.Root) { + t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root) + } + +} From 059ad352156b995aab9132aff9569c2e03c32b9b Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Feb 2014 23:10:05 +0100 Subject: [PATCH 35/78] Type checking --- ethutil/value.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ethutil/value.go b/ethutil/value.go index d3a38f87f..03d23d559 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -36,7 +36,8 @@ func (val *Value) Len() int { if data, ok := val.Val.([]interface{}); ok { return len(data) } else if data, ok := val.Val.([]byte); ok { - // FIXME + return len(data) + } else if data, ok := val.Val.(string); ok { return len(data) } @@ -139,6 +140,19 @@ func (val *Value) SliceFromTo(from, to int) *Value { return NewValue(slice[from:to]) } +// TODO More type checking methods +func (val *Value) IsSlice() bool { + return val.Type() == reflect.Slice +} + +func (val *Value) IsStr() bool { + return val.Type() == reflect.String +} + +func (val *Value) IsEmpty() bool { + return (val.IsSlice() || val.IsStr()) && val.Len() == 0 +} + // Threat the value as a slice func (val *Value) Get(idx int) *Value { if d, ok := val.Val.([]interface{}); ok { From 504d356232e11e98a19f9f2b6fd5ee61a5226b1d Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Feb 2014 23:10:16 +0100 Subject: [PATCH 36/78] Added peek(n) --- ethchain/stack.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ethchain/stack.go b/ethchain/stack.go index 74f3d7ec9..e08f84082 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -159,6 +159,22 @@ func (st *Stack) Popn() (*big.Int, *big.Int) { return ints[0], ints[1] } +func (st *Stack) Peek() *big.Int { + s := len(st.data) + + str := st.data[s-1] + + return str +} + +func (st *Stack) Peekn() (*big.Int, *big.Int) { + s := len(st.data) + + ints := st.data[s-2:] + + return ints[0], ints[1] +} + func (st *Stack) Push(d *big.Int) { st.data = append(st.data, d) } From 8f69c2ac45a1109985ad8cc3b98dcd2e315dc8e9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Feb 2014 23:10:36 +0100 Subject: [PATCH 37/78] Added contract addr acessors --- ethchain/contract.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ethchain/contract.go b/ethchain/contract.go index 70189593b..5dccb8728 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -30,6 +30,14 @@ func (c *Contract) RlpDecode(data []byte) { c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()) } +func (c *Contract) Addr(addr []byte) *ethutil.Value { + return ethutil.NewValueFromBytes([]byte(c.state.Get(string(addr)))) +} + +func (c *Contract) SetAddr(addr []byte, value interface{}) { + c.state.Update(string(addr), string(ethutil.NewValue(value).Encode())) +} + func (c *Contract) State() *ethutil.Trie { return c.state } From ed05779adb27d715b52de99022b6d927e5fe4706 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Feb 2014 23:10:43 +0100 Subject: [PATCH 38/78] Updated fees --- ethchain/fees.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/fees.go b/ethchain/fees.go index 0096871d1..02f09fa04 100644 --- a/ethchain/fees.go +++ b/ethchain/fees.go @@ -8,7 +8,7 @@ var TxFeeRat *big.Int = big.NewInt(100000000000000) var TxFee *big.Int = big.NewInt(100) var StepFee *big.Int = big.NewInt(1) -var StoreFee *big.Int = big.NewInt(0) +var StoreFee *big.Int = big.NewInt(5) var DataFee *big.Int = big.NewInt(20) var ExtroFee *big.Int = big.NewInt(40) var CryptoFee *big.Int = big.NewInt(20) From 06ea7fc8308265e80b24352f676315ed4c826b6a Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Feb 2014 23:11:17 +0100 Subject: [PATCH 39/78] re: Added contract fees --- ethchain/block_manager.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 1fcc06be4..1847ba2d4 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -336,19 +336,23 @@ out: op := OpCode(o) var fee *big.Int = new(big.Int) + var fee2 *big.Int = new(big.Int) if stepcount > 16 { fee.Add(fee, StepFee) } // Calculate the fees switch op { - /* - FIXME (testnet requires no funds yet) - case oSSTORE: - fee.Add(fee, StoreFee) - case oSLOAD: - fee.Add(fee, StoreFee) - */ + case oSSTORE: + y, x := bm.stack.Peekn() + val := contract.Addr(ethutil.BigToBytes(x, 256)) + if val.IsEmpty() && len(y.Bytes()) > 0 { + fee2.Add(DataFee, StoreFee) + } else { + fee2.Sub(DataFee, StoreFee) + } + case oSLOAD: + fee.Add(fee, StoreFee) case oEXTRO, oBALANCE: fee.Add(fee, ExtroFee) case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID: @@ -357,11 +361,12 @@ out: fee.Add(fee, ContractFee) } - if contract.Amount.Cmp(fee) < 0 { + tf := new(big.Int).Add(fee, fee2) + if contract.Amount.Cmp(tf) < 0 { break } // Add the fee to the total fee. It's subtracted when we're done looping - totalFee.Add(totalFee, fee) + totalFee.Add(totalFee, tf) if !cb(0) { break @@ -608,9 +613,10 @@ out: case oSSTORE: // Store Y at index X y, x := bm.stack.Popn() - idx := ethutil.BigToBytes(x, 256) - fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(idx)) - contract.State().Update(string(idx), string(y.Bytes())) + addr := ethutil.BigToBytes(x, 256) + fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr)) + contract.SetAddr(addr, y) + //contract.State().Update(string(idx), string(y)) case oJMP: x := int(bm.stack.Pop().Uint64()) // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) From f2a1260294b25a31452fd00fe59820467f5cd86a Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Feb 2014 12:36:22 +0100 Subject: [PATCH 40/78] Nil is also considered empty --- ethutil/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethutil/value.go b/ethutil/value.go index 03d23d559..3dd84d12d 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -150,7 +150,7 @@ func (val *Value) IsStr() bool { } func (val *Value) IsEmpty() bool { - return (val.IsSlice() || val.IsStr()) && val.Len() == 0 + return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0) } // Threat the value as a slice From b20c0b1d59f4109c49c7351ddeecbe195912da38 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Feb 2014 12:36:41 +0100 Subject: [PATCH 41/78] Removed all old code --- ethutil/parsing.go | 145 +++++++++++++++++++++------------------------ 1 file changed, 69 insertions(+), 76 deletions(-) diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 2c41fb4df..b43dac064 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -1,95 +1,88 @@ 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", +var OpCodes = map[string]byte{ + "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, + "TXVALUE": 18, + "TXFEE": 19, + "TXDATAN": 20, + "TXDATA": 21, + "BLK_PREVHASH": 22, + "BLK_COINBASE": 23, + "BLK_TIMESTAMP": 24, + "BLK_NUMBER": 25, + "BLK_DIFFICULTY": 26, + "BASEFEE": 27, + "SHA256": 32, + "RIPEMD160": 33, + "ECMUL": 34, + "ECADD": 35, + "ECSIGN": 36, + "ECRECOVER": 37, + "ECVALID": 38, + "SHA3": 39, + "PUSH": 48, + "POP": 49, + "DUP": 50, + "SWAP": 51, + "MLOAD": 52, + "MSTORE": 53, + "SLOAD": 54, + "SSTORE": 55, + "JMP": 56, + "JMPI": 57, + "IND": 58, + "EXTRO": 59, + "BALANCE": 60, + "MKTX": 61, + "SUICIDE": 62, } -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])) +func IsOpCode(s string) bool { + for key, _ := range OpCodes { + if key == s { + return true + } + } + return false +} + +func CompileInstr(s string) ([]byte, error) { + isOp := IsOpCode(s) + if isOp { + return []byte{OpCodes[s]}, nil } - code := OpCodes[tokens[0]] // Replace op codes with the proper numerical equivalent - op := new(big.Int) - op.SetString(code, 0) + num := new(big.Int) + num.SetString(s, 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 + return num.Bytes(), nil } func Instr(instr string) (int, []string, error) { + base := new(big.Int) base.SetString(instr, 0) From 681eacaa7fdda41fe168baba03095ee74708444f Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Feb 2014 12:37:06 +0100 Subject: [PATCH 42/78] Removed old instruction code --- ethchain/transaction.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 46f5e7e4c..2417bbd7d 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -26,12 +26,9 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { // Serialize the data tx.Data = make([]string, len(data)) for i, val := range data { - instr, err := ethutil.CompileInstr(val) - if err != nil { - //fmt.Printf("compile error:%d %v\n", i+1, err) - } + instr, _ := ethutil.CompileInstr(val) - tx.Data[i] = instr + tx.Data[i] = string(instr) } return &tx From 18cc35338afc8a3843716af0d96bd03d36e735ea Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Feb 2014 12:37:16 +0100 Subject: [PATCH 43/78] Fixed contract running --- ethchain/block_manager.go | 18 ++++++++++-------- ethchain/block_manager_test.go | 1 - 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 1847ba2d4..91bcaa468 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -321,19 +321,20 @@ func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallbac stepcount := 0 totalFee := new(big.Int) + + // helper function for getting a contract's memory address + getMem := func(num int) *ethutil.Value { + nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256) + return contract.Addr(nb) + } out: for { stepcount++ // The base big int for all calculations. Use this for any results. base := new(big.Int) - // XXX Should Instr return big int slice instead of string slice? - // Get the next instruction from the contract - nb := ethutil.BigToBytes(big.NewInt(int64(pc)), 256) - r := contract.State().Get(string(nb)) - v := ethutil.NewValueFromBytes([]byte(r)) + val := getMem(pc) //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb) - o := v.Uint() - op := OpCode(o) + op := OpCode(val.Uint()) var fee *big.Int = new(big.Int) var fee2 *big.Int = new(big.Int) @@ -378,6 +379,7 @@ out: switch op { case oSTOP: + fmt.Println("") break out case oADD: x, y := bm.stack.Popn() @@ -580,7 +582,7 @@ out: case oECVALID: case oPUSH: pc++ - bm.stack.Push(bm.mem[strconv.Itoa(pc)]) + bm.stack.Push(getMem(pc).BigInt()) case oPOP: // Pop current value of the stack bm.stack.Pop() diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go index ae29e2e25..853d459d8 100644 --- a/ethchain/block_manager_test.go +++ b/ethchain/block_manager_test.go @@ -22,7 +22,6 @@ func TestVm(t *testing.T) { "1", "PUSH", "2", - "STOP", }) bm.ApplyTransactions(block, []*Transaction{ctrct}) From cca8585554119a4dc02c6720948012bf876a1db8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Feb 2014 13:05:59 +0100 Subject: [PATCH 44/78] Get a chain of blocks made simple --- ethchain/block_chain.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 5b55782a9..21f540b96 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -2,6 +2,7 @@ package ethchain import ( "bytes" + "fmt" "github.com/ethereum/eth-go/ethutil" "log" "math" @@ -111,6 +112,25 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} { return chain } +func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block { + genHash := bc.genesisBlock.Hash() + + block := bc.GetBlock(hash) + var blocks []*Block + + for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) { + fmt.Println(block) + blocks = append([]*Block{block}, blocks...) + + if bytes.Compare(genHash, block.Hash()) == 0 { + break + } + i++ + } + + return blocks +} + func (bc *BlockChain) setLastBlock() { data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) if len(data) != 0 { @@ -147,6 +167,9 @@ func (bc *BlockChain) Add(block *Block) { func (bc *BlockChain) GetBlock(hash []byte) *Block { data, _ := ethutil.Config.Db.Get(hash) + if len(data) == 0 { + return nil + } return NewBlockFromData(data) } From 4bfd717ba2b753f183e9e3fecd91d7e4083aaffc Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 22 Feb 2014 01:53:09 +0100 Subject: [PATCH 45/78] Added the ability to extend the logger with more sub systems --- ethutil/config.go | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/ethutil/config.go b/ethutil/config.go index 607152b5b..5bf56134d 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -1,6 +1,7 @@ package ethutil import ( + "fmt" "log" "os" "os/user" @@ -18,7 +19,7 @@ const ( type config struct { Db Database - Log Logger + Log *Logger ExecPath string Debug bool Ver string @@ -45,7 +46,7 @@ func ReadConfig(base string) *config { } } - Config = &config{ExecPath: path, Debug: true, Ver: "0.2.3"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.3.0"} Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) } @@ -59,13 +60,18 @@ const ( LogStd = 0x2 ) +type LogSystem interface { + Println(v ...interface{}) + Printf(format string, v ...interface{}) +} + type Logger struct { - logSys []*log.Logger + logSys []LogSystem logLevel int } -func NewLogger(flag LoggerType, level int) Logger { - var loggers []*log.Logger +func NewLogger(flag LoggerType, level int) *Logger { + var loggers []LogSystem flags := log.LstdFlags @@ -84,7 +90,11 @@ func NewLogger(flag LoggerType, level int) Logger { loggers = append(loggers, log) } - return Logger{logSys: loggers, logLevel: level} + return &Logger{logSys: loggers, logLevel: level} +} + +func (log *Logger) AddLogSystem(logger LogSystem) { + log.logSys = append(log.logSys, logger) } const ( @@ -92,7 +102,7 @@ const ( LogLevelInfo ) -func (log Logger) Debugln(v ...interface{}) { +func (log *Logger) Debugln(v ...interface{}) { if log.logLevel != LogLevelDebug { return } @@ -102,7 +112,7 @@ func (log Logger) Debugln(v ...interface{}) { } } -func (log Logger) Debugf(format string, v ...interface{}) { +func (log *Logger) Debugf(format string, v ...interface{}) { if log.logLevel != LogLevelDebug { return } @@ -112,17 +122,18 @@ func (log Logger) Debugf(format string, v ...interface{}) { } } -func (log Logger) Infoln(v ...interface{}) { +func (log *Logger) Infoln(v ...interface{}) { if log.logLevel > LogLevelInfo { return } + fmt.Println(len(log.logSys)) for _, logger := range log.logSys { logger.Println(v...) } } -func (log Logger) Infof(format string, v ...interface{}) { +func (log *Logger) Infof(format string, v ...interface{}) { if log.logLevel > LogLevelInfo { return } From 73b9ae95797ce8c38d82cfcb7c793efea268f476 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 22 Feb 2014 01:53:25 +0100 Subject: [PATCH 46/78] Updated some of the log statements to use the ethutil logger --- ethchain/block_chain.go | 2 -- ethereum.go | 16 ++++++++-------- peer.go | 30 ++++++++++++++---------------- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 21f540b96..96d22366d 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -2,7 +2,6 @@ package ethchain import ( "bytes" - "fmt" "github.com/ethereum/eth-go/ethutil" "log" "math" @@ -119,7 +118,6 @@ func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block { var blocks []*Block for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) { - fmt.Println(block) blocks = append([]*Block{block}, blocks...) if bytes.Compare(genHash, block.Hash()) == 0 { diff --git a/ethereum.go b/ethereum.go index e3038cbf9..f86cb121e 100644 --- a/ethereum.go +++ b/ethereum.go @@ -70,7 +70,7 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { if usePnp { nat, err = Discover() if err != nil { - log.Println("UPnP failed", err) + ethutil.Config.Log.Debugln("UPnP failed", err) } } @@ -234,7 +234,7 @@ func (s *Ethereum) Start() { log.Println("Connection listening disabled. Acting as client") } else { // Starting accepting connections - log.Println("Ready and accepting connections") + ethutil.Config.Log.Infoln("Ready and accepting connections") // Start the peer handler go s.peerHandler(ln) } @@ -250,7 +250,7 @@ func (s *Ethereum) Start() { s.TxPool.Start() if ethutil.Config.Seed { - log.Println("Seeding") + ethutil.Config.Log.Debugln("Seeding") // Testnet seed bootstrapping resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt") if err != nil { @@ -272,7 +272,7 @@ func (s *Ethereum) peerHandler(listener net.Listener) { for { conn, err := listener.Accept() if err != nil { - log.Println(err) + ethutil.Config.Log.Debugln(err) continue } @@ -315,13 +315,13 @@ out: var err error _, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60) if err != nil { - log.Println("can't add UPnP port mapping:", err) + ethutil.Config.Log.Debugln("can't add UPnP port mapping:", err) break out } if first && err == nil { _, err = s.nat.GetExternalAddress() if err != nil { - log.Println("UPnP can't get external address:", err) + ethutil.Config.Log.Debugln("UPnP can't get external address:", err) continue out } first = false @@ -335,8 +335,8 @@ out: timer.Stop() if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil { - log.Println("unable to remove UPnP port mapping:", err) + ethutil.Config.Log.Debugln("unable to remove UPnP port mapping:", err) } else { - log.Println("succesfully disestablished UPnP port mapping") + ethutil.Config.Log.Debugln("succesfully disestablished UPnP port mapping") } } diff --git a/peer.go b/peer.go index ed81ddd8e..9538e6500 100644 --- a/peer.go +++ b/peer.go @@ -6,7 +6,6 @@ import ( "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "log" "net" "runtime" "strconv" @@ -165,7 +164,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { conn, err := net.DialTimeout("tcp", addr, 30*time.Second) if err != nil { - log.Println("Connection to peer failed", err) + ethutil.Config.Log.Debugln("Connection to peer failed", err) p.Stop() return } @@ -202,7 +201,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) { err := ethwire.WriteMessage(p.conn, msg) if err != nil { - log.Println("Can't send message:", err) + ethutil.Config.Log.Debugln("Can't send message:", err) // Stop the client if there was an error writing to it p.Stop() return @@ -264,7 +263,7 @@ func (p *Peer) HandleInbound() { // Wait for a message from the peer msgs, err := ethwire.ReadMessages(p.conn) if err != nil { - log.Println(err) + ethutil.Config.Log.Debugln(err) } for _, msg := range msgs { switch msg.Type { @@ -277,7 +276,7 @@ func (p *Peer) HandleInbound() { } case ethwire.MsgDiscTy: p.Stop() - log.Println("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint())) + ethutil.Config.Log.Infoln("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint())) case ethwire.MsgPingTy: // Respond back with pong p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, "")) @@ -296,9 +295,8 @@ func (p *Peer) HandleInbound() { if err != nil { if ethutil.Config.Debug { - log.Printf("[PEER] Block %x failed\n", block.Hash()) - log.Printf("[PEER] %v\n", err) - log.Println(block) + ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash()) + ethutil.Config.Log.Infof("[PEER] %v\n", err) } break } else { @@ -309,7 +307,7 @@ func (p *Peer) HandleInbound() { if err != nil { // If the parent is unknown try to catch up with this peer if ethchain.IsParentErr(err) { - log.Println("Attempting to catch up") + ethutil.Config.Log.Infoln("Attempting to catch up") p.catchingUp = false p.CatchupWithPeer() } else if ethchain.IsValidationErr(err) { @@ -321,7 +319,7 @@ func (p *Peer) HandleInbound() { if p.catchingUp && msg.Data.Len() > 1 { if ethutil.Config.Debug && lastBlock != nil { blockInfo := lastBlock.BlockInfo() - log.Printf("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) + ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) } p.catchingUp = false p.CatchupWithPeer() @@ -392,12 +390,12 @@ func (p *Peer) HandleInbound() { p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()})) } case ethwire.MsgNotInChainTy: - log.Printf("Not in chain %x\n", msg.Data) + ethutil.Config.Log.Infoln("Not in chain %x\n", msg.Data) // TODO // Unofficial but fun nonetheless case ethwire.MsgTalkTy: - log.Printf("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str()) + ethutil.Config.Log.Infoln("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str()) } } } @@ -440,7 +438,7 @@ func (p *Peer) Start() { err := p.pushHandshake() if err != nil { - log.Println("Peer can't send outbound version ack", err) + ethutil.Config.Log.Debugln("Peer can't send outbound version ack", err) p.Stop() @@ -499,7 +497,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { c := msg.Data if c.Get(0).Uint() != 5 { - log.Println("Invalid peer version. Require protocol v4") + ethutil.Config.Log.Debugln("Invalid peer version. Require protocol v5") p.Stop() return } @@ -531,7 +529,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { // Get a reference to the peers version p.Version = c.Get(2).Str() - log.Println("[PEER]", p) + ethutil.Config.Log.Debugln("[PEER]", p) } func (p *Peer) String() string { @@ -558,7 +556,7 @@ func (p *Peer) CatchupWithPeer() { msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)}) p.QueueMessage(msg) - log.Printf("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4]) + ethutil.Config.Log.Debugln("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4]) } } From c66cf95b4019eeaf49db0c02cc7cb73c78098f5e Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 23 Feb 2014 01:56:48 +0100 Subject: [PATCH 47/78] Added address states for storing a session based address --- ethchain/address.go | 60 +++++++++++++++++++++++++++++++++++++++ ethchain/address_test.go | 8 ++++++ ethchain/block_manager.go | 36 ++++++++++++++++++----- 3 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 ethchain/address.go create mode 100644 ethchain/address_test.go diff --git a/ethchain/address.go b/ethchain/address.go new file mode 100644 index 000000000..a228c7566 --- /dev/null +++ b/ethchain/address.go @@ -0,0 +1,60 @@ +package ethchain + +import ( + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type Address struct { + Amount *big.Int + Nonce uint64 +} + +func NewAddress(amount *big.Int) *Address { + return &Address{Amount: amount, Nonce: 0} +} + +func NewAddressFromData(data []byte) *Address { + address := &Address{} + address.RlpDecode(data) + + return address +} + +func (a *Address) AddFee(fee *big.Int) { + a.Amount.Add(a.Amount, fee) +} + +func (a *Address) RlpEncode() []byte { + return ethutil.Encode([]interface{}{a.Amount, a.Nonce}) +} + +func (a *Address) RlpDecode(data []byte) { + decoder := ethutil.NewValueFromBytes(data) + + a.Amount = decoder.Get(0).BigInt() + a.Nonce = decoder.Get(1).Uint() +} + +type AddrStateStore struct { + states map[string]*AddressState +} + +func NewAddrStateStore() *AddrStateStore { + return &AddrStateStore{states: make(map[string]*AddressState)} +} + +func (s *AddrStateStore) Add(addr []byte, account *Address) *AddressState { + state := &AddressState{Nonce: account.Nonce, Account: account} + s.states[string(addr)] = state + return state +} + +func (s *AddrStateStore) Get(addr []byte) *AddressState { + return s.states[string(addr)] +} + +type AddressState struct { + Nonce uint64 + Account *Address +} diff --git a/ethchain/address_test.go b/ethchain/address_test.go new file mode 100644 index 000000000..161e1b251 --- /dev/null +++ b/ethchain/address_test.go @@ -0,0 +1,8 @@ +package ethchain + +import ( + "testing" +) + +func TestAddressState(t *testing.T) { +} diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 91bcaa468..b82e5a74a 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "fmt" "github.com/ethereum/eth-go/ethutil" - "github.com/ethereum/eth-go/ethwire" + _ "github.com/ethereum/eth-go/ethwire" "github.com/obscuren/secp256k1-go" "log" "math" @@ -19,6 +19,7 @@ type BlockProcessor interface { ProcessBlock(block *Block) } +// TODO rename to state manager type BlockManager struct { // Mutex for locking the block processor. Blocks can only be handled one at a time mutex sync.Mutex @@ -26,6 +27,10 @@ type BlockManager struct { // The block chain :) bc *BlockChain + // States for addresses. You can watch any address + // at any given time + addrStateStore *AddrStateStore + // Stack for processing contracts stack *Stack // non-persistent key/value memory storage @@ -58,11 +63,12 @@ func AddTestNetFunds(block *Block) { func NewBlockManager(speaker PublicSpeaker) *BlockManager { bm := &BlockManager{ //server: s, - bc: NewBlockChain(), - stack: NewStack(), - mem: make(map[string]*big.Int), - Pow: &EasyPow{}, - Speaker: speaker, + bc: NewBlockChain(), + stack: NewStack(), + mem: make(map[string]*big.Int), + Pow: &EasyPow{}, + Speaker: speaker, + addrStateStore: NewAddrStateStore(), } if bm.bc.CurrentBlock == nil { @@ -81,6 +87,22 @@ func NewBlockManager(speaker PublicSpeaker) *BlockManager { return bm } +// Watches any given address and puts it in the address state store +func (bm *BlockManager) WatchAddr(addr []byte) *AddressState { + account := bm.bc.CurrentBlock.GetAddr(addr) + + return bm.addrStateStore.Add(addr, account) +} + +func (bm *BlockManager) GetAddrState(addr []byte) *AddressState { + addrState := bm.addrStateStore.Get(addr) + if addrState == nil { + addrState = bm.WatchAddr(addr) + } + + return addrState +} + func (bm *BlockManager) BlockChain() *BlockChain { return bm.bc } @@ -165,7 +187,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { */ // Broadcast the valid block back to the wire - bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val}) + //bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val}) // If there's a block processor present, pass in the block for further // processing From f5737b929a972102b16e4b206a52b1e36b508860 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 23 Feb 2014 01:57:04 +0100 Subject: [PATCH 48/78] Added a secondary processor --- ethchain/transaction_pool.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 75a8aa5d1..1278cc4dc 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -34,6 +34,10 @@ type PublicSpeaker interface { Broadcast(msgType ethwire.MsgType, data []interface{}) } +type TxProcessor interface { + ProcessTransaction(tx *Transaction) +} + // The tx pool a thread safe transaction pool handler. In order to // guarantee a non blocking pool we use a queue channel which can be // independently read without needing access to the actual pool. If the @@ -54,7 +58,7 @@ type TxPool struct { BlockManager *BlockManager - Hook TxPoolHook + SecondaryProcessor TxProcessor } func NewTxPool() *TxPool { @@ -69,12 +73,14 @@ func NewTxPool() *TxPool { // Blocking function. Don't use directly. Use QueueTransaction instead func (pool *TxPool) addTransaction(tx *Transaction) { + log.Println("Adding tx to pool") pool.mutex.Lock() pool.pool.PushBack(tx) pool.mutex.Unlock() // Broadcast the transaction to the rest of the peers pool.Speaker.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()}) + log.Println("broadcasting it") } // Process transaction validates the Tx and processes funds from the @@ -179,8 +185,8 @@ out: // doesn't matter since this is a goroutine pool.addTransaction(tx) - if pool.Hook != nil { - pool.Hook <- tx + if pool.SecondaryProcessor != nil { + pool.SecondaryProcessor.ProcessTransaction(tx) } } case <-pool.quit: From a4a4ffbeff2fd9082f2c96330ea0915ae1b6e6c1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 23 Feb 2014 01:57:22 +0100 Subject: [PATCH 49/78] Moved address --- ethchain/contract.go | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/ethchain/contract.go b/ethchain/contract.go index 5dccb8728..68ec39f0b 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -41,34 +41,3 @@ func (c *Contract) SetAddr(addr []byte, value interface{}) { func (c *Contract) State() *ethutil.Trie { return c.state } - -type Address struct { - Amount *big.Int - Nonce uint64 -} - -func NewAddress(amount *big.Int) *Address { - return &Address{Amount: amount, Nonce: 0} -} - -func NewAddressFromData(data []byte) *Address { - address := &Address{} - address.RlpDecode(data) - - return address -} - -func (a *Address) AddFee(fee *big.Int) { - a.Amount.Add(a.Amount, fee) -} - -func (a *Address) RlpEncode() []byte { - return ethutil.Encode([]interface{}{a.Amount, a.Nonce}) -} - -func (a *Address) RlpDecode(data []byte) { - decoder := ethutil.NewValueFromBytes(data) - - a.Amount = decoder.Get(0).BigInt() - a.Nonce = decoder.Get(1).Uint() -} From 3a45cdeaf9682dea0407f827571353220eaf257b Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 23 Feb 2014 01:57:45 +0100 Subject: [PATCH 50/78] Moved txpool start to initialisation method of ethereumm --- ethereum.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ethereum.go b/ethereum.go index f86cb121e..725fe5a3d 100644 --- a/ethereum.go +++ b/ethereum.go @@ -47,6 +47,7 @@ type Ethereum struct { Nonce uint64 Addr net.Addr + Port string peerMut sync.Mutex @@ -93,6 +94,9 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { ethereum.TxPool.BlockManager = ethereum.BlockManager ethereum.BlockManager.TransactionPool = ethereum.TxPool + // Start the tx pool + ethereum.TxPool.Start() + return ethereum, nil } @@ -229,7 +233,7 @@ func (s *Ethereum) ReapDeadPeerHandler() { // Start the ethereum func (s *Ethereum) Start() { // Bind to addr and port - ln, err := net.Listen("tcp", ":30303") + ln, err := net.Listen("tcp", ":"+s.Port) if err != nil { log.Println("Connection listening disabled. Acting as client") } else { @@ -246,9 +250,6 @@ func (s *Ethereum) Start() { // Start the reaping processes go s.ReapDeadPeerHandler() - // Start the tx pool - s.TxPool.Start() - if ethutil.Config.Seed { ethutil.Config.Log.Debugln("Seeding") // Testnet seed bootstrapping @@ -306,7 +307,7 @@ func (s *Ethereum) upnpUpdateThread() { // Go off immediately to prevent code duplication, thereafter we renew // lease every 15 minutes. timer := time.NewTimer(0 * time.Second) - lport, _ := strconv.ParseInt("30303", 10, 16) + lport, _ := strconv.ParseInt(s.Port, 10, 16) first := true out: for { From 8ecb24f1141013a935b0f7e858ef7273d67de5e5 Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Sun, 23 Feb 2014 14:43:18 -0800 Subject: [PATCH 51/78] parse now returns byte[] instead of string --- ethutil/parsing_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go index 482eef3ee..69a5e9016 100644 --- a/ethutil/parsing_test.go +++ b/ethutil/parsing_test.go @@ -13,7 +13,7 @@ func TestCompile(t *testing.T) { } calc := (48 + 0*256 + 0*int64(math.Pow(256, 2))) - if Big(instr).Int64() != calc { + if BigD(instr).Int64() != calc { t.Error("Expected", calc, ", got:", instr) } } From 377c9951033d4f8d157221fd36d15c39ae17cddc Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:10:45 +0100 Subject: [PATCH 52/78] Separated the VM from the block manager and added states --- ethchain/state.go | 56 ++++++ ethchain/vm.go | 437 ++++++++++++++++++++++++++++++++++++++++++++ ethchain/vm_test.go | 106 +++++++++++ 3 files changed, 599 insertions(+) create mode 100644 ethchain/state.go create mode 100644 ethchain/vm.go create mode 100644 ethchain/vm_test.go diff --git a/ethchain/state.go b/ethchain/state.go new file mode 100644 index 000000000..1a18ea1d7 --- /dev/null +++ b/ethchain/state.go @@ -0,0 +1,56 @@ +package ethchain + +import ( + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type State struct { + trie *ethutil.Trie +} + +func NewState(trie *ethutil.Trie) *State { + return &State{trie: trie} +} + +func (s *State) GetContract(addr []byte) *Contract { + data := s.trie.Get(string(addr)) + if data == "" { + return nil + } + + contract := &Contract{} + contract.RlpDecode([]byte(data)) + + return contract +} + +func (s *State) UpdateContract(addr []byte, contract *Contract) { + s.trie.Update(string(addr), string(contract.RlpEncode())) +} + +func Compile(code []string) (script []string) { + script = make([]string, len(code)) + for i, val := range code { + instr, _ := ethutil.CompileInstr(val) + + script[i] = string(instr) + } + + return +} + +func (s *State) GetAccount(addr []byte) (account *Address) { + data := s.trie.Get(string(addr)) + if data == "" { + account = NewAddress(big.NewInt(0)) + } else { + account = NewAddressFromData([]byte(data)) + } + + return +} + +func (s *State) UpdateAccount(addr []byte, account *Address) { + s.trie.Update(string(addr), string(account.RlpEncode())) +} diff --git a/ethchain/vm.go b/ethchain/vm.go new file mode 100644 index 000000000..d5f4d7ad6 --- /dev/null +++ b/ethchain/vm.go @@ -0,0 +1,437 @@ +package ethchain + +import ( + "bytes" + "fmt" + "github.com/ethereum/eth-go/ethutil" + "github.com/obscuren/secp256k1-go" + "log" + "math" + "math/big" +) + +type Vm struct { + txPool *TxPool + // Stack for processing contracts + stack *Stack + // non-persistent key/value memory storage + mem map[string]*big.Int + + vars RuntimeVars +} + +type RuntimeVars struct { + address []byte + blockNumber uint64 + sender []byte + prevHash []byte + coinbase []byte + time int64 + diff *big.Int + txValue *big.Int + txData []string +} + +func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) { + vm.mem = make(map[string]*big.Int) + vm.stack = NewStack() + + addr := vars.address // tx.Hash()[12:] + // Instruction pointer + pc := 0 + + if contract == nil { + fmt.Println("Contract not found") + return + } + + Pow256 := ethutil.BigPow(2, 256) + + if ethutil.Config.Debug { + fmt.Printf("# op\n") + } + + stepcount := 0 + totalFee := new(big.Int) + +out: + for { + stepcount++ + // The base big int for all calculations. Use this for any results. + base := new(big.Int) + val := contract.GetMem(pc) + //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb) + op := OpCode(val.Uint()) + + var fee *big.Int = new(big.Int) + var fee2 *big.Int = new(big.Int) + if stepcount > 16 { + fee.Add(fee, StepFee) + } + + // Calculate the fees + switch op { + case oSSTORE: + y, x := vm.stack.Peekn() + val := contract.Addr(ethutil.BigToBytes(x, 256)) + if val.IsEmpty() && len(y.Bytes()) > 0 { + fee2.Add(DataFee, StoreFee) + } else { + fee2.Sub(DataFee, StoreFee) + } + case oSLOAD: + fee.Add(fee, StoreFee) + case oEXTRO, oBALANCE: + fee.Add(fee, ExtroFee) + case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID: + fee.Add(fee, CryptoFee) + case oMKTX: + fee.Add(fee, ContractFee) + } + + tf := new(big.Int).Add(fee, fee2) + if contract.Amount.Cmp(tf) < 0 { + fmt.Println("Contract fee", ContractFee) + fmt.Println("Insufficient fees to continue running the contract", tf, contract.Amount) + break + } + // Add the fee to the total fee. It's subtracted when we're done looping + totalFee.Add(totalFee, tf) + + if ethutil.Config.Debug { + fmt.Printf("%-3d %-4s", pc, op.String()) + } + + switch op { + case oSTOP: + fmt.Println("") + break out + case oADD: + x, y := vm.stack.Popn() + // (x + y) % 2 ** 256 + base.Add(x, y) + base.Mod(base, Pow256) + // Pop result back on the stack + vm.stack.Push(base) + case oSUB: + x, y := vm.stack.Popn() + // (x - y) % 2 ** 256 + base.Sub(x, y) + base.Mod(base, Pow256) + // Pop result back on the stack + vm.stack.Push(base) + case oMUL: + x, y := vm.stack.Popn() + // (x * y) % 2 ** 256 + base.Mul(x, y) + base.Mod(base, Pow256) + // Pop result back on the stack + vm.stack.Push(base) + case oDIV: + x, y := vm.stack.Popn() + // floor(x / y) + base.Div(x, y) + // Pop result back on the stack + vm.stack.Push(base) + case oSDIV: + x, y := vm.stack.Popn() + // n > 2**255 + if x.Cmp(Pow256) > 0 { + x.Sub(Pow256, x) + } + if y.Cmp(Pow256) > 0 { + y.Sub(Pow256, y) + } + z := new(big.Int) + z.Div(x, y) + if z.Cmp(Pow256) > 0 { + z.Sub(Pow256, z) + } + // Push result on to the stack + vm.stack.Push(z) + case oMOD: + x, y := vm.stack.Popn() + base.Mod(x, y) + vm.stack.Push(base) + case oSMOD: + x, y := vm.stack.Popn() + // n > 2**255 + if x.Cmp(Pow256) > 0 { + x.Sub(Pow256, x) + } + if y.Cmp(Pow256) > 0 { + y.Sub(Pow256, y) + } + z := new(big.Int) + z.Mod(x, y) + if z.Cmp(Pow256) > 0 { + z.Sub(Pow256, z) + } + // Push result on to the stack + vm.stack.Push(z) + case oEXP: + x, y := vm.stack.Popn() + base.Exp(x, y, Pow256) + + vm.stack.Push(base) + case oNEG: + base.Sub(Pow256, vm.stack.Pop()) + vm.stack.Push(base) + case oLT: + x, y := vm.stack.Popn() + // x < y + if x.Cmp(y) < 0 { + vm.stack.Push(ethutil.BigTrue) + } else { + vm.stack.Push(ethutil.BigFalse) + } + case oLE: + x, y := vm.stack.Popn() + // x <= y + if x.Cmp(y) < 1 { + vm.stack.Push(ethutil.BigTrue) + } else { + vm.stack.Push(ethutil.BigFalse) + } + case oGT: + x, y := vm.stack.Popn() + // x > y + if x.Cmp(y) > 0 { + vm.stack.Push(ethutil.BigTrue) + } else { + vm.stack.Push(ethutil.BigFalse) + } + case oGE: + x, y := vm.stack.Popn() + // x >= y + if x.Cmp(y) > -1 { + vm.stack.Push(ethutil.BigTrue) + } else { + vm.stack.Push(ethutil.BigFalse) + } + case oNOT: + x, y := vm.stack.Popn() + // x != y + if x.Cmp(y) != 0 { + vm.stack.Push(ethutil.BigTrue) + } else { + vm.stack.Push(ethutil.BigFalse) + } + case oMYADDRESS: + vm.stack.Push(ethutil.BigD(addr)) + case oTXSENDER: + vm.stack.Push(ethutil.BigD(vars.sender)) + case oTXVALUE: + vm.stack.Push(vars.txValue) + case oTXDATAN: + vm.stack.Push(big.NewInt(int64(len(vars.txData)))) + case oTXDATA: + v := vm.stack.Pop() + // v >= len(data) + if v.Cmp(big.NewInt(int64(len(vars.txData)))) >= 0 { + vm.stack.Push(ethutil.Big("0")) + } else { + vm.stack.Push(ethutil.Big(vars.txData[v.Uint64()])) + } + case oBLK_PREVHASH: + vm.stack.Push(ethutil.BigD(vars.prevHash)) + case oBLK_COINBASE: + vm.stack.Push(ethutil.BigD(vars.coinbase)) + case oBLK_TIMESTAMP: + vm.stack.Push(big.NewInt(vars.time)) + case oBLK_NUMBER: + vm.stack.Push(big.NewInt(int64(vars.blockNumber))) + case oBLK_DIFFICULTY: + vm.stack.Push(vars.diff) + case oBASEFEE: + // e = 10^21 + e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0)) + d := new(big.Rat) + d.SetInt(vars.diff) + c := new(big.Rat) + c.SetFloat64(0.5) + // d = diff / 0.5 + d.Quo(d, c) + // base = floor(d) + base.Div(d.Num(), d.Denom()) + + x := new(big.Int) + x.Div(e, base) + + // x = floor(10^21 / floor(diff^0.5)) + vm.stack.Push(x) + case oSHA256, oSHA3, oRIPEMD160: + // This is probably save + // ceil(pop / 32) + length := int(math.Ceil(float64(vm.stack.Pop().Uint64()) / 32.0)) + // New buffer which will contain the concatenated popped items + data := new(bytes.Buffer) + for i := 0; i < length; i++ { + // Encode the number to bytes and have it 32bytes long + num := ethutil.NumberToBytes(vm.stack.Pop().Bytes(), 256) + data.WriteString(string(num)) + } + + if op == oSHA256 { + vm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes()))) + } else if op == oSHA3 { + vm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes()))) + } else { + vm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes()))) + } + case oECMUL: + y := vm.stack.Pop() + x := vm.stack.Pop() + //n := vm.stack.Pop() + + //if ethutil.Big(x).Cmp(ethutil.Big(y)) { + data := new(bytes.Buffer) + data.WriteString(x.String()) + data.WriteString(y.String()) + if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 { + // TODO + } else { + // Invalid, push infinity + vm.stack.Push(ethutil.Big("0")) + vm.stack.Push(ethutil.Big("0")) + } + //} else { + // // Invalid, push infinity + // vm.stack.Push("0") + // vm.stack.Push("0") + //} + + case oECADD: + case oECSIGN: + case oECRECOVER: + case oECVALID: + case oPUSH: + pc++ + vm.stack.Push(contract.GetMem(pc).BigInt()) + case oPOP: + // Pop current value of the stack + vm.stack.Pop() + case oDUP: + // Dup top stack + x := vm.stack.Pop() + vm.stack.Push(x) + vm.stack.Push(x) + case oSWAP: + // Swap two top most values + x, y := vm.stack.Popn() + vm.stack.Push(y) + vm.stack.Push(x) + case oMLOAD: + x := vm.stack.Pop() + vm.stack.Push(vm.mem[x.String()]) + case oMSTORE: + x, y := vm.stack.Popn() + vm.mem[x.String()] = y + case oSLOAD: + // Load the value in storage and push it on the stack + x := vm.stack.Pop() + // decode the object as a big integer + decoder := ethutil.NewValueFromBytes([]byte(contract.State().Get(x.String()))) + if !decoder.IsNil() { + vm.stack.Push(decoder.BigInt()) + } else { + vm.stack.Push(ethutil.BigFalse) + } + case oSSTORE: + // Store Y at index X + y, x := vm.stack.Popn() + addr := ethutil.BigToBytes(x, 256) + fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr)) + contract.SetAddr(addr, y) + //contract.State().Update(string(idx), string(y)) + case oJMP: + x := int(vm.stack.Pop().Uint64()) + // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) + pc = x + pc-- + case oJMPI: + x := vm.stack.Pop() + // Set pc to x if it's non zero + if x.Cmp(ethutil.BigFalse) != 0 { + pc = int(x.Uint64()) + pc-- + } + case oIND: + vm.stack.Push(big.NewInt(int64(pc))) + case oEXTRO: + memAddr := vm.stack.Pop() + contractAddr := vm.stack.Pop().Bytes() + + // Push the contract's memory on to the stack + vm.stack.Push(contractMemory(state, contractAddr, memAddr)) + case oBALANCE: + // Pushes the balance of the popped value on to the stack + account := state.GetAccount(vm.stack.Pop().Bytes()) + vm.stack.Push(account.Amount) + case oMKTX: + addr, value := vm.stack.Popn() + from, length := vm.stack.Popn() + + makeInlineTx(addr.Bytes(), value, from, length, contract, state) + case oSUICIDE: + recAddr := vm.stack.Pop().Bytes() + // Purge all memory + deletedMemory := contract.state.NewIterator().Purge() + // Add refunds to the pop'ed address + refund := new(big.Int).Mul(StoreFee, big.NewInt(int64(deletedMemory))) + account := state.GetAccount(recAddr) + account.Amount.Add(account.Amount, refund) + // Update the refunding address + state.UpdateAccount(recAddr, account) + // Delete the contract + state.trie.Update(string(addr), "") + + fmt.Printf("(%d) => %x\n", deletedMemory, recAddr) + break out + default: + fmt.Printf("Invalid OPCODE: %x\n", op) + } + fmt.Println("") + vm.stack.Print() + pc++ + } + + state.UpdateContract(addr, contract) +} + +func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) { + fmt.Printf(" => creating inline tx %x %v %v %v", addr, value, from, length) + j := 0 + dataItems := make([]string, int(length.Uint64())) + for i := from.Uint64(); i < length.Uint64(); i++ { + dataItems[j] = contract.GetMem(j).Str() + j++ + } + + tx := NewTransaction(addr, value, dataItems) + if tx.IsContract() { + contract := MakeContract(tx, state) + state.UpdateContract(tx.Hash()[12:], contract) + } else { + account := state.GetAccount(tx.Recipient) + account.Amount.Add(account.Amount, tx.Value) + state.UpdateAccount(tx.Recipient, account) + } +} + +// Returns an address from the specified contract's address +func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.Int { + contract := state.GetContract(contractAddr) + if contract == nil { + log.Panicf("invalid contract addr %x", contractAddr) + } + val := state.trie.Get(memAddr.String()) + + // decode the object as a big integer + decoder := ethutil.NewValueFromBytes([]byte(val)) + if decoder.IsNil() { + return ethutil.BigFalse + } + + return decoder.BigInt() +} diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go new file mode 100644 index 000000000..6ceb0ff15 --- /dev/null +++ b/ethchain/vm_test.go @@ -0,0 +1,106 @@ +package ethchain + +import ( + "fmt" + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethutil" + "math/big" + "testing" +) + +func TestRun(t *testing.T) { + InitFees() + + ethutil.ReadConfig("") + + db, _ := ethdb.NewMemDatabase() + state := NewState(ethutil.NewTrie(db, "")) + + script := Compile([]string{ + "TXSENDER", + "SUICIDE", + }) + + tx := NewTransaction(ContractAddr, big.NewInt(1e17), script) + fmt.Printf("contract addr %x\n", tx.Hash()[12:]) + contract := MakeContract(tx, state) + vm := &Vm{} + + vm.Process(contract, state, RuntimeVars{ + address: tx.Hash()[12:], + blockNumber: 1, + sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), + prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), + coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + time: 1, + diff: big.NewInt(256), + txValue: tx.Value, + txData: tx.Data, + }) +} + +func TestRun1(t *testing.T) { + ethutil.ReadConfig("") + + db, _ := ethdb.NewMemDatabase() + state := NewState(ethutil.NewTrie(db, "")) + + script := Compile([]string{ + "PUSH", "0", + "PUSH", "0", + "TXSENDER", + "PUSH", "10000000", + "MKTX", + }) + fmt.Println(ethutil.NewValue(script)) + + tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) + fmt.Printf("contract addr %x\n", tx.Hash()[12:]) + contract := MakeContract(tx, state) + vm := &Vm{} + + vm.Process(contract, state, RuntimeVars{ + address: tx.Hash()[12:], + blockNumber: 1, + sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), + prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), + coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + time: 1, + diff: big.NewInt(256), + txValue: tx.Value, + txData: tx.Data, + }) +} + +func TestRun2(t *testing.T) { + ethutil.ReadConfig("") + + db, _ := ethdb.NewMemDatabase() + state := NewState(ethutil.NewTrie(db, "")) + + script := Compile([]string{ + "PUSH", "0", + "PUSH", "0", + "TXSENDER", + "PUSH", "10000000", + "MKTX", + }) + fmt.Println(ethutil.NewValue(script)) + + tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) + fmt.Printf("contract addr %x\n", tx.Hash()[12:]) + contract := MakeContract(tx, state) + vm := &Vm{} + + vm.Process(contract, state, RuntimeVars{ + address: tx.Hash()[12:], + blockNumber: 1, + sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), + prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), + coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + time: 1, + diff: big.NewInt(256), + txValue: tx.Value, + txData: tx.Data, + }) +} From 1a98bbf1c858ed4bafcc7ffa146a30e40ef919b0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:11:00 +0100 Subject: [PATCH 53/78] Added a trie iterator --- ethutil/trie.go | 85 ++++++++++++++++++++++++++++++++++++++++++++ ethutil/trie_test.go | 24 +++++++++++++ 2 files changed, 109 insertions(+) diff --git a/ethutil/trie.go b/ethutil/trie.go index 322f77647..83527d364 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -70,6 +70,12 @@ func (cache *Cache) Get(key []byte) *Value { return value } +func (cache *Cache) Delete(key []byte) { + delete(cache.nodes, string(key)) + + cache.db.Delete(key) +} + func (cache *Cache) Commit() { // Don't try to commit if it isn't dirty if !cache.IsDirty { @@ -413,3 +419,82 @@ func (t *Trie) Copy() *Trie { return trie } + +type TrieIterator struct { + trie *Trie + key string + value string + + shas [][]byte + values []string +} + +func (t *Trie) NewIterator() *TrieIterator { + return &TrieIterator{trie: t} +} + +// Some time in the near future this will need refactoring :-) +// XXX Note to self, IsSlice == inline node. Str == sha3 to node +func (it *TrieIterator) workNode(currentNode *Value) { + if currentNode.Len() == 2 { + k := CompactDecode(currentNode.Get(0).Str()) + + if currentNode.Get(1).IsSlice() { + it.workNode(currentNode.Get(1)) + } else { + if k[len(k)-1] == 16 { + it.values = append(it.values, currentNode.Get(1).Str()) + } else { + it.shas = append(it.shas, currentNode.Get(1).Bytes()) + it.getNode(currentNode.Get(1).Bytes()) + } + } + } else { + for i := 0; i < currentNode.Len(); i++ { + if i == 16 && currentNode.Get(i).Len() != 0 { + it.values = append(it.values, currentNode.Get(i).Str()) + } else { + if currentNode.Get(i).IsSlice() { + it.workNode(currentNode.Get(i)) + } else { + val := currentNode.Get(i).Str() + if val != "" { + it.shas = append(it.shas, currentNode.Get(1).Bytes()) + it.getNode([]byte(val)) + } + } + } + } + } +} + +func (it *TrieIterator) getNode(node []byte) { + currentNode := it.trie.cache.Get(node) + it.workNode(currentNode) +} + +func (it *TrieIterator) Collect() [][]byte { + if it.trie.Root == "" { + return nil + } + + it.getNode(NewValue(it.trie.Root).Bytes()) + + return it.shas +} + +func (it *TrieIterator) Purge() int { + shas := it.Collect() + for _, sha := range shas { + it.trie.cache.Delete(sha) + } + return len(it.values) +} + +func (it *TrieIterator) Key() string { + return "" +} + +func (it *TrieIterator) Value() string { + return "" +} diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index 9d2c8e19f..c3a8f224d 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,6 +1,7 @@ package ethutil import ( + "fmt" "reflect" "testing" ) @@ -21,6 +22,10 @@ func (db *MemDatabase) Put(key []byte, value []byte) { func (db *MemDatabase) Get(key []byte) ([]byte, error) { return db.db[string(key)], nil } +func (db *MemDatabase) Delete(key []byte) error { + delete(db.db, string(key)) + return nil +} func (db *MemDatabase) Print() {} func (db *MemDatabase) Close() {} func (db *MemDatabase) LastKnownTD() []byte { return nil } @@ -148,3 +153,22 @@ func TestTrieDeleteWithValue(t *testing.T) { } } + +func TestTrieIterator(t *testing.T) { + _, trie := New() + trie.Update("c", LONG_WORD) + trie.Update("ca", LONG_WORD) + trie.Update("cat", LONG_WORD) + + it := trie.NewIterator() + fmt.Println("purging") + fmt.Println("len =", it.Purge()) + /* + for it.Next() { + k := it.Key() + v := it.Value() + + fmt.Println(k, v) + } + */ +} From 5971c82094b79e7c05bfb652c46fac02381b2816 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:11:42 +0100 Subject: [PATCH 54/78] Updated opcodes ... again --- ethutil/parsing.go | 102 ++++++++++++++++++++-------------------- ethutil/parsing_test.go | 4 +- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/ethutil/parsing.go b/ethutil/parsing.go index b43dac064..553bb9717 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -7,57 +7,57 @@ import ( // Op codes var OpCodes = map[string]byte{ - "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, - "TXVALUE": 18, - "TXFEE": 19, - "TXDATAN": 20, - "TXDATA": 21, - "BLK_PREVHASH": 22, - "BLK_COINBASE": 23, - "BLK_TIMESTAMP": 24, - "BLK_NUMBER": 25, - "BLK_DIFFICULTY": 26, - "BASEFEE": 27, - "SHA256": 32, - "RIPEMD160": 33, - "ECMUL": 34, - "ECADD": 35, - "ECSIGN": 36, - "ECRECOVER": 37, - "ECVALID": 38, - "SHA3": 39, - "PUSH": 48, - "POP": 49, - "DUP": 50, - "SWAP": 51, - "MLOAD": 52, - "MSTORE": 53, - "SLOAD": 54, - "SSTORE": 55, - "JMP": 56, - "JMPI": 57, - "IND": 58, - "EXTRO": 59, - "BALANCE": 60, - "MKTX": 61, - "SUICIDE": 62, + "STOP": 0x00, + "ADD": 0x01, + "MUL": 0x02, + "SUB": 0x03, + "DIV": 0x04, + "SDIV": 0x05, + "MOD": 0x06, + "SMOD": 0x07, + "EXP": 0x08, + "NEG": 0x09, + "LT": 0x0a, + "LE": 0x0b, + "GT": 0x0c, + "GE": 0x0d, + "EQ": 0x0e, + "NOT": 0x0f, + "MYADDRESS": 0x10, + "TXSENDER": 0x11, + "TXVALUE": 0x12, + "TXDATAN": 0x13, + "TXDATA": 0x14, + "BLK_PREVHASH": 0x15, + "BLK_COINBASE": 0x16, + "BLK_TIMESTAMP": 0x17, + "BLK_NUMBER": 0x18, + "BLK_DIFFICULTY": 0x19, + "BLK_NONCE": 0x1a, + "BASEFEE": 0x1b, + "SHA256": 0x20, + "RIPEMD160": 0x21, + "ECMUL": 0x22, + "ECADD": 0x23, + "ECSIGN": 0x24, + "ECRECOVER": 0x25, + "ECVALID": 0x26, + "SHA3": 0x27, + "PUSH": 0x30, + "POP": 0x31, + "DUP": 0x32, + "SWAP": 0x33, + "MLOAD": 0x34, + "MSTORE": 0x35, + "SLOAD": 0x36, + "SSTORE": 0x37, + "JMP": 0x38, + "JMPI": 0x39, + "IND": 0x3a, + "EXTRO": 0x3b, + "BALANCE": 0x3c, + "MKTX": 0x3d, + "SUICIDE": 0x3f, } func IsOpCode(s string) bool { diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go index 482eef3ee..c40649187 100644 --- a/ethutil/parsing_test.go +++ b/ethutil/parsing_test.go @@ -1,5 +1,6 @@ package ethutil +/* import ( "math" "testing" @@ -19,14 +20,13 @@ func TestCompile(t *testing.T) { } func TestValidInstr(t *testing.T) { - /* op, args, err := Instr("68163") if err != nil { t.Error("Error decoding instruction") } - */ } func TestInvalidInstr(t *testing.T) { } +*/ From 55c1c220d07c37610c9aa61c583eccb3c8e3829e Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:12:01 +0100 Subject: [PATCH 55/78] Added delete to database interfaces --- ethdb/database.go | 4 ++++ ethdb/memory_database.go | 6 ++++++ ethutil/db.go | 1 + 3 files changed, 11 insertions(+) diff --git a/ethdb/database.go b/ethdb/database.go index 76e4b4e4d..1e987920d 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -36,6 +36,10 @@ func (db *LDBDatabase) Get(key []byte) ([]byte, error) { return db.db.Get(key, nil) } +func (db *LDBDatabase) Delete(key []byte) error { + return db.db.Delete(key, nil) +} + func (db *LDBDatabase) LastKnownTD() []byte { data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil) diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go index cd9f24000..9e91eb7d6 100644 --- a/ethdb/memory_database.go +++ b/ethdb/memory_database.go @@ -26,6 +26,12 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) { return db.db[string(key)], nil } +func (db *MemDatabase) Delete(key []byte) error { + delete(db.db, string(key)) + + return nil +} + func (db *MemDatabase) Print() { for key, val := range db.db { fmt.Printf("%x(%d): ", key, len(key)) diff --git a/ethutil/db.go b/ethutil/db.go index 3681c4b05..b11d5d726 100644 --- a/ethutil/db.go +++ b/ethutil/db.go @@ -4,6 +4,7 @@ package ethutil type Database interface { Put(key []byte, value []byte) Get(key []byte) ([]byte, error) + Delete(key []byte) error LastKnownTD() []byte Close() Print() From a3fb7008b2df860b01df71ef7da42b394570d1e2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:12:24 +0100 Subject: [PATCH 56/78] Added make contract --- ethchain/contract.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ethchain/contract.go b/ethchain/contract.go index 68ec39f0b..dbcbb3697 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -41,3 +41,31 @@ func (c *Contract) SetAddr(addr []byte, value interface{}) { func (c *Contract) State() *ethutil.Trie { return c.state } + +func (c *Contract) GetMem(num int) *ethutil.Value { + nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256) + + return c.Addr(nb) +} + +func MakeContract(tx *Transaction, state *State) *Contract { + // Create contract if there's no recipient + if tx.IsContract() { + addr := tx.Hash()[12:] + + value := tx.Value + contract := NewContract(value, []byte("")) + state.trie.Update(string(addr), string(contract.RlpEncode())) + for i, val := range tx.Data { + if len(val) > 0 { + bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) + contract.state.Update(string(bytNum), string(ethutil.Encode(val))) + } + } + state.trie.Update(string(addr), string(contract.RlpEncode())) + + return contract + } + + return nil +} From 4cc5b03137e513dd54e9feb07a564398ca53b342 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:12:32 +0100 Subject: [PATCH 57/78] Added opcodes --- ethchain/stack.go | 103 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index e08f84082..13b0f247b 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -9,57 +9,57 @@ type OpCode int // Op codes const ( - oSTOP OpCode = iota - oADD - oMUL - oSUB - oDIV - oSDIV - oMOD - oSMOD - oEXP - oNEG - oLT - oLE - oGT - oGE - oEQ - oNOT - oMYADDRESS - oTXSENDER - oTXVALUE - oTXFEE - oTXDATAN - oTXDATA - oBLK_PREVHASH - oBLK_COINBASE - oBLK_TIMESTAMP - oBLK_NUMBER - oBLK_DIFFICULTY - oBASEFEE - oSHA256 OpCode = 32 - oRIPEMD160 OpCode = 33 - oECMUL OpCode = 34 - oECADD OpCode = 35 - oECSIGN OpCode = 36 - oECRECOVER OpCode = 37 - oECVALID OpCode = 38 - oSHA3 OpCode = 39 - oPUSH OpCode = 48 - oPOP OpCode = 49 - oDUP OpCode = 50 - oSWAP OpCode = 51 - oMLOAD OpCode = 52 - oMSTORE OpCode = 53 - oSLOAD OpCode = 54 - oSSTORE OpCode = 55 - oJMP OpCode = 56 - oJMPI OpCode = 57 - oIND OpCode = 58 - oEXTRO OpCode = 59 - oBALANCE OpCode = 60 - oMKTX OpCode = 61 - oSUICIDE OpCode = 62 + oSTOP = 0x00 + oADD = 0x01 + oMUL = 0x02 + oSUB = 0x03 + oDIV = 0x04 + oSDIV = 0x05 + oMOD = 0x06 + oSMOD = 0x07 + oEXP = 0x08 + oNEG = 0x09 + oLT = 0x0a + oLE = 0x0b + oGT = 0x0c + oGE = 0x0d + oEQ = 0x0e + oNOT = 0x0f + oMYADDRESS = 0x10 + oTXSENDER = 0x11 + oTXVALUE = 0x12 + oTXDATAN = 0x13 + oTXDATA = 0x14 + oBLK_PREVHASH = 0x15 + oBLK_COINBASE = 0x16 + oBLK_TIMESTAMP = 0x17 + oBLK_NUMBER = 0x18 + oBLK_DIFFICULTY = 0x19 + oBLK_NONCE = 0x1a + oBASEFEE = 0x1b + oSHA256 = 0x20 + oRIPEMD160 = 0x21 + oECMUL = 0x22 + oECADD = 0x23 + oECSIGN = 0x24 + oECRECOVER = 0x25 + oECVALID = 0x26 + oSHA3 = 0x27 + oPUSH = 0x30 + oPOP = 0x31 + oDUP = 0x32 + oSWAP = 0x33 + oMLOAD = 0x34 + oMSTORE = 0x35 + oSLOAD = 0x36 + oSSTORE = 0x37 + oJMP = 0x38 + oJMPI = 0x39 + oIND = 0x3a + oEXTRO = 0x3b + oBALANCE = 0x3c + oMKTX = 0x3d + oSUICIDE = 0x3f ) // Since the opcodes aren't all in order we can't use a regular slice @@ -83,7 +83,6 @@ var opCodeToString = map[OpCode]string{ oMYADDRESS: "MYADDRESS", oTXSENDER: "TXSENDER", oTXVALUE: "TXVALUE", - oTXFEE: "TXFEE", oTXDATAN: "TXDATAN", oTXDATA: "TXDATA", oBLK_PREVHASH: "BLK_PREVHASH", From 22e2c3429bee515f69250dfdddc419a389e8ccd6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:12:53 +0100 Subject: [PATCH 58/78] Infof rather than infoln --- peer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peer.go b/peer.go index 9538e6500..a8708206f 100644 --- a/peer.go +++ b/peer.go @@ -390,7 +390,7 @@ func (p *Peer) HandleInbound() { p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()})) } case ethwire.MsgNotInChainTy: - ethutil.Config.Log.Infoln("Not in chain %x\n", msg.Data) + ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data) // TODO // Unofficial but fun nonetheless @@ -556,7 +556,7 @@ func (p *Peer) CatchupWithPeer() { msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)}) p.QueueMessage(msg) - ethutil.Config.Log.Debugln("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4]) + ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4]) } } From d8b5bbd48c8e989808058150137d9713d1936b18 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:13:22 +0100 Subject: [PATCH 59/78] Added compile --- ethchain/block_manager_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go index 853d459d8..df3e56e5e 100644 --- a/ethchain/block_manager_test.go +++ b/ethchain/block_manager_test.go @@ -17,12 +17,13 @@ func TestVm(t *testing.T) { bm := NewBlockManager(nil) block := bm.bc.genesisBlock - ctrct := NewTransaction(ContractAddr, big.NewInt(200000000), []string{ + script := Compile([]string{ "PUSH", "1", "PUSH", "2", "STOP", }) + ctrct := NewTransaction(ContractAddr, big.NewInt(200000000), script) bm.ApplyTransactions(block, []*Transaction{ctrct}) } From d69db6e617478911e90f6627329012578bfd0fe5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:13:42 +0100 Subject: [PATCH 60/78] Removed compiling from the transaction --- ethchain/transaction.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 2417bbd7d..39b2bda94 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -24,12 +24,7 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { tx.Nonce = 0 // Serialize the data - tx.Data = make([]string, len(data)) - for i, val := range data { - instr, _ := ethutil.CompileInstr(val) - - tx.Data[i] = string(instr) - } + tx.Data = data return &tx } From b29c1eecd104de58611b9a17a68b48321fd40e87 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:44:18 +0100 Subject: [PATCH 61/78] Removed debug print --- ethchain/vm.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index d5f4d7ad6..9abd9cd41 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -91,7 +91,6 @@ out: tf := new(big.Int).Add(fee, fee2) if contract.Amount.Cmp(tf) < 0 { - fmt.Println("Contract fee", ContractFee) fmt.Println("Insufficient fees to continue running the contract", tf, contract.Amount) break } @@ -392,7 +391,7 @@ out: fmt.Printf("Invalid OPCODE: %x\n", op) } fmt.Println("") - vm.stack.Print() + //vm.stack.Print() pc++ } From 88a9c62fccd16a782e7d7221daf6b6f207c22097 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:44:29 +0100 Subject: [PATCH 62/78] Proper tests --- ethchain/block_manager.go | 420 ++------------------------------- ethchain/block_manager_test.go | 10 +- 2 files changed, 24 insertions(+), 406 deletions(-) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index b82e5a74a..ad96f6a34 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -6,11 +6,8 @@ import ( "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/ethereum/eth-go/ethwire" - "github.com/obscuren/secp256k1-go" "log" - "math" "math/big" - "strconv" "sync" "time" ) @@ -113,9 +110,12 @@ func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) { // If there's no recipient, it's a contract if tx.IsContract() { block.MakeContract(tx) - bm.ProcessContract(tx, block) } else { - bm.TransactionPool.ProcessTransaction(tx, block) + if contract := block.GetContract(tx.Recipient); contract != nil { + bm.ProcessContract(contract, tx, block) + } else { + bm.TransactionPool.ProcessTransaction(tx, block) + } } } } @@ -300,7 +300,7 @@ func (bm *BlockManager) Stop() { bm.bc.Stop() } -func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) { +func (bm *BlockManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) { // Recovering function in case the VM had any errors /* defer func() { @@ -310,402 +310,16 @@ func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) { }() */ - // Process contract - bm.ProcContract(tx, block, func(opType OpType) bool { - // TODO turn on once big ints are in place - //if !block.PayFee(tx.Hash(), StepFee.Uint64()) { - // return false - //} - - return true // Continue + vm := &Vm{} + vm.Process(contract, NewState(block.state), RuntimeVars{ + address: tx.Hash()[12:], + blockNumber: block.BlockInfo().Number, + sender: tx.Sender(), + prevHash: block.PrevHash, + coinbase: block.Coinbase, + time: block.Time, + diff: block.Difficulty, + txValue: tx.Value, + txData: tx.Data, }) } - -// Contract evaluation is done here. -func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) { - addr := tx.Hash()[12:] - // Instruction pointer - pc := 0 - blockInfo := bm.bc.BlockInfo(block) - - contract := block.GetContract(addr) - - if contract == nil { - fmt.Println("Contract not found") - return - } - - Pow256 := ethutil.BigPow(2, 256) - - if ethutil.Config.Debug { - fmt.Printf("# op\n") - } - - stepcount := 0 - totalFee := new(big.Int) - - // helper function for getting a contract's memory address - getMem := func(num int) *ethutil.Value { - nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256) - return contract.Addr(nb) - } -out: - for { - stepcount++ - // The base big int for all calculations. Use this for any results. - base := new(big.Int) - val := getMem(pc) - //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb) - op := OpCode(val.Uint()) - - var fee *big.Int = new(big.Int) - var fee2 *big.Int = new(big.Int) - if stepcount > 16 { - fee.Add(fee, StepFee) - } - - // Calculate the fees - switch op { - case oSSTORE: - y, x := bm.stack.Peekn() - val := contract.Addr(ethutil.BigToBytes(x, 256)) - if val.IsEmpty() && len(y.Bytes()) > 0 { - fee2.Add(DataFee, StoreFee) - } else { - fee2.Sub(DataFee, StoreFee) - } - case oSLOAD: - fee.Add(fee, StoreFee) - case oEXTRO, oBALANCE: - fee.Add(fee, ExtroFee) - case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID: - fee.Add(fee, CryptoFee) - case oMKTX: - fee.Add(fee, ContractFee) - } - - tf := new(big.Int).Add(fee, fee2) - if contract.Amount.Cmp(tf) < 0 { - break - } - // Add the fee to the total fee. It's subtracted when we're done looping - totalFee.Add(totalFee, tf) - - if !cb(0) { - break - } - - if ethutil.Config.Debug { - fmt.Printf("%-3d %-4s", pc, op.String()) - } - - switch op { - case oSTOP: - fmt.Println("") - break out - case oADD: - x, y := bm.stack.Popn() - // (x + y) % 2 ** 256 - base.Add(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - bm.stack.Push(base) - case oSUB: - x, y := bm.stack.Popn() - // (x - y) % 2 ** 256 - base.Sub(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - bm.stack.Push(base) - case oMUL: - x, y := bm.stack.Popn() - // (x * y) % 2 ** 256 - base.Mul(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - bm.stack.Push(base) - case oDIV: - x, y := bm.stack.Popn() - // floor(x / y) - base.Div(x, y) - // Pop result back on the stack - bm.stack.Push(base) - case oSDIV: - x, y := bm.stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Div(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) - } - // Push result on to the stack - bm.stack.Push(z) - case oMOD: - x, y := bm.stack.Popn() - base.Mod(x, y) - bm.stack.Push(base) - case oSMOD: - x, y := bm.stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Mod(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) - } - // Push result on to the stack - bm.stack.Push(z) - case oEXP: - x, y := bm.stack.Popn() - base.Exp(x, y, Pow256) - - bm.stack.Push(base) - case oNEG: - base.Sub(Pow256, bm.stack.Pop()) - bm.stack.Push(base) - case oLT: - x, y := bm.stack.Popn() - // x < y - if x.Cmp(y) < 0 { - bm.stack.Push(ethutil.BigTrue) - } else { - bm.stack.Push(ethutil.BigFalse) - } - case oLE: - x, y := bm.stack.Popn() - // x <= y - if x.Cmp(y) < 1 { - bm.stack.Push(ethutil.BigTrue) - } else { - bm.stack.Push(ethutil.BigFalse) - } - case oGT: - x, y := bm.stack.Popn() - // x > y - if x.Cmp(y) > 0 { - bm.stack.Push(ethutil.BigTrue) - } else { - bm.stack.Push(ethutil.BigFalse) - } - case oGE: - x, y := bm.stack.Popn() - // x >= y - if x.Cmp(y) > -1 { - bm.stack.Push(ethutil.BigTrue) - } else { - bm.stack.Push(ethutil.BigFalse) - } - case oNOT: - x, y := bm.stack.Popn() - // x != y - if x.Cmp(y) != 0 { - bm.stack.Push(ethutil.BigTrue) - } else { - bm.stack.Push(ethutil.BigFalse) - } - case oMYADDRESS: - bm.stack.Push(ethutil.BigD(tx.Hash())) - case oTXSENDER: - bm.stack.Push(ethutil.BigD(tx.Sender())) - case oTXVALUE: - bm.stack.Push(tx.Value) - case oTXDATAN: - bm.stack.Push(big.NewInt(int64(len(tx.Data)))) - case oTXDATA: - v := bm.stack.Pop() - // v >= len(data) - if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 { - bm.stack.Push(ethutil.Big("0")) - } else { - bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()])) - } - case oBLK_PREVHASH: - bm.stack.Push(ethutil.BigD(block.PrevHash)) - case oBLK_COINBASE: - bm.stack.Push(ethutil.BigD(block.Coinbase)) - case oBLK_TIMESTAMP: - bm.stack.Push(big.NewInt(block.Time)) - case oBLK_NUMBER: - bm.stack.Push(big.NewInt(int64(blockInfo.Number))) - case oBLK_DIFFICULTY: - bm.stack.Push(block.Difficulty) - case oBASEFEE: - // e = 10^21 - e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0)) - d := new(big.Rat) - d.SetInt(block.Difficulty) - c := new(big.Rat) - c.SetFloat64(0.5) - // d = diff / 0.5 - d.Quo(d, c) - // base = floor(d) - base.Div(d.Num(), d.Denom()) - - x := new(big.Int) - x.Div(e, base) - - // x = floor(10^21 / floor(diff^0.5)) - bm.stack.Push(x) - case oSHA256, oSHA3, oRIPEMD160: - // This is probably save - // ceil(pop / 32) - length := int(math.Ceil(float64(bm.stack.Pop().Uint64()) / 32.0)) - // New buffer which will contain the concatenated popped items - data := new(bytes.Buffer) - for i := 0; i < length; i++ { - // Encode the number to bytes and have it 32bytes long - num := ethutil.NumberToBytes(bm.stack.Pop().Bytes(), 256) - data.WriteString(string(num)) - } - - if op == oSHA256 { - bm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes()))) - } else if op == oSHA3 { - bm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes()))) - } else { - bm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes()))) - } - case oECMUL: - y := bm.stack.Pop() - x := bm.stack.Pop() - //n := bm.stack.Pop() - - //if ethutil.Big(x).Cmp(ethutil.Big(y)) { - data := new(bytes.Buffer) - data.WriteString(x.String()) - data.WriteString(y.String()) - if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 { - // TODO - } else { - // Invalid, push infinity - bm.stack.Push(ethutil.Big("0")) - bm.stack.Push(ethutil.Big("0")) - } - //} else { - // // Invalid, push infinity - // bm.stack.Push("0") - // bm.stack.Push("0") - //} - - case oECADD: - case oECSIGN: - case oECRECOVER: - case oECVALID: - case oPUSH: - pc++ - bm.stack.Push(getMem(pc).BigInt()) - case oPOP: - // Pop current value of the stack - bm.stack.Pop() - case oDUP: - // Dup top stack - x := bm.stack.Pop() - bm.stack.Push(x) - bm.stack.Push(x) - case oSWAP: - // Swap two top most values - x, y := bm.stack.Popn() - bm.stack.Push(y) - bm.stack.Push(x) - case oMLOAD: - x := bm.stack.Pop() - bm.stack.Push(bm.mem[x.String()]) - case oMSTORE: - x, y := bm.stack.Popn() - bm.mem[x.String()] = y - case oSLOAD: - // Load the value in storage and push it on the stack - x := bm.stack.Pop() - // decode the object as a big integer - decoder := ethutil.NewValueFromBytes([]byte(contract.State().Get(x.String()))) - if !decoder.IsNil() { - bm.stack.Push(decoder.BigInt()) - } else { - bm.stack.Push(ethutil.BigFalse) - } - case oSSTORE: - // Store Y at index X - y, x := bm.stack.Popn() - addr := ethutil.BigToBytes(x, 256) - fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr)) - contract.SetAddr(addr, y) - //contract.State().Update(string(idx), string(y)) - case oJMP: - x := int(bm.stack.Pop().Uint64()) - // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) - pc = x - pc-- - case oJMPI: - x := bm.stack.Pop() - // Set pc to x if it's non zero - if x.Cmp(ethutil.BigFalse) != 0 { - pc = int(x.Uint64()) - pc-- - } - case oIND: - bm.stack.Push(big.NewInt(int64(pc))) - case oEXTRO: - memAddr := bm.stack.Pop() - contractAddr := bm.stack.Pop().Bytes() - - // Push the contract's memory on to the stack - bm.stack.Push(getContractMemory(block, contractAddr, memAddr)) - case oBALANCE: - // Pushes the balance of the popped value on to the stack - d := block.State().Get(bm.stack.Pop().String()) - ether := NewAddressFromData([]byte(d)) - bm.stack.Push(ether.Amount) - case oMKTX: - value, addr := bm.stack.Popn() - from, length := bm.stack.Popn() - - j := 0 - dataItems := make([]string, int(length.Uint64())) - for i := from.Uint64(); i < length.Uint64(); i++ { - dataItems[j] = string(bm.mem[strconv.Itoa(int(i))].Bytes()) - j++ - } - // TODO sign it? - tx := NewTransaction(addr.Bytes(), value, dataItems) - // Add the transaction to the tx pool - bm.TransactionPool.QueueTransaction(tx) - case oSUICIDE: - //addr := bm.stack.Pop() - default: - fmt.Println("Invalid OPCODE", op) - } - fmt.Println("") - bm.stack.Print() - pc++ - } - - block.UpdateContract(addr, contract) -} - -// Returns an address from the specified contract's address -func getContractMemory(block *Block, contractAddr []byte, memAddr *big.Int) *big.Int { - contract := block.GetContract(contractAddr) - if contract == nil { - log.Panicf("invalid contract addr %x", contractAddr) - } - val := contract.State().Get(memAddr.String()) - - // decode the object as a big integer - decoder := ethutil.NewValueFromBytes([]byte(val)) - if decoder.IsNil() { - return ethutil.BigFalse - } - - return decoder.BigInt() -} diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go index df3e56e5e..ec4fbe8c5 100644 --- a/ethchain/block_manager_test.go +++ b/ethchain/block_manager_test.go @@ -22,8 +22,12 @@ func TestVm(t *testing.T) { "1", "PUSH", "2", - "STOP", }) - ctrct := NewTransaction(ContractAddr, big.NewInt(200000000), script) - bm.ApplyTransactions(block, []*Transaction{ctrct}) + tx := NewTransaction(ContractAddr, big.NewInt(200000000), script) + addr := tx.Hash()[12:] + bm.ApplyTransactions(block, []*Transaction{tx}) + + tx2 := NewTransaction(addr, big.NewInt(1e17), nil) + tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + bm.ApplyTransactions(block, []*Transaction{tx2}) } From 95a8ebc2495476c5dbe6c35f30629ca11f660b86 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 12:45:21 +0100 Subject: [PATCH 63/78] Changed MakeContract on block MakeContract in Block is now using the MakeContract defined in contract.go --- ethchain/block.go | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index 0b4f93e8c..7ad9984be 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -220,23 +220,9 @@ func (block *Block) Undo() { } func (block *Block) MakeContract(tx *Transaction) { - // Create contract if there's no recipient - if tx.IsContract() { - addr := tx.Hash()[12:] + contract := MakeContract(tx, NewState(block.state)) - value := tx.Value - contract := NewContract(value, []byte("")) - block.state.Update(string(addr), string(contract.RlpEncode())) - for i, val := range tx.Data { - if len(val) > 0 { - bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) - contract.state.Update(string(bytNum), val) - } - } - block.UpdateContract(addr, contract) - - block.contractStates[string(addr)] = contract.state - } + block.contractStates[string(tx.Hash()[12:])] = contract.state } /////// Block Encoding From 6c7255cde2afdff7710af23c9830835398e6b43d Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 13:15:34 +0100 Subject: [PATCH 64/78] Fixed a minor issue on crashed contracts --- ethchain/block.go | 5 +++-- ethchain/contract.go | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index 7ad9984be..472ae134a 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -221,8 +221,9 @@ func (block *Block) Undo() { func (block *Block) MakeContract(tx *Transaction) { contract := MakeContract(tx, NewState(block.state)) - - block.contractStates[string(tx.Hash()[12:])] = contract.state + if contract != nil { + block.contractStates[string(tx.Hash()[12:])] = contract.state + } } /////// Block Encoding diff --git a/ethchain/contract.go b/ethchain/contract.go index dbcbb3697..14abb05d1 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -1,6 +1,7 @@ package ethchain import ( + "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" ) @@ -65,6 +66,8 @@ func MakeContract(tx *Transaction, state *State) *Contract { state.trie.Update(string(addr), string(contract.RlpEncode())) return contract + } else { + fmt.Println("NO CONTRACT") } return nil From 0afdedb01a8e4203129175dc9dcc213a55906a66 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Feb 2014 13:50:52 +0100 Subject: [PATCH 65/78] Some log statements changed to the default logger --- ethchain/block_manager.go | 25 ++++--------------------- ethchain/vm.go | 10 +++++----- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index ad96f6a34..04036dbb1 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -165,37 +165,20 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { if bm.CalculateTD(block) { // Sync the current block's state to the database and cancelling out the deferred Undo bm.bc.CurrentBlock.Sync() - // Add the block to the chain - bm.bc.Add(block) - - /* - ethutil.Config.Db.Put(block.Hash(), block.RlpEncode()) - bm.bc.CurrentBlock = block - bm.LastBlockHash = block.Hash() - bm.writeBlockInfo(block) - */ - - /* - txs := bm.TransactionPool.Flush() - var coded = []interface{}{} - for _, tx := range txs { - err := bm.TransactionPool.ValidateTransaction(tx) - if err == nil { - coded = append(coded, tx.RlpEncode()) - } - } - */ // Broadcast the valid block back to the wire //bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val}) + // Add the block to the chain + bm.bc.Add(block) + // If there's a block processor present, pass in the block for further // processing if bm.SecondaryBlockProcessor != nil { bm.SecondaryBlockProcessor.ProcessBlock(block) } - log.Printf("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash()) + ethutil.Config.Log.Infof("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash()) } else { fmt.Println("total diff failed") } diff --git a/ethchain/vm.go b/ethchain/vm.go index 9abd9cd41..c7a91a9c5 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -48,7 +48,7 @@ func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) { Pow256 := ethutil.BigPow(2, 256) if ethutil.Config.Debug { - fmt.Printf("# op\n") + ethutil.Config.Log.Debugf("# op\n") } stepcount := 0 @@ -98,7 +98,7 @@ out: totalFee.Add(totalFee, tf) if ethutil.Config.Debug { - fmt.Printf("%-3d %-4s", pc, op.String()) + ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) } switch op { @@ -385,12 +385,12 @@ out: // Delete the contract state.trie.Update(string(addr), "") - fmt.Printf("(%d) => %x\n", deletedMemory, recAddr) + ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr) break out default: fmt.Printf("Invalid OPCODE: %x\n", op) } - fmt.Println("") + ethutil.Config.Log.Debugln("") //vm.stack.Print() pc++ } @@ -399,7 +399,7 @@ out: } func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) { - fmt.Printf(" => creating inline tx %x %v %v %v", addr, value, from, length) + ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length) j := 0 dataItems := make([]string, int(length.Uint64())) for i := from.Uint64(); i < length.Uint64(); i++ { From b30b9ab8cb13ddbc68a4912c9f06116c0f59bc27 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 10:50:53 +0100 Subject: [PATCH 66/78] Fixed a minor issue where a string is expected but returns slice --- ethutil/trie.go | 4 ++-- ethutil/trie_test.go | 17 +++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/ethutil/trie.go b/ethutil/trie.go index 83527d364..a17dc37ad 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -439,7 +439,7 @@ func (it *TrieIterator) workNode(currentNode *Value) { if currentNode.Len() == 2 { k := CompactDecode(currentNode.Get(0).Str()) - if currentNode.Get(1).IsSlice() { + if currentNode.Get(1).Str() == "" { it.workNode(currentNode.Get(1)) } else { if k[len(k)-1] == 16 { @@ -454,7 +454,7 @@ func (it *TrieIterator) workNode(currentNode *Value) { if i == 16 && currentNode.Get(i).Len() != 0 { it.values = append(it.values, currentNode.Get(i).Str()) } else { - if currentNode.Get(i).IsSlice() { + if currentNode.Get(i).Str() == "" { it.workNode(currentNode.Get(i)) } else { val := currentNode.Get(i).Str() diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index c3a8f224d..645c5a225 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,7 +1,6 @@ package ethutil import ( - "fmt" "reflect" "testing" ) @@ -160,15 +159,13 @@ func TestTrieIterator(t *testing.T) { trie.Update("ca", LONG_WORD) trie.Update("cat", LONG_WORD) + lenBefore := len(trie.cache.nodes) it := trie.NewIterator() - fmt.Println("purging") - fmt.Println("len =", it.Purge()) - /* - for it.Next() { - k := it.Key() - v := it.Value() + if num := it.Purge(); num != 3 { + t.Errorf("Expected purge to return 3, got %d", num) + } - fmt.Println(k, v) - } - */ + if lenBefore == len(trie.cache.nodes) { + t.Errorf("Expected cached nodes to be deleted") + } } From c7e73ba12d747186002433db54d002ab43bed171 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 11:20:24 +0100 Subject: [PATCH 67/78] Added currency converting --- ethutil/common.go | 35 +++++++++++++++++++++++++++++++++++ ethutil/common_test.go | 17 +++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 ethutil/common.go create mode 100644 ethutil/common_test.go diff --git a/ethutil/common.go b/ethutil/common.go new file mode 100644 index 000000000..07df6bb13 --- /dev/null +++ b/ethutil/common.go @@ -0,0 +1,35 @@ +package ethutil + +import ( + "fmt" + "math/big" +) + +var ( + Ether = BigPow(10, 18) + Finney = BigPow(10, 15) + Szabo = BigPow(10, 12) + Vito = BigPow(10, 9) + Turing = BigPow(10, 6) + Eins = BigPow(10, 3) + Wei = big.NewInt(1) +) + +func CurrencyToString(num *big.Int) string { + switch { + case num.Cmp(Ether) >= 0: + return fmt.Sprintf("%v Ether", new(big.Int).Div(num, Ether)) + case num.Cmp(Finney) >= 0: + return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney)) + case num.Cmp(Szabo) >= 0: + return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo)) + case num.Cmp(Vito) >= 0: + return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito)) + case num.Cmp(Turing) >= 0: + return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing)) + case num.Cmp(Eins) >= 0: + return fmt.Sprintf("%v Eins", new(big.Int).Div(num, Eins)) + } + + return fmt.Sprintf("%v Wei", num) +} diff --git a/ethutil/common_test.go b/ethutil/common_test.go new file mode 100644 index 000000000..3a6a37ff5 --- /dev/null +++ b/ethutil/common_test.go @@ -0,0 +1,17 @@ +package ethutil + +import ( + "fmt" + "math/big" + "testing" +) + +func TestCommon(t *testing.T) { + fmt.Println(CurrencyToString(BigPow(10, 19))) + fmt.Println(CurrencyToString(BigPow(10, 16))) + fmt.Println(CurrencyToString(BigPow(10, 13))) + fmt.Println(CurrencyToString(BigPow(10, 10))) + fmt.Println(CurrencyToString(BigPow(10, 7))) + fmt.Println(CurrencyToString(BigPow(10, 4))) + fmt.Println(CurrencyToString(big.NewInt(10))) +} From 4f4175a3e295272025d312c800f65fc1a143a9ca Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 11:21:03 +0100 Subject: [PATCH 68/78] Addad db name for new ldb --- ethdb/database.go | 8 ++++++-- ethereum.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ethdb/database.go b/ethdb/database.go index 1e987920d..d1967f023 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -11,8 +11,8 @@ type LDBDatabase struct { db *leveldb.DB } -func NewLDBDatabase() (*LDBDatabase, error) { - dbPath := path.Join(ethutil.Config.ExecPath, "database") +func NewLDBDatabase(name string) (*LDBDatabase, error) { + dbPath := path.Join(ethutil.Config.ExecPath, name) // Open the db db, err := leveldb.OpenFile(dbPath, nil) @@ -40,6 +40,10 @@ func (db *LDBDatabase) Delete(key []byte) error { return db.db.Delete(key, nil) } +func (db *LDBDatabase) Db() *leveldb.DB { + return db.db +} + func (db *LDBDatabase) LastKnownTD() []byte { data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil) diff --git a/ethereum.go b/ethereum.go index 725fe5a3d..90682b396 100644 --- a/ethereum.go +++ b/ethereum.go @@ -61,7 +61,7 @@ type Ethereum struct { } func New(caps Caps, usePnp bool) (*Ethereum, error) { - db, err := ethdb.NewLDBDatabase() + db, err := ethdb.NewLDBDatabase("database") //db, err := ethdb.NewMemDatabase() if err != nil { return nil, err From 507fc7b9d1e227de91b25e18891c4cd44452b222 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 11:21:35 +0100 Subject: [PATCH 69/78] Length checking when fetching contract. Contract always have 3 fields --- ethchain/block.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ethchain/block.go b/ethchain/block.go index 472ae134a..7ca44a47d 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -131,6 +131,11 @@ func (block *Block) GetContract(addr []byte) *Contract { return nil } + value := ethutil.NewValueFromBytes([]byte(data)) + if value.Len() == 2 { + return nil + } + contract := &Contract{} contract.RlpDecode([]byte(data)) From ce07d9bb4c95a118636c4b71e9cba2d81540c4a5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 11:21:49 +0100 Subject: [PATCH 70/78] Error logging on tx processing --- ethchain/block_manager.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 04036dbb1..364a06158 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -92,12 +92,13 @@ func (bm *BlockManager) WatchAddr(addr []byte) *AddressState { } func (bm *BlockManager) GetAddrState(addr []byte) *AddressState { - addrState := bm.addrStateStore.Get(addr) - if addrState == nil { - addrState = bm.WatchAddr(addr) + account := bm.addrStateStore.Get(addr) + if account == nil { + a := bm.bc.CurrentBlock.GetAddr(addr) + account = &AddressState{Nonce: a.Nonce, Account: a} } - return addrState + return account } func (bm *BlockManager) BlockChain() *BlockChain { @@ -114,7 +115,10 @@ func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) { if contract := block.GetContract(tx.Recipient); contract != nil { bm.ProcessContract(contract, tx, block) } else { - bm.TransactionPool.ProcessTransaction(tx, block) + err := bm.TransactionPool.ProcessTransaction(tx, block) + if err != nil { + ethutil.Config.Log.Infoln("[BMGR]", err) + } } } } From 1e7b3cbb1343d5362b8ed55d06b1927171f56a41 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 11:22:05 +0100 Subject: [PATCH 71/78] Removed debug log --- ethchain/contract.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/ethchain/contract.go b/ethchain/contract.go index 14abb05d1..dbcbb3697 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -1,7 +1,6 @@ package ethchain import ( - "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" ) @@ -66,8 +65,6 @@ func MakeContract(tx *Transaction, state *State) *Contract { state.trie.Update(string(addr), string(contract.RlpEncode())) return contract - } else { - fmt.Println("NO CONTRACT") } return nil From 4b8c50e2cde130bf278b14040a267aab573dd53e Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 11:22:18 +0100 Subject: [PATCH 72/78] Deprication --- ethchain/transaction.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 39b2bda94..57df9cdc4 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -29,7 +29,12 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { return &tx } +// XXX Deprecated func NewTransactionFromData(data []byte) *Transaction { + return NewTransactionFromBytes(data) +} + +func NewTransactionFromBytes(data []byte) *Transaction { tx := &Transaction{} tx.RlpDecode(data) From e98b53bbef8cdbeed54546c75d856d53810e424c Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 25 Feb 2014 11:22:27 +0100 Subject: [PATCH 73/78] WIP Observing pattern --- ethchain/transaction_pool.go | 41 +++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 1278cc4dc..cd09bf02e 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -17,6 +17,17 @@ const ( ) type TxPoolHook chan *Transaction +type TxMsgTy byte + +const ( + TxPre = iota + TxPost +) + +type TxMsg struct { + Tx *Transaction + Type TxMsgTy +} func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction { for e := pool.Front(); e != nil; e = e.Next() { @@ -59,6 +70,8 @@ type TxPool struct { BlockManager *BlockManager SecondaryProcessor TxProcessor + + subscribers []chan TxMsg } func NewTxPool() *TxPool { @@ -73,21 +86,17 @@ func NewTxPool() *TxPool { // Blocking function. Don't use directly. Use QueueTransaction instead func (pool *TxPool) addTransaction(tx *Transaction) { - log.Println("Adding tx to pool") pool.mutex.Lock() pool.pool.PushBack(tx) pool.mutex.Unlock() // Broadcast the transaction to the rest of the peers pool.Speaker.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()}) - log.Println("broadcasting it") } // Process transaction validates the Tx and processes funds from the // sender to the recipient. func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) { - log.Printf("[TXPL] Processing Tx %x\n", tx.Hash()) - defer func() { if r := recover(); r != nil { log.Println(r) @@ -132,6 +141,11 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error block.UpdateAddr(tx.Sender(), sender) + log.Printf("[TXPL] Processed Tx %x\n", tx.Hash()) + + // Notify the subscribers + pool.notifySubscribers(TxPost, tx) + return } @@ -145,7 +159,8 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { } // Get the sender - sender := block.GetAddr(tx.Sender()) + accountState := pool.BlockManager.GetAddrState(tx.Sender()) + sender := accountState.Account totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) // Make sure there's enough in the sender's account. Having insufficient @@ -185,9 +200,8 @@ out: // doesn't matter since this is a goroutine pool.addTransaction(tx) - if pool.SecondaryProcessor != nil { - pool.SecondaryProcessor.ProcessTransaction(tx) - } + // Notify the subscribers + pool.notifySubscribers(TxPre, tx) } case <-pool.quit: break out @@ -231,3 +245,14 @@ func (pool *TxPool) Stop() { pool.Flush() } + +func (pool *TxPool) Subscribe(channel chan TxMsg) { + pool.subscribers = append(pool.subscribers, channel) +} + +func (pool *TxPool) notifySubscribers(ty TxMsgTy, tx *Transaction) { + msg := TxMsg{Type: ty, Tx: tx} + for _, subscriber := range pool.subscribers { + subscriber <- msg + } +} From c9f3d1c00ba70016be4bb871f9ecd50d456c6985 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 28 Feb 2014 10:36:06 +0100 Subject: [PATCH 74/78] leveldb API changed for NewIterator. Fixes #20 --- ethdb/database.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ethdb/database.go b/ethdb/database.go index d1967f023..3dbff36de 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -54,13 +54,19 @@ func (db *LDBDatabase) LastKnownTD() []byte { return data } +func (db *LDBDatabase) GetKeys() []*ethutil.Key { + data, _ := db.Get([]byte("KeyRing")) + + return []*ethutil.Key{ethutil.NewKeyFromBytes(data)} +} + func (db *LDBDatabase) Close() { // Close the leveldb database db.db.Close() } func (db *LDBDatabase) Print() { - iter := db.db.NewIterator(nil) + iter := db.db.NewIterator(nil, nil) for iter.Next() { key := iter.Key() value := iter.Value() From c0fcefa3a023c643040b3d9e300980bd32c33dff Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 28 Feb 2014 12:18:41 +0100 Subject: [PATCH 75/78] Added a GetKeys method to support multiple accounts --- ethdb/memory_database.go | 6 ++++++ ethutil/db.go | 1 + ethutil/key.go | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 ethutil/key.go diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go index 9e91eb7d6..b0fa64ed7 100644 --- a/ethdb/memory_database.go +++ b/ethdb/memory_database.go @@ -26,6 +26,12 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) { return db.db[string(key)], nil } +func (db *MemDatabase) GetKeys() []*ethutil.Key { + data, _ := db.Get([]byte("KeyRing")) + + return []*ethutil.Key{ethutil.NewKeyFromBytes(data)} +} + func (db *MemDatabase) Delete(key []byte) error { delete(db.db, string(key)) diff --git a/ethutil/db.go b/ethutil/db.go index b11d5d726..abbf4a2b0 100644 --- a/ethutil/db.go +++ b/ethutil/db.go @@ -4,6 +4,7 @@ package ethutil type Database interface { Put(key []byte, value []byte) Get(key []byte) ([]byte, error) + GetKeys() []*Key Delete(key []byte) error LastKnownTD() []byte Close() diff --git a/ethutil/key.go b/ethutil/key.go new file mode 100644 index 000000000..ec195f213 --- /dev/null +++ b/ethutil/key.go @@ -0,0 +1,19 @@ +package ethutil + +type Key struct { + PrivateKey []byte + PublicKey []byte +} + +func NewKeyFromBytes(data []byte) *Key { + val := NewValueFromBytes(data) + return &Key{val.Get(0).Bytes(), val.Get(1).Bytes()} +} + +func (k *Key) Address() []byte { + return Sha3Bin(k.PublicKey[1:])[12:] +} + +func (k *Key) RlpEncode() []byte { + return EmptyValue().Append(k.PrivateKey).Append(k.PublicKey).Encode() +} From 9d06f9e6fbc20006df6812ed3feb5d8058c0b0a6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 28 Feb 2014 12:19:01 +0100 Subject: [PATCH 76/78] Updated readme#trie --- ethutil/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ethutil/README.md b/ethutil/README.md index c98612e1e..1ed56b71b 100644 --- a/ethutil/README.md +++ b/ethutil/README.md @@ -53,6 +53,8 @@ trie.Put("doge", "coin") // Look up the key "do" in the trie out := trie.Get("do") fmt.Println(out) // => verb + +trie.Delete("puppy") ``` The patricia trie, in combination with RLP, provides a robust, @@ -82,7 +84,7 @@ type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc). `NewEmptyValue()` returns a new \*Value with it's initial value set to a `[]interface{}` -`AppendLint()` appends a list to the current value. +`AppendList()` appends a list to the current value. `Append(v)` appends the value (v) to the current value/list. From 8fa19664e657aca80446e6c3f7868f19ac717a07 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 28 Feb 2014 12:19:21 +0100 Subject: [PATCH 77/78] Added BigCopy --- ethutil/big.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ethutil/big.go b/ethutil/big.go index c41d63add..1a3902fa3 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -41,3 +41,12 @@ func BigToBytes(num *big.Int, base int) []byte { return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) } + +// Functions like the build in "copy" function +// but works on big integers +func BigCopy(src *big.Int) (ret *big.Int) { + ret = new(big.Int) + ret.Add(ret, src) + + return +} From 3f7ec1a83fe13dc934d92a405ff01b0be6c04ac0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 28 Feb 2014 12:19:45 +0100 Subject: [PATCH 78/78] Conform to Db interface --- ethutil/trie_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index 645c5a225..7c398f1de 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -25,6 +25,7 @@ func (db *MemDatabase) Delete(key []byte) error { delete(db.db, string(key)) return nil } +func (db *MemDatabase) GetKeys() []*Key { return nil } func (db *MemDatabase) Print() {} func (db *MemDatabase) Close() {} func (db *MemDatabase) LastKnownTD() []byte { return nil }