aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--README.md101
-rw-r--r--ethchain/.gitignore12
-rw-r--r--ethchain/asm.go40
-rw-r--r--ethchain/block.go419
-rw-r--r--ethchain/block_chain.go371
-rw-r--r--ethchain/block_chain_test.go134
-rw-r--r--ethchain/block_manager_test.go36
-rw-r--r--ethchain/closure.go129
-rw-r--r--ethchain/dagger.go215
-rw-r--r--ethchain/dagger_test.go18
-rw-r--r--ethchain/error.go98
-rw-r--r--ethchain/fees.go9
-rw-r--r--ethchain/genesis.go45
-rw-r--r--ethchain/stack.go150
-rw-r--r--ethchain/state.go206
-rw-r--r--ethchain/state_manager.go355
-rw-r--r--ethchain/state_object.go273
-rw-r--r--ethchain/state_object_test.go52
-rw-r--r--ethchain/state_test.go30
-rw-r--r--ethchain/state_transition.go302
-rw-r--r--ethchain/transaction.go240
-rw-r--r--ethchain/transaction_pool.go286
-rw-r--r--ethchain/transaction_test.go1
-rw-r--r--ethchain/types.go346
-rw-r--r--ethchain/vm.go742
-rw-r--r--ethchain/vm_test.go68
-rw-r--r--ethdb/.gitignore12
-rw-r--r--ethdb/README.md11
-rw-r--r--ethdb/database.go80
-rw-r--r--ethdb/database_test.go6
-rw-r--r--ethdb/memory_database.go62
-rw-r--r--ethereum.go499
-rw-r--r--ethlog/README.md62
-rw-r--r--ethlog/loggers.go188
-rw-r--r--ethlog/loggers_test.go109
-rw-r--r--ethminer/miner.go186
-rw-r--r--ethpub/pub.go232
-rw-r--r--ethpub/types.go264
-rw-r--r--ethrpc/packages.go287
-rw-r--r--ethrpc/server.go66
-rw-r--r--ethutil/.gitignore12
-rw-r--r--ethutil/.travis.yml3
-rw-r--r--ethutil/README.md139
-rw-r--r--ethutil/big.go75
-rw-r--r--ethutil/bytes.go119
-rw-r--r--ethutil/common.go61
-rw-r--r--ethutil/common_test.go44
-rw-r--r--ethutil/config.go78
-rw-r--r--ethutil/db.go12
-rw-r--r--ethutil/encoding.go76
-rw-r--r--ethutil/encoding_test.go67
-rw-r--r--ethutil/helpers.go64
-rw-r--r--ethutil/keypair.go115
-rw-r--r--ethutil/mnemonic.go1690
-rw-r--r--ethutil/mnemonic_test.go74
-rw-r--r--ethutil/package.go123
-rw-r--r--ethutil/rand.go24
-rw-r--r--ethutil/reactor.go87
-rw-r--r--ethutil/reactor_test.go30
-rw-r--r--ethutil/rlp.go225
-rw-r--r--ethutil/rlp_test.go135
-rw-r--r--ethutil/script.go37
-rw-r--r--ethutil/trie.go578
-rw-r--r--ethutil/trie_test.go331
-rw-r--r--ethutil/value.go279
-rw-r--r--ethutil/value_test.go65
-rw-r--r--ethwire/.gitignore12
-rw-r--r--ethwire/README.md36
-rw-r--r--ethwire/messaging.go339
-rw-r--r--nat.go12
-rw-r--r--natpmp.go54
-rw-r--r--natupnp.go338
-rw-r--r--peer.go785
74 files changed, 12810 insertions, 54 deletions
diff --git a/.gitignore b/.gitignore
index de3a847ac..64dff7dd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,3 @@
*un~
.DS_Store
*/**/.DS_Store
-ethereum/ethereum
-ethereal/ethereal
-
diff --git a/README.md b/README.md
index d2a55ce0f..26783d12d 100644
--- a/README.md
+++ b/README.md
@@ -3,72 +3,66 @@ Ethereum
[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
-Ethereum Go Client © 2014 Jeffrey Wilcke.
-
-Current state: Proof of Concept 0.5.15.
-
-For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).
+Ethereum Go Development package (C) Jeffrey Wilcke
+
+Ethereum is currently in its testing phase. The current state is "Proof
+of Concept 0.5.15". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
+
+Ethereum Go is split up in several sub packages Please refer to each
+individual package for more information.
+ 1. [eth](https://github.com/ethereum/eth-go)
+ 2. [ethchain](https://github.com/ethereum/eth-go/tree/master/ethchain)
+ 3. [ethwire](https://github.com/ethereum/eth-go/tree/master/ethwire)
+ 4. [ethdb](https://github.com/ethereum/eth-go/tree/master/ethdb)
+ 5. [ethutil](https://github.com/ethereum/eth-go/tree/master/ethutil)
+
+The [eth](https://github.com/ethereum/eth-go) is the top-level package
+of the Ethereum protocol. It functions as the Ethereum bootstrapping and
+peer communication layer. The [ethchain](https://github.com/ethereum/eth-go/tree/master/ethchain)
+contains the Ethereum blockchain, block manager, transaction and
+transaction handlers. The [ethwire](https://github.com/ethereum/eth-go/tree/master/ethwire) contains
+the Ethereum [wire protocol](http://wiki.ethereum.org/index.php/Wire_Protocol) which can be used
+to hook in to the Ethereum network. [ethutil](https://github.com/ethereum/eth-go/tree/master/ethutil) contains
+utility functions which are not Ethereum specific. The utility package
+contains the [patricia trie](http://wiki.ethereum.org/index.php/Patricia_Tree),
+[RLP Encoding](http://wiki.ethereum.org/index.php/RLP) and hex encoding
+helpers. The [ethdb](https://github.com/ethereum/eth-go/tree/master/ethdb) package
+contains the LevelDB interface and memory DB interface.
+
+This is the bootstrap package. Eth-go contains all the necessary code to
+get a node and connectivity going.
Build
=======
-To build Ethereal (GUI):
-
-`go get github.com/ethereum/go-ethereum/ethereal`
-
-To build the node (CLI):
-
-`go get github.com/ethereum/go-ethereum/ethereum`
+This is the Developer package. For the Ethereal client please see
+[Ethereum(G)](https://github.com/ethereum/go-ethereum).
-For further, detailed, build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go))
-
-General command line options
-====================
-
-```
-Shared between ethereum and ethereal
--id Set the custom identifier of the client (shows up on other clients)
--port Port on which the server will accept incomming connections
--upnp Enable UPnP
--maxpeer Desired amount of peers
--rpc Start JSON RPC
-
--dir Data directory used to store configs and databases
--import Import a private key
--genaddr Generates a new address and private key (destructive action)
--h This
-
-Ethereum only
-ethereum [options] [filename]
--js Start the JavaScript REPL
-filename Load the given file and interpret as JavaScript
--m Start mining blocks
-
-Etheral only
--asset_path absolute path to GUI assets directory
-```
+`go get -u github.com/ethereum/eth-go`
Contribution
============
-If you would like to contribute to Ethereum Go, please fork, fix, commit and
-send a pull request to the main repository. Commits which do not comply with the coding standards explained below
-will be ignored. If you send a pull request, make sure that you
-commit to the `develop` branch and that you do not merge to `master`.
-Commits that are directly based off of the `master` branch instead of the `develop` branch will be ignored.
+If you'd like to contribute to Eth please fork, fix, commit and
+send a pull request. Commits who do not comply with the coding standards
+are ignored. If you send pull requests make absolute sure that you
+commit on the `develop` branch and that you do not merge to master.
+Commits that are directly based on master are simply ignored.
-To make this process simpler try following the [git flow](http://nvie.com/posts/a-successful-git-branching-model/) branching model, as it sets this process up and streamlines work flow.
+To make life easier try [git flow](http://nvie.com/posts/a-successful-git-branching-model/) it sets
+this all up and streamlines your work flow.
Coding standards
================
-Code should be formatted according to the [Go Formatting
+Sources should be formatted according to the [Go Formatting
Style](http://golang.org/doc/effective_go.html#formatting).
-Unless struct fields are supposed to be directly accessible, provide
-getters and hide the fields through Go's exporting facility.
+Unless structs fields are supposed to be directly accesible, provide
+Getters and hide the fields through Go's exporting facility.
-Make comments in your code meaningful and only use them when necessary. Describe in detail what your code is trying to achieve. For example, this would be redundant and unnecessary commenting:
+When you comment put meaningfull comments. Describe in detail what you
+want to achieve.
*wrong*
@@ -79,7 +73,12 @@ if x > y {
}
```
-Everyone reading the source code should know what this code snippet was meant to achieve, and so those are **not** meaningful comments.
+Everyone reading the source probably know what you wanted to achieve
+with above code. Those are **not** meaningful comments.
-While this project is constantly tested and run, code tests should be written regardless. There is not time to evaluate every person's code specifically, so it is expected of you to write tests for the code so that it does not have to be tested manually. In fact, contributing by simply writing tests is perfectly fine!
+While the project isn't 100% tested I want you to write tests non the
+less. I haven't got time to evaluate everyone's code in detail so I
+expect you to write tests for me so I don't have to test your code
+manually. (If you want to contribute by just writing tests that's fine
+too!)
diff --git a/ethchain/.gitignore b/ethchain/.gitignore
new file mode 100644
index 000000000..f725d58d1
--- /dev/null
+++ b/ethchain/.gitignore
@@ -0,0 +1,12 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile ~/.gitignore_global
+
+/tmp
+*/**/*un~
+*un~
+.DS_Store
+*/**/.DS_Store
+
diff --git a/ethchain/asm.go b/ethchain/asm.go
new file mode 100644
index 000000000..09d6af56f
--- /dev/null
+++ b/ethchain/asm.go
@@ -0,0 +1,40 @@
+package ethchain
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+func Disassemble(script []byte) (asm []string) {
+ pc := new(big.Int)
+ for {
+ if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 {
+ return
+ }
+
+ // Get the memory location of pc
+ val := script[pc.Int64()]
+ // Get the opcode (it must be an opcode!)
+ op := OpCode(val)
+
+ asm = append(asm, fmt.Sprintf("%04v: %v", pc, op))
+
+ switch op {
+ case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
+ pc.Add(pc, ethutil.Big1)
+ a := int64(op) - int64(PUSH1) + 1
+ data := script[pc.Int64() : pc.Int64()+a]
+ if len(data) == 0 {
+ data = []byte{0}
+ }
+ asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data))
+
+ pc.Add(pc, big.NewInt(a-1))
+ }
+
+ pc.Add(pc, ethutil.Big1)
+ }
+
+ return
+}
diff --git a/ethchain/block.go b/ethchain/block.go
new file mode 100644
index 000000000..fee4a2d59
--- /dev/null
+++ b/ethchain/block.go
@@ -0,0 +1,419 @@
+package ethchain
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "strconv"
+ "time"
+)
+
+type BlockInfo struct {
+ Number uint64
+ Hash []byte
+ Parent []byte
+}
+
+func (bi *BlockInfo) RlpDecode(data []byte) {
+ decoder := ethutil.NewValueFromBytes(data)
+
+ bi.Number = decoder.Get(0).Uint()
+ bi.Hash = decoder.Get(1).Bytes()
+ bi.Parent = decoder.Get(2).Bytes()
+}
+
+func (bi *BlockInfo) RlpEncode() []byte {
+ return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent})
+}
+
+type Block struct {
+ // Hash to the previous block
+ PrevHash []byte
+ // Uncles of this block
+ Uncles []*Block
+ UncleSha []byte
+ // The coin base address
+ Coinbase []byte
+ // Block Trie state
+ //state *ethutil.Trie
+ state *State
+ // Difficulty for the current block
+ Difficulty *big.Int
+ // Creation time
+ Time int64
+ // The block number
+ Number *big.Int
+ // Minimum Gas Price
+ MinGasPrice *big.Int
+ // Gas limit
+ GasLimit *big.Int
+ // Gas used
+ GasUsed *big.Int
+ // Extra data
+ Extra string
+ // Block Nonce for verification
+ Nonce []byte
+ // List of transactions and/or contracts
+ transactions []*Transaction
+ receipts []*Receipt
+ TxSha []byte
+}
+
+// New block takes a raw encoded string
+// XXX DEPRICATED
+func NewBlockFromData(raw []byte) *Block {
+ return NewBlockFromBytes(raw)
+}
+
+func NewBlockFromBytes(raw []byte) *Block {
+ block := &Block{}
+ block.RlpDecode(raw)
+
+ return block
+}
+
+// New block takes a raw encoded string
+func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
+ block := &Block{}
+ block.RlpValueDecode(rlpValue)
+
+ return block
+}
+
+func CreateBlock(root interface{},
+ prevHash []byte,
+ base []byte,
+ Difficulty *big.Int,
+ Nonce []byte,
+ extra string) *Block {
+
+ block := &Block{
+ PrevHash: prevHash,
+ Coinbase: base,
+ Difficulty: Difficulty,
+ Nonce: Nonce,
+ Time: time.Now().Unix(),
+ Extra: extra,
+ UncleSha: EmptyShaList,
+ GasUsed: new(big.Int),
+ MinGasPrice: new(big.Int),
+ GasLimit: new(big.Int),
+ }
+ block.SetUncles([]*Block{})
+
+ block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, root))
+
+ return block
+}
+
+// Returns a hash of the block
+func (block *Block) Hash() []byte {
+ return ethutil.Sha3Bin(block.Value().Encode())
+}
+
+func (block *Block) HashNoNonce() []byte {
+ return ethutil.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash,
+ block.UncleSha, block.Coinbase, block.state.trie.Root,
+ block.TxSha, block.Difficulty, block.Number, block.MinGasPrice,
+ block.GasLimit, block.GasUsed, block.Time, block.Extra}))
+}
+
+func (block *Block) State() *State {
+ return block.state
+}
+
+func (block *Block) Transactions() []*Transaction {
+ return block.transactions
+}
+
+func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
+ contract := block.state.GetStateObject(addr)
+ // If we can't pay the fee return
+ if contract == nil || contract.Amount.Cmp(fee) < 0 /* amount < fee */ {
+ fmt.Println("Contract has insufficient funds", contract.Amount, fee)
+
+ return false
+ }
+
+ base := new(big.Int)
+ contract.Amount = base.Sub(contract.Amount, fee)
+ block.state.trie.Update(string(addr), string(contract.RlpEncode()))
+
+ data := block.state.trie.Get(string(block.Coinbase))
+
+ // Get the ether (Coinbase) and add the fee (gief fee to miner)
+ account := NewStateObjectFromBytes(block.Coinbase, []byte(data))
+
+ base = new(big.Int)
+ account.Amount = base.Add(account.Amount, fee)
+
+ //block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
+ block.state.UpdateStateObject(account)
+
+ return true
+}
+
+func (block *Block) CalcGasLimit(parent *Block) *big.Int {
+ if block.Number.Cmp(big.NewInt(0)) == 0 {
+ return ethutil.BigPow(10, 6)
+ }
+
+ previous := new(big.Int).Mul(big.NewInt(1023), parent.GasLimit)
+ current := new(big.Rat).Mul(new(big.Rat).SetInt(block.GasUsed), big.NewRat(6, 5))
+ curInt := new(big.Int).Div(current.Num(), current.Denom())
+
+ result := new(big.Int).Add(previous, curInt)
+ result.Div(result, big.NewInt(1024))
+
+ min := ethutil.BigPow(10, 4)
+
+ return ethutil.BigMax(min, result)
+ /*
+ base := new(big.Int)
+ base2 := new(big.Int)
+ parentGL := bc.CurrentBlock.GasLimit
+ parentUsed := bc.CurrentBlock.GasUsed
+
+ base.Mul(parentGL, big.NewInt(1024-1))
+ base2.Mul(parentUsed, big.NewInt(6))
+ base2.Div(base2, big.NewInt(5))
+ base.Add(base, base2)
+ base.Div(base, big.NewInt(1024))
+ */
+
+}
+
+func (block *Block) BlockInfo() BlockInfo {
+ bi := BlockInfo{}
+ data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
+ bi.RlpDecode(data)
+
+ return bi
+}
+
+func (self *Block) GetTransaction(hash []byte) *Transaction {
+ for _, receipt := range self.receipts {
+ if bytes.Compare(receipt.Tx.Hash(), hash) == 0 {
+ return receipt.Tx
+ }
+ }
+
+ return nil
+}
+
+// Sync the block's state and contract respectively
+func (block *Block) Sync() {
+ block.state.Sync()
+}
+
+func (block *Block) Undo() {
+ // Sync the block state itself
+ block.state.Reset()
+}
+
+/////// Block Encoding
+func (block *Block) rlpReceipts() interface{} {
+ // Marshal the transactions of this block
+ encR := make([]interface{}, len(block.receipts))
+ for i, r := range block.receipts {
+ // Cast it to a string (safe)
+ encR[i] = r.RlpData()
+ }
+
+ return encR
+}
+
+func (block *Block) rlpUncles() interface{} {
+ // Marshal the transactions of this block
+ uncles := make([]interface{}, len(block.Uncles))
+ for i, uncle := range block.Uncles {
+ // Cast it to a string (safe)
+ uncles[i] = uncle.header()
+ }
+
+ return uncles
+}
+
+func (block *Block) SetUncles(uncles []*Block) {
+ block.Uncles = uncles
+
+ // Sha of the concatenated uncles
+ block.UncleSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpUncles()))
+}
+
+func (self *Block) SetReceipts(receipts []*Receipt, txs []*Transaction) {
+ self.receipts = receipts
+ self.setTransactions(txs)
+}
+
+func (block *Block) setTransactions(txs []*Transaction) {
+ block.transactions = txs
+
+ trie := ethutil.NewTrie(ethutil.Config.Db, "")
+ for i, tx := range txs {
+ trie.Update(strconv.Itoa(i), string(tx.RlpEncode()))
+ }
+
+ switch trie.Root.(type) {
+ case string:
+ block.TxSha = []byte(trie.Root.(string))
+ case []byte:
+ block.TxSha = trie.Root.([]byte)
+ default:
+ panic(fmt.Sprintf("invalid root type %T", trie.Root))
+ }
+
+}
+
+func (block *Block) Value() *ethutil.Value {
+ return ethutil.NewValue([]interface{}{block.header(), block.rlpReceipts(), block.rlpUncles()})
+}
+
+func (block *Block) RlpEncode() []byte {
+ // Encode a slice interface which contains the header and the list of
+ // transactions.
+ return block.Value().Encode()
+}
+
+func (block *Block) RlpDecode(data []byte) {
+ rlpValue := ethutil.NewValueFromBytes(data)
+ block.RlpValueDecode(rlpValue)
+}
+
+func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
+ header := decoder.Get(0)
+
+ block.PrevHash = header.Get(0).Bytes()
+ block.UncleSha = header.Get(1).Bytes()
+ block.Coinbase = header.Get(2).Bytes()
+ block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val))
+ block.TxSha = header.Get(4).Bytes()
+ block.Difficulty = header.Get(5).BigInt()
+ block.Number = header.Get(6).BigInt()
+ //fmt.Printf("#%v : %x\n", block.Number, block.Coinbase)
+ block.MinGasPrice = header.Get(7).BigInt()
+ block.GasLimit = header.Get(8).BigInt()
+ block.GasUsed = header.Get(9).BigInt()
+ block.Time = int64(header.Get(10).BigInt().Uint64())
+ block.Extra = header.Get(11).Str()
+ block.Nonce = header.Get(12).Bytes()
+
+ // Tx list might be empty if this is an uncle. Uncles only have their
+ // header set.
+ if decoder.Get(1).IsNil() == false { // Yes explicitness
+ receipts := decoder.Get(1)
+ block.transactions = make([]*Transaction, receipts.Len())
+ block.receipts = make([]*Receipt, receipts.Len())
+ for i := 0; i < receipts.Len(); i++ {
+ receipt := NewRecieptFromValue(receipts.Get(i))
+ block.transactions[i] = receipt.Tx
+ block.receipts[i] = receipt
+ }
+
+ }
+
+ if decoder.Get(2).IsNil() == false { // Yes explicitness
+ uncles := decoder.Get(2)
+ block.Uncles = make([]*Block, uncles.Len())
+ for i := 0; i < uncles.Len(); i++ {
+ block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
+ }
+ }
+
+}
+
+func NewUncleBlockFromValue(header *ethutil.Value) *Block {
+ block := &Block{}
+
+ block.PrevHash = header.Get(0).Bytes()
+ block.UncleSha = header.Get(1).Bytes()
+ block.Coinbase = header.Get(2).Bytes()
+ block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val))
+ block.TxSha = header.Get(4).Bytes()
+ block.Difficulty = header.Get(5).BigInt()
+ block.Number = header.Get(6).BigInt()
+ block.MinGasPrice = header.Get(7).BigInt()
+ block.GasLimit = header.Get(8).BigInt()
+ block.GasUsed = header.Get(9).BigInt()
+ block.Time = int64(header.Get(10).BigInt().Uint64())
+ block.Extra = header.Get(11).Str()
+ block.Nonce = header.Get(12).Bytes()
+
+ return block
+}
+
+func (block *Block) GetRoot() interface{} {
+ return block.state.trie.Root
+}
+
+func (self *Block) Receipts() []*Receipt {
+ return self.receipts
+}
+
+func (block *Block) header() []interface{} {
+ return []interface{}{
+ // Sha of the previous block
+ block.PrevHash,
+ // Sha of uncles
+ block.UncleSha,
+ // Coinbase address
+ block.Coinbase,
+ // root state
+ block.state.trie.Root,
+ // Sha of tx
+ block.TxSha,
+ // Current block Difficulty
+ block.Difficulty,
+ // The block number
+ block.Number,
+ // Block minimum gas price
+ block.MinGasPrice,
+ // Block upper gas bound
+ block.GasLimit,
+ // Block gas used
+ block.GasUsed,
+ // Time the block was found?
+ block.Time,
+ // Extra data
+ block.Extra,
+ // Block's Nonce for validation
+ block.Nonce,
+ }
+}
+
+func (block *Block) String() string {
+ return fmt.Sprintf(`
+ BLOCK(%x):
+ PrevHash: %x
+ UncleSha: %x
+ Coinbase: %x
+ Root: %x
+ TxSha: %x
+ Difficulty: %v
+ Number: %v
+ MinGas: %v
+ MaxLimit: %v
+ GasUsed: %v
+ Time: %v
+ Extra: %v
+ Nonce: %x
+ NumTx: %v
+`,
+ block.Hash(),
+ block.PrevHash,
+ block.UncleSha,
+ block.Coinbase,
+ block.state.trie.Root,
+ block.TxSha,
+ block.Difficulty,
+ block.Number,
+ block.MinGasPrice,
+ block.GasLimit,
+ block.GasUsed,
+ block.Time,
+ block.Extra,
+ block.Nonce,
+ len(block.transactions),
+ )
+}
diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go
new file mode 100644
index 000000000..6e4c72b27
--- /dev/null
+++ b/ethchain/block_chain.go
@@ -0,0 +1,371 @@
+package ethchain
+
+import (
+ "bytes"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "math"
+ "math/big"
+)
+
+var chainlogger = ethlog.NewLogger("CHAIN")
+
+type BlockChain struct {
+ Ethereum EthManager
+ // The famous, the fabulous Mister GENESIIIIIIS (block)
+ genesisBlock *Block
+ // Last known total difficulty
+ TD *big.Int
+
+ LastBlockNumber uint64
+
+ CurrentBlock *Block
+ LastBlockHash []byte
+}
+
+func NewBlockChain(ethereum EthManager) *BlockChain {
+ bc := &BlockChain{}
+ bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
+ bc.Ethereum = ethereum
+
+ bc.setLastBlock()
+
+ return bc
+}
+
+func (bc *BlockChain) Genesis() *Block {
+ return bc.genesisBlock
+}
+
+func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
+ var root interface{}
+ var lastBlockTime int64
+ hash := ZeroHash256
+
+ if bc.CurrentBlock != nil {
+ root = bc.CurrentBlock.state.trie.Root
+ hash = bc.LastBlockHash
+ lastBlockTime = bc.CurrentBlock.Time
+ }
+
+ block := CreateBlock(
+ root,
+ hash,
+ coinbase,
+ ethutil.BigPow(2, 32),
+ nil,
+ "")
+
+ block.MinGasPrice = big.NewInt(10000000000000)
+
+ if bc.CurrentBlock != nil {
+ var mul *big.Int
+ if block.Time < lastBlockTime+42 {
+ mul = big.NewInt(1)
+ } else {
+ mul = big.NewInt(-1)
+ }
+
+ diff := new(big.Int)
+ diff.Add(diff, bc.CurrentBlock.Difficulty)
+ diff.Div(diff, big.NewInt(1024))
+ diff.Mul(diff, mul)
+ diff.Add(diff, bc.CurrentBlock.Difficulty)
+ block.Difficulty = diff
+
+ block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
+
+ block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
+ }
+
+ return block
+}
+
+func (bc *BlockChain) HasBlock(hash []byte) bool {
+ data, _ := ethutil.Config.Db.Get(hash)
+ return len(data) != 0
+}
+
+// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
+func (bc *BlockChain) HasBlockWithPrevHash(hash []byte) bool {
+ block := bc.CurrentBlock
+
+ for ; block != nil; block = bc.GetBlock(block.PrevHash) {
+ if bytes.Compare(hash, block.PrevHash) == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int {
+ blockDiff := new(big.Int)
+
+ for _, uncle := range block.Uncles {
+ blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
+ }
+ blockDiff = blockDiff.Add(blockDiff, block.Difficulty)
+
+ return blockDiff
+}
+func (bc *BlockChain) FindCanonicalChainFromMsg(msg *ethwire.Msg, commonBlockHash []byte) bool {
+ var blocks []*Block
+ for i := 0; i < (msg.Data.Len() - 1); i++ {
+ block := NewBlockFromRlpValue(msg.Data.Get(i))
+ blocks = append(blocks, block)
+ }
+ return bc.FindCanonicalChain(blocks, commonBlockHash)
+}
+
+// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one
+// Return true if we are the using the canonical chain false if not
+func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte) bool {
+ // 1. Calculate TD of the current chain
+ // 2. Calculate TD of the new chain
+ // Reset state to the correct one
+
+ chainDifficulty := new(big.Int)
+
+ // Calculate the entire chain until the block we both have
+ // Start with the newest block we got, all the way back to the common block we both know
+ for _, block := range blocks {
+ if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
+ chainlogger.Infoln("[CHAIN] We have found the common parent block, breaking")
+ break
+ }
+ chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
+ }
+
+ chainlogger.Infoln("Incoming chain difficulty:", chainDifficulty)
+
+ curChainDifficulty := new(big.Int)
+ block := bc.CurrentBlock
+ for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
+ i++
+ if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
+ chainlogger.Infoln("We have found the common parent block, breaking")
+ break
+ }
+ anOtherBlock := bc.GetBlock(block.PrevHash)
+ if anOtherBlock == nil {
+ // We do not want to count the genesis block for difficulty since that's not being sent
+ chainlogger.Infoln("At genesis block, breaking")
+ break
+ }
+ curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
+ }
+
+ chainlogger.Infoln("Current chain difficulty:", curChainDifficulty)
+ if chainDifficulty.Cmp(curChainDifficulty) == 1 {
+ chainlogger.Infof("The incoming Chain beat our asses, resetting to block: %x", commonBlockHash)
+ bc.ResetTillBlockHash(commonBlockHash)
+ return false
+ } else {
+ chainlogger.Infoln("Our chain showed the incoming chain who is boss. Ignoring.")
+ return true
+ }
+}
+func (bc *BlockChain) ResetTillBlockHash(hash []byte) error {
+ lastBlock := bc.CurrentBlock
+ var returnTo *Block
+ // Reset to Genesis if that's all the origin there is.
+ if bytes.Compare(hash, bc.genesisBlock.Hash()) == 0 {
+ returnTo = bc.genesisBlock
+ bc.CurrentBlock = bc.genesisBlock
+ bc.LastBlockHash = bc.genesisBlock.Hash()
+ bc.LastBlockNumber = 1
+ } else {
+ returnTo = bc.GetBlock(hash)
+ bc.CurrentBlock = returnTo
+ bc.LastBlockHash = returnTo.Hash()
+ bc.LastBlockNumber = returnTo.Number.Uint64()
+ }
+
+ // Manually reset the last sync block
+ err := ethutil.Config.Db.Delete(lastBlock.Hash())
+ if err != nil {
+ return err
+ }
+
+ var block *Block
+ for ; block != nil; block = bc.GetBlock(block.PrevHash) {
+ if bytes.Compare(block.Hash(), hash) == 0 {
+ chainlogger.Infoln("We have arrived at the the common parent block, breaking")
+ break
+ }
+ err = ethutil.Config.Db.Delete(block.Hash())
+ if err != nil {
+ return err
+ }
+ }
+ chainlogger.Infoln("Split chain deleted and reverted to common parent block.")
+ return nil
+}
+
+func (bc *BlockChain) GenesisBlock() *Block {
+ return bc.genesisBlock
+}
+
+// Get chain return blocks from hash up to max in RLP format
+func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
+ var chain []interface{}
+ // Get the current hash to start with
+ currentHash := bc.CurrentBlock.Hash()
+ // Get the last number on the block chain
+ lastNumber := bc.CurrentBlock.Number.Uint64()
+ // Get the parents number
+ parentNumber := bc.GetBlock(hash).Number.Uint64()
+ // Get the min amount. We might not have max amount of blocks
+ count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
+ startNumber := parentNumber + count
+
+ num := lastNumber
+ for num > startNumber {
+ num--
+
+ block := bc.GetBlock(currentHash)
+ if block == nil {
+ break
+ }
+ currentHash = block.PrevHash
+ }
+
+ for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
+ // Get the block of the chain
+ block := bc.GetBlock(currentHash)
+ if block == nil {
+ chainlogger.Debugf("Unexpected error during GetChainFromHash: Unable to find %x\n", currentHash)
+ break
+ }
+
+ currentHash = block.PrevHash
+
+ chain = append(chain, block.Value().Val)
+
+ num--
+ }
+
+ 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) {
+ blocks = append([]*Block{block}, blocks...)
+
+ if bytes.Compare(genHash, block.Hash()) == 0 {
+ break
+ }
+ i++
+ }
+
+ return blocks
+}
+
+func AddTestNetFunds(block *Block) {
+ for _, addr := range []string{
+ "51ba59315b3a95761d0863b05ccc7a7f54703d99",
+ "e4157b34ea9615cfbde6b4fda419828124b70c78",
+ "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df",
+ "6c386a4b26f73c802f34673f7248bb118f97424a",
+ "cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
+ "2ef47100e0787b915105fd5e3f4ff6752079d5cb",
+ "e6716f9544a56c530d868e4bfbacb172315bdead",
+ "1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
+ } {
+ codedAddr := ethutil.FromHex(addr)
+ account := block.state.GetAccount(codedAddr)
+ account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
+ block.state.UpdateStateObject(account)
+ }
+}
+
+func (bc *BlockChain) setLastBlock() {
+ data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
+ if len(data) != 0 {
+ block := NewBlockFromBytes(data)
+ //info := bc.BlockInfo(block)
+ bc.CurrentBlock = block
+ bc.LastBlockHash = block.Hash()
+ bc.LastBlockNumber = block.Number.Uint64()
+
+ chainlogger.Infof("Last known block height #%d\n", bc.LastBlockNumber)
+ } else {
+ AddTestNetFunds(bc.genesisBlock)
+
+ bc.genesisBlock.state.trie.Sync()
+ // Prepare the genesis block
+ bc.Add(bc.genesisBlock)
+
+ //chainlogger.Infof("root %x\n", bm.bc.genesisBlock.State().Root)
+ //bm.bc.genesisBlock.PrintHash()
+ }
+
+ // Set the last know difficulty (might be 0x0 as initial value, Genesis)
+ bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
+
+ chainlogger.Infof("Last block: %x\n", bc.CurrentBlock.Hash())
+}
+
+func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
+ ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes())
+ bc.TD = td
+}
+
+// Add a block to the chain and record addition information
+func (bc *BlockChain) Add(block *Block) {
+ bc.writeBlockInfo(block)
+ // Prepare the genesis block
+
+ bc.CurrentBlock = block
+ bc.LastBlockHash = block.Hash()
+
+ encodedBlock := block.RlpEncode()
+ ethutil.Config.Db.Put(block.Hash(), encodedBlock)
+ ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
+}
+
+func (bc *BlockChain) GetBlock(hash []byte) *Block {
+ data, _ := ethutil.Config.Db.Get(hash)
+ if len(data) == 0 {
+ return nil
+ }
+
+ return NewBlockFromBytes(data)
+}
+
+func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
+ bi := BlockInfo{}
+ data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
+ bi.RlpDecode(data)
+
+ return bi
+}
+
+func (bc *BlockChain) BlockInfo(block *Block) BlockInfo {
+ bi := BlockInfo{}
+ data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
+ bi.RlpDecode(data)
+
+ return bi
+}
+
+// Unexported method for writing extra non-essential block info to the db
+func (bc *BlockChain) writeBlockInfo(block *Block) {
+ bc.LastBlockNumber++
+ bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash}
+
+ // For now we use the block hash with the words "info" appended as key
+ ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
+}
+
+func (bc *BlockChain) Stop() {
+ if bc.CurrentBlock != nil {
+ chainlogger.Infoln("Stopped")
+ }
+}
diff --git a/ethchain/block_chain_test.go b/ethchain/block_chain_test.go
new file mode 100644
index 000000000..bbc96c823
--- /dev/null
+++ b/ethchain/block_chain_test.go
@@ -0,0 +1,134 @@
+package ethchain
+
+import (
+ "container/list"
+ "fmt"
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "testing"
+)
+
+// Implement our EthTest Manager
+type TestManager struct {
+ stateManager *StateManager
+ reactor *ethutil.ReactorEngine
+
+ txPool *TxPool
+ blockChain *BlockChain
+ Blocks []*Block
+}
+
+func (s *TestManager) IsListening() bool {
+ return false
+}
+
+func (s *TestManager) IsMining() bool {
+ return false
+}
+
+func (s *TestManager) PeerCount() int {
+ return 0
+}
+
+func (s *TestManager) Peers() *list.List {
+ return list.New()
+}
+
+func (s *TestManager) BlockChain() *BlockChain {
+ return s.blockChain
+}
+
+func (tm *TestManager) TxPool() *TxPool {
+ return tm.txPool
+}
+
+func (tm *TestManager) StateManager() *StateManager {
+ return tm.stateManager
+}
+
+func (tm *TestManager) Reactor() *ethutil.ReactorEngine {
+ return tm.reactor
+}
+func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) {
+ fmt.Println("Broadcast not implemented")
+}
+
+func NewTestManager() *TestManager {
+
+ ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
+
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ fmt.Println("Could not create mem-db, failing")
+ return nil
+ }
+ ethutil.Config.Db = db
+
+ testManager := &TestManager{}
+ testManager.reactor = ethutil.NewReactorEngine()
+
+ testManager.txPool = NewTxPool(testManager)
+ testManager.blockChain = NewBlockChain(testManager)
+ testManager.stateManager = NewStateManager(testManager)
+
+ // Start the tx pool
+ testManager.txPool.Start()
+
+ return testManager
+}
+
+func (tm *TestManager) AddFakeBlock(blk []byte) error {
+ block := NewBlockFromBytes(blk)
+ tm.Blocks = append(tm.Blocks, block)
+ err := tm.StateManager().Process(block, false)
+ return err
+}
+
+func (tm *TestManager) CreateChain1() error {
+ err := tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 58, 253, 98, 206, 198, 181, 152, 223, 201, 116, 197, 154, 111, 104, 54, 113, 249, 184, 246, 15, 226, 142, 187, 47, 138, 60, 201, 66, 226, 237, 29, 7, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 240, 0, 132, 83, 48, 32, 251, 128, 160, 4, 10, 11, 225, 132, 86, 146, 227, 229, 137, 164, 245, 16, 139, 219, 12, 251, 178, 154, 168, 210, 18, 84, 40, 250, 41, 124, 92, 169, 242, 246, 180, 192, 192})
+ err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 222, 229, 152, 228, 200, 163, 244, 144, 120, 18, 203, 253, 195, 185, 105, 131, 163, 226, 116, 40, 140, 68, 249, 198, 221, 152, 121, 0, 124, 11, 180, 125, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 224, 4, 132, 83, 48, 36, 250, 128, 160, 79, 58, 51, 246, 238, 249, 210, 253, 136, 83, 71, 134, 49, 114, 190, 189, 242, 78, 100, 238, 101, 84, 204, 176, 198, 25, 139, 151, 60, 84, 51, 126, 192, 192})
+ err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 68, 52, 33, 210, 160, 189, 217, 255, 78, 37, 196, 217, 94, 247, 166, 169, 224, 199, 102, 110, 85, 213, 45, 13, 173, 106, 4, 103, 151, 195, 38, 86, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 208, 12, 132, 83, 48, 38, 206, 128, 160, 65, 147, 32, 128, 177, 198, 131, 57, 57, 68, 135, 65, 198, 178, 138, 43, 25, 135, 92, 174, 208, 119, 103, 225, 26, 207, 243, 31, 225, 29, 173, 119, 192, 192})
+ return err
+}
+func (tm *TestManager) CreateChain2() error {
+ err := tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 58, 253, 98, 206, 198, 181, 152, 223, 201, 116, 197, 154, 111, 104, 54, 113, 249, 184, 246, 15, 226, 142, 187, 47, 138, 60, 201, 66, 226, 237, 29, 7, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 72, 201, 77, 81, 160, 103, 70, 18, 102, 204, 82, 192, 86, 157, 40, 30, 117, 218, 224, 202, 1, 36, 249, 88, 82, 210, 19, 156, 112, 31, 13, 117, 227, 0, 125, 221, 190, 165, 16, 193, 163, 161, 175, 33, 37, 184, 235, 62, 201, 93, 102, 185, 143, 54, 146, 114, 30, 253, 178, 245, 87, 38, 191, 214, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 240, 0, 132, 83, 48, 40, 35, 128, 160, 162, 214, 119, 207, 212, 186, 64, 47, 14, 186, 98, 118, 203, 79, 172, 205, 33, 206, 225, 177, 225, 194, 98, 188, 63, 219, 13, 151, 47, 32, 204, 27, 192, 192})
+ err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 0, 210, 76, 6, 13, 18, 219, 190, 18, 250, 23, 178, 198, 117, 254, 85, 14, 74, 104, 116, 56, 144, 116, 172, 14, 3, 236, 99, 248, 228, 142, 91, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 72, 201, 77, 81, 160, 103, 70, 18, 102, 204, 82, 192, 86, 157, 40, 30, 117, 218, 224, 202, 1, 36, 249, 88, 82, 210, 19, 156, 112, 31, 13, 117, 227, 0, 125, 221, 190, 165, 16, 193, 163, 161, 175, 33, 37, 184, 235, 62, 201, 93, 102, 185, 143, 54, 146, 114, 30, 253, 178, 245, 87, 38, 191, 214, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 255, 252, 132, 83, 48, 40, 74, 128, 160, 185, 20, 138, 2, 210, 15, 71, 144, 89, 167, 94, 155, 148, 118, 170, 157, 122, 70, 70, 114, 50, 221, 231, 8, 132, 167, 115, 239, 44, 245, 41, 226, 192, 192})
+ return err
+}
+
+func TestNegativeBlockChainReorg(t *testing.T) {
+ // We are resetting the database between creation so we need to cache our information
+ testManager2 := NewTestManager()
+ testManager2.CreateChain2()
+ tm2Blocks := testManager2.Blocks
+
+ testManager := NewTestManager()
+ testManager.CreateChain1()
+ oldState := testManager.BlockChain().CurrentBlock.State()
+
+ if testManager.BlockChain().FindCanonicalChain(tm2Blocks, testManager.BlockChain().GenesisBlock().Hash()) != true {
+ t.Error("I expected TestManager to have the longest chain, but it was TestManager2 instead.")
+ }
+ if testManager.BlockChain().CurrentBlock.State() != oldState {
+ t.Error("I expected the top state to be the same as it was as before the reorg")
+ }
+
+}
+
+func TestPositiveBlockChainReorg(t *testing.T) {
+ testManager := NewTestManager()
+ testManager.CreateChain1()
+ tm1Blocks := testManager.Blocks
+
+ testManager2 := NewTestManager()
+ testManager2.CreateChain2()
+ oldState := testManager2.BlockChain().CurrentBlock.State()
+
+ if testManager2.BlockChain().FindCanonicalChain(tm1Blocks, testManager.BlockChain().GenesisBlock().Hash()) == true {
+ t.Error("I expected TestManager to have the longest chain, but it was TestManager2 instead.")
+ }
+ if testManager2.BlockChain().CurrentBlock.State() == oldState {
+ t.Error("I expected the top state to have been modified but it was not")
+ }
+}
diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go
new file mode 100644
index 000000000..3a1e5f510
--- /dev/null
+++ b/ethchain/block_manager_test.go
@@ -0,0 +1,36 @@
+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, _ := ethdb.NewMemDatabase()
+ ethutil.Config.Db = db
+ bm := NewStateManager(nil)
+
+ block := bm.bc.genesisBlock
+ bm.Prepare(block.State(), block.State())
+ script := Compile([]string{
+ "PUSH",
+ "1",
+ "PUSH",
+ "2",
+ })
+ 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})
+}
+*/
diff --git a/ethchain/closure.go b/ethchain/closure.go
new file mode 100644
index 000000000..32b297e90
--- /dev/null
+++ b/ethchain/closure.go
@@ -0,0 +1,129 @@
+package ethchain
+
+// TODO Re write VM to use values instead of big integers?
+
+import (
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+type ClosureRef interface {
+ ReturnGas(*big.Int, *big.Int, *State)
+ Address() []byte
+ GetMem(*big.Int) *ethutil.Value
+ SetStorage(*big.Int, *ethutil.Value)
+ N() *big.Int
+}
+
+// Basic inline closure object which implement the 'closure' interface
+type Closure struct {
+ caller ClosureRef
+ object *StateObject
+ Script []byte
+ State *State
+
+ Gas, UsedGas, Price *big.Int
+
+ Args []byte
+}
+
+// Create a new closure for the given data items
+func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure {
+ c := &Closure{caller: caller, object: object, Script: script, State: state, Args: nil}
+
+ // Gas should be a pointer so it can safely be reduced through the run
+ // This pointer will be off the state transition
+ c.Gas = gas //new(big.Int).Set(gas)
+ // In most cases price and value are pointers to transaction objects
+ // and we don't want the transaction's values to change.
+ c.Price = new(big.Int).Set(price)
+ c.UsedGas = new(big.Int)
+
+ return c
+}
+
+// Retuns the x element in data slice
+func (c *Closure) GetMem(x *big.Int) *ethutil.Value {
+ m := c.object.GetMem(x)
+ if m == nil {
+ return ethutil.EmptyValue()
+ }
+
+ return m
+}
+
+func (c *Closure) Get(x *big.Int) *ethutil.Value {
+ return c.Gets(x, big.NewInt(1))
+}
+
+func (c *Closure) Gets(x, y *big.Int) *ethutil.Value {
+ if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) {
+ return ethutil.NewValue(0)
+ }
+
+ partial := c.Script[x.Int64() : x.Int64()+y.Int64()]
+
+ return ethutil.NewValue(partial)
+}
+
+func (c *Closure) SetStorage(x *big.Int, val *ethutil.Value) {
+ c.object.SetStorage(x, val)
+}
+
+func (c *Closure) Address() []byte {
+ return c.object.Address()
+}
+
+type DebugHook func(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
+
+func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) ([]byte, *big.Int, error) {
+ c.Args = args
+
+ ret, err := vm.RunClosure(c, hook)
+
+ return ret, c.UsedGas, err
+}
+
+func (c *Closure) Return(ret []byte) []byte {
+ // Return the remaining gas to the caller
+ // If no caller is present return it to
+ // the origin (i.e. contract or tx)
+ if c.caller != nil {
+ c.caller.ReturnGas(c.Gas, c.Price, c.State)
+ } else {
+ c.object.ReturnGas(c.Gas, c.Price, c.State)
+ }
+
+ return ret
+}
+
+func (c *Closure) UseGas(gas *big.Int) bool {
+ if c.Gas.Cmp(gas) < 0 {
+ return false
+ }
+
+ // Sub the amount of gas from the remaining
+ c.Gas.Sub(c.Gas, gas)
+ c.UsedGas.Add(c.UsedGas, gas)
+
+ return true
+}
+
+// Implement the caller interface
+func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
+ // Return the gas to the closure
+ c.Gas.Add(c.Gas, gas)
+ c.UsedGas.Sub(c.UsedGas, gas)
+}
+
+func (c *Closure) Object() *StateObject {
+ return c.object
+}
+
+func (c *Closure) Caller() ClosureRef {
+ return c.caller
+}
+
+func (c *Closure) N() *big.Int {
+ return c.object.N()
+}
diff --git a/ethchain/dagger.go b/ethchain/dagger.go
new file mode 100644
index 000000000..08c4826db
--- /dev/null
+++ b/ethchain/dagger.go
@@ -0,0 +1,215 @@
+package ethchain
+
+import (
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/obscuren/sha3"
+ "hash"
+ "math/big"
+ "math/rand"
+ "time"
+)
+
+var powlogger = ethlog.NewLogger("POW")
+
+type PoW interface {
+ Search(block *Block, reactChan chan ethutil.React) []byte
+ Verify(hash []byte, diff *big.Int, nonce []byte) bool
+}
+
+type EasyPow struct {
+ hash *big.Int
+}
+
+func (pow *EasyPow) Search(block *Block, reactChan chan ethutil.React) []byte {
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+ hash := block.HashNoNonce()
+ diff := block.Difficulty
+ i := int64(0)
+ start := time.Now().UnixNano()
+
+ for {
+ select {
+ case <-reactChan:
+ return nil
+ default:
+ i++
+ if i%1234567 == 0 {
+ elapsed := time.Now().UnixNano() - start
+ hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
+ powlogger.Infoln("Hashing @", int64(hashes), "khash")
+ }
+
+ sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
+ if pow.Verify(hash, diff, sha) {
+ return sha
+ }
+ }
+ }
+
+ return nil
+}
+
+func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool {
+ sha := sha3.NewKeccak256()
+
+ d := append(hash, nonce...)
+ sha.Write(d)
+
+ v := ethutil.BigPow(2, 256)
+ ret := new(big.Int).Div(v, diff)
+
+ res := new(big.Int)
+ res.SetBytes(sha.Sum(nil))
+
+ return res.Cmp(ret) == -1
+}
+
+func (pow *EasyPow) SetHash(hash *big.Int) {
+}
+
+type Dagger struct {
+ hash *big.Int
+ xn *big.Int
+}
+
+var Found bool
+
+func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+ for i := 0; i < 1000; i++ {
+ rnd := r.Int63()
+
+ res := dag.Eval(big.NewInt(rnd))
+ powlogger.Infof("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
+ if res.Cmp(obj) < 0 {
+ // Post back result on the channel
+ resChan <- rnd
+ // Notify other threads we've found a valid nonce
+ Found = true
+ }
+
+ // Break out if found
+ if Found {
+ break
+ }
+ }
+
+ resChan <- 0
+}
+
+func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
+ // TODO fix multi threading. Somehow it results in the wrong nonce
+ amountOfRoutines := 1
+
+ dag.hash = hash
+
+ obj := ethutil.BigPow(2, 256)
+ obj = obj.Div(obj, diff)
+
+ Found = false
+ resChan := make(chan int64, 3)
+ var res int64
+
+ for k := 0; k < amountOfRoutines; k++ {
+ go dag.Find(obj, resChan)
+
+ // Wait for each go routine to finish
+ }
+ for k := 0; k < amountOfRoutines; k++ {
+ // Get the result from the channel. 0 = quit
+ if r := <-resChan; r != 0 {
+ res = r
+ }
+ }
+
+ return big.NewInt(res)
+}
+
+func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {
+ dag.hash = hash
+
+ obj := ethutil.BigPow(2, 256)
+ obj = obj.Div(obj, diff)
+
+ return dag.Eval(nonce).Cmp(obj) < 0
+}
+
+func DaggerVerify(hash, diff, nonce *big.Int) bool {
+ dagger := &Dagger{}
+ dagger.hash = hash
+
+ obj := ethutil.BigPow(2, 256)
+ obj = obj.Div(obj, diff)
+
+ return dagger.Eval(nonce).Cmp(obj) < 0
+}
+
+func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
+ if L == i {
+ return dag.hash
+ }
+
+ var m *big.Int
+ if L == 9 {
+ m = big.NewInt(16)
+ } else {
+ m = big.NewInt(3)
+ }
+
+ sha := sha3.NewKeccak256()
+ sha.Reset()
+ d := sha3.NewKeccak256()
+ b := new(big.Int)
+ ret := new(big.Int)
+
+ for k := 0; k < int(m.Uint64()); k++ {
+ d.Reset()
+ d.Write(dag.hash.Bytes())
+ d.Write(dag.xn.Bytes())
+ d.Write(big.NewInt(int64(L)).Bytes())
+ d.Write(big.NewInt(int64(i)).Bytes())
+ d.Write(big.NewInt(int64(k)).Bytes())
+
+ b.SetBytes(Sum(d))
+ pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
+ sha.Write(dag.Node(L-1, pk).Bytes())
+ }
+
+ ret.SetBytes(Sum(sha))
+
+ return ret
+}
+
+func Sum(sha hash.Hash) []byte {
+ //in := make([]byte, 32)
+ return sha.Sum(nil)
+}
+
+func (dag *Dagger) Eval(N *big.Int) *big.Int {
+ pow := ethutil.BigPow(2, 26)
+ dag.xn = pow.Div(N, pow)
+
+ sha := sha3.NewKeccak256()
+ sha.Reset()
+ ret := new(big.Int)
+
+ for k := 0; k < 4; k++ {
+ d := sha3.NewKeccak256()
+ b := new(big.Int)
+
+ d.Reset()
+ d.Write(dag.hash.Bytes())
+ d.Write(dag.xn.Bytes())
+ d.Write(N.Bytes())
+ d.Write(big.NewInt(int64(k)).Bytes())
+
+ b.SetBytes(Sum(d))
+ pk := (b.Uint64() & 0x1ffffff)
+
+ sha.Write(dag.Node(9, pk).Bytes())
+ }
+
+ return ret.SetBytes(Sum(sha))
+}
diff --git a/ethchain/dagger_test.go b/ethchain/dagger_test.go
new file mode 100644
index 000000000..9d4e03c92
--- /dev/null
+++ b/ethchain/dagger_test.go
@@ -0,0 +1,18 @@
+package ethchain
+
+import (
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "testing"
+)
+
+func BenchmarkDaggerSearch(b *testing.B) {
+ hash := big.NewInt(0)
+ diff := ethutil.BigPow(2, 36)
+ o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity
+
+ // Reset timer so the big generation isn't included in the benchmark
+ b.ResetTimer()
+ // Validate
+ DaggerVerify(hash, diff, o)
+}
diff --git a/ethchain/error.go b/ethchain/error.go
new file mode 100644
index 000000000..2cf09a1ec
--- /dev/null
+++ b/ethchain/error.go
@@ -0,0 +1,98 @@
+package ethchain
+
+import (
+ "fmt"
+ "math/big"
+)
+
+// Parent error. In case a parent is unknown this error will be thrown
+// by the block manager
+type ParentErr struct {
+ Message string
+}
+
+func (err *ParentErr) Error() string {
+ return err.Message
+}
+
+func ParentError(hash []byte) error {
+ return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)}
+}
+
+func IsParentErr(err error) bool {
+ _, ok := err.(*ParentErr)
+
+ return ok
+}
+
+// Block validation error. If any validation fails, this error will be thrown
+type ValidationErr struct {
+ Message string
+}
+
+func (err *ValidationErr) Error() string {
+ return err.Message
+}
+
+func ValidationError(format string, v ...interface{}) *ValidationErr {
+ return &ValidationErr{Message: fmt.Sprintf(format, v...)}
+}
+
+func IsValidationErr(err error) bool {
+ _, ok := err.(*ValidationErr)
+
+ return ok
+}
+
+type GasLimitErr struct {
+ Message string
+ Is, Max *big.Int
+}
+
+func IsGasLimitErr(err error) bool {
+ _, ok := err.(*GasLimitErr)
+
+ return ok
+}
+func (err *GasLimitErr) Error() string {
+ return err.Message
+}
+func GasLimitError(is, max *big.Int) *GasLimitErr {
+ return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
+}
+
+type NonceErr struct {
+ Message string
+ Is, Exp uint64
+}
+
+func (err *NonceErr) Error() string {
+ return err.Message
+}
+
+func NonceError(is, exp uint64) *NonceErr {
+ return &NonceErr{Message: fmt.Sprintf("Nonce err. Is %d, expected %d", is, exp), Is: is, Exp: exp}
+}
+
+func IsNonceErr(err error) bool {
+ _, ok := err.(*NonceErr)
+
+ return ok
+}
+
+type OutOfGasErr struct {
+ Message string
+}
+
+func OutOfGasError() *OutOfGasErr {
+ return &OutOfGasErr{Message: "Out of gas"}
+}
+func (self *OutOfGasErr) Error() string {
+ return self.Message
+}
+
+func IsOutOfGasErr(err error) bool {
+ _, ok := err.(*OutOfGasErr)
+
+ return ok
+}
diff --git a/ethchain/fees.go b/ethchain/fees.go
new file mode 100644
index 000000000..743be86a2
--- /dev/null
+++ b/ethchain/fees.go
@@ -0,0 +1,9 @@
+package ethchain
+
+import (
+ "math/big"
+)
+
+var BlockReward *big.Int = big.NewInt(1.5e+18)
+var UncleReward *big.Int = big.NewInt(1.125e+18)
+var UncleInclusionReward *big.Int = big.NewInt(1.875e+17)
diff --git a/ethchain/genesis.go b/ethchain/genesis.go
new file mode 100644
index 000000000..359c47c26
--- /dev/null
+++ b/ethchain/genesis.go
@@ -0,0 +1,45 @@
+package ethchain
+
+import (
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+/*
+ * This is the special genesis block.
+ */
+
+var ZeroHash256 = make([]byte, 32)
+var ZeroHash160 = make([]byte, 20)
+var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{}))
+
+var GenesisHeader = []interface{}{
+ // Previous hash (none)
+ ZeroHash256,
+ // Sha of uncles
+ ethutil.Sha3Bin(ethutil.Encode([]interface{}{})),
+ // Coinbase
+ ZeroHash160,
+ // Root state
+ "",
+ // tx sha
+ "",
+ // Difficulty
+ ethutil.BigPow(2, 22),
+ // Number
+ ethutil.Big0,
+ // Block minimum gas price
+ ethutil.Big0,
+ // Block upper gas bound
+ big.NewInt(1000000),
+ // Block gas used
+ ethutil.Big0,
+ // Time
+ ethutil.Big0,
+ // Extra
+ nil,
+ // Nonce
+ ethutil.Sha3Bin(big.NewInt(42).Bytes()),
+}
+
+var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}
diff --git a/ethchain/stack.go b/ethchain/stack.go
new file mode 100644
index 000000000..a9fa2e522
--- /dev/null
+++ b/ethchain/stack.go
@@ -0,0 +1,150 @@
+package ethchain
+
+import (
+ "fmt"
+ "math"
+ "math/big"
+)
+
+type OpType int
+
+const (
+ tNorm = iota
+ tData
+ tExtro
+ tCrypto
+)
+
+type TxCallback func(opType OpType) bool
+
+// Simple push/pop stack mechanism
+type Stack struct {
+ data []*big.Int
+}
+
+func NewStack() *Stack {
+ return &Stack{}
+}
+
+func (st *Stack) Data() []*big.Int {
+ return st.data
+}
+
+func (st *Stack) Len() int {
+ return len(st.data)
+}
+
+func (st *Stack) Pop() *big.Int {
+ str := st.data[len(st.data)-1]
+
+ copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1])
+ st.data = st.data[:len(st.data)-1]
+
+ return str
+}
+
+func (st *Stack) Popn() (*big.Int, *big.Int) {
+ ints := st.data[len(st.data)-2:]
+
+ copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2])
+ st.data = st.data[:len(st.data)-2]
+
+ return ints[0], ints[1]
+}
+
+func (st *Stack) Peek() *big.Int {
+ str := st.data[len(st.data)-1]
+
+ return str
+}
+
+func (st *Stack) Peekn() (*big.Int, *big.Int) {
+ ints := st.data[:2]
+
+ return ints[0], ints[1]
+}
+
+func (st *Stack) Push(d *big.Int) {
+ st.data = append(st.data, new(big.Int).Set(d))
+}
+
+func (st *Stack) Get(amount *big.Int) []*big.Int {
+ // offset + size <= len(data)
+ length := big.NewInt(int64(len(st.data)))
+ if amount.Cmp(length) <= 0 {
+ start := new(big.Int).Sub(length, amount)
+ return st.data[start.Int64():length.Int64()]
+ }
+
+ return nil
+}
+
+func (st *Stack) Print() {
+ fmt.Println("### stack ###")
+ if len(st.data) > 0 {
+ for i, val := range st.data {
+ fmt.Printf("%-3d %v\n", i, val)
+ }
+ } else {
+ fmt.Println("-- empty --")
+ }
+ fmt.Println("#############")
+}
+
+type Memory struct {
+ store []byte
+}
+
+func (m *Memory) Set(offset, size int64, value []byte) {
+ totSize := offset + size
+ lenSize := int64(len(m.store) - 1)
+ if totSize > lenSize {
+ // Calculate the diff between the sizes
+ diff := totSize - lenSize
+ if diff > 0 {
+ // Create a new empty slice and append it
+ newSlice := make([]byte, diff-1)
+ // Resize slice
+ m.store = append(m.store, newSlice...)
+ }
+ }
+ copy(m.store[offset:offset+size], value)
+}
+
+func (m *Memory) Resize(size uint64) {
+ if uint64(m.Len()) < size {
+ m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
+ }
+}
+
+func (m *Memory) Get(offset, size int64) []byte {
+ if len(m.store) > int(offset) {
+ end := int(math.Min(float64(len(m.store)), float64(offset+size)))
+
+ return m.store[offset:end]
+ }
+
+ return nil
+}
+
+func (m *Memory) Len() int {
+ return len(m.store)
+}
+
+func (m *Memory) Data() []byte {
+ return m.store
+}
+
+func (m *Memory) Print() {
+ fmt.Printf("### mem %d bytes ###\n", len(m.store))
+ if len(m.store) > 0 {
+ addr := 0
+ for i := 0; i+32 <= len(m.store); i += 32 {
+ fmt.Printf("%03d: % x\n", addr, m.store[i:i+32])
+ addr++
+ }
+ } else {
+ fmt.Println("-- empty --")
+ }
+ fmt.Println("####################")
+}
diff --git a/ethchain/state.go b/ethchain/state.go
new file mode 100644
index 000000000..06a185f59
--- /dev/null
+++ b/ethchain/state.go
@@ -0,0 +1,206 @@
+package ethchain
+
+import (
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+// States within the ethereum protocol are used to store anything
+// within the merkle trie. States take care of caching and storing
+// nested states. It's the general query interface to retrieve:
+// * Contracts
+// * Accounts
+type State struct {
+ // The trie for this structure
+ trie *ethutil.Trie
+
+ stateObjects map[string]*StateObject
+
+ manifest *Manifest
+}
+
+// Create a new state from a given trie
+func NewState(trie *ethutil.Trie) *State {
+ return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
+}
+
+// Resets the trie and all siblings
+func (s *State) Reset() {
+ s.trie.Undo()
+
+ // Reset all nested states
+ for _, stateObject := range s.stateObjects {
+ if stateObject.state == nil {
+ continue
+ }
+
+ stateObject.state.Reset()
+ }
+
+ s.Empty()
+}
+
+// Syncs the trie and all siblings
+func (s *State) Sync() {
+ // Sync all nested states
+ for _, stateObject := range s.stateObjects {
+ s.UpdateStateObject(stateObject)
+
+ if stateObject.state == nil {
+ continue
+ }
+
+ stateObject.state.Sync()
+ }
+
+ s.trie.Sync()
+
+ s.Empty()
+}
+
+func (self *State) Empty() {
+ self.stateObjects = make(map[string]*StateObject)
+}
+
+func (self *State) Update() {
+ for _, stateObject := range self.stateObjects {
+ self.UpdateStateObject(stateObject)
+ }
+}
+
+// Purges the current trie.
+func (s *State) Purge() int {
+ return s.trie.NewIterator().Purge()
+}
+
+func (s *State) EachStorage(cb ethutil.EachCallback) {
+ it := s.trie.NewIterator()
+ it.Each(cb)
+}
+
+func (self *State) ResetStateObject(stateObject *StateObject) {
+ delete(self.stateObjects, string(stateObject.Address()))
+
+ stateObject.state.Reset()
+}
+
+func (self *State) UpdateStateObject(stateObject *StateObject) {
+ addr := stateObject.Address()
+
+ if self.stateObjects[string(addr)] == nil {
+ self.stateObjects[string(addr)] = stateObject
+ }
+
+ ethutil.Config.Db.Put(ethutil.Sha3Bin(stateObject.Script()), stateObject.Script())
+
+ self.trie.Update(string(addr), string(stateObject.RlpEncode()))
+
+ self.manifest.AddObjectChange(stateObject)
+}
+
+func (self *State) GetStateObject(addr []byte) *StateObject {
+ stateObject := self.stateObjects[string(addr)]
+ if stateObject != nil {
+ return stateObject
+ }
+
+ data := self.trie.Get(string(addr))
+ if len(data) == 0 {
+ return nil
+ }
+
+ stateObject = NewStateObjectFromBytes(addr, []byte(data))
+ self.stateObjects[string(addr)] = stateObject
+
+ return stateObject
+}
+
+func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
+ stateObject := self.GetStateObject(addr)
+ if stateObject == nil {
+ stateObject = self.NewStateObject(addr)
+ }
+
+ return stateObject
+}
+
+func (self *State) NewStateObject(addr []byte) *StateObject {
+ //statelogger.Infof("(+) %x\n", addr)
+
+ stateObject := NewStateObject(addr)
+ self.stateObjects[string(addr)] = stateObject
+
+ return stateObject
+}
+
+func (self *State) GetAccount(addr []byte) *StateObject {
+ return self.GetOrNewStateObject(addr)
+}
+
+func (s *State) Cmp(other *State) bool {
+ return s.trie.Cmp(other.trie)
+}
+
+func (self *State) Copy() *State {
+ if self.trie != nil {
+ state := NewState(self.trie.Copy())
+ for k, stateObject := range self.stateObjects {
+ state.stateObjects[k] = stateObject.Copy()
+ }
+
+ return state
+ }
+
+ return nil
+}
+
+func (self *State) Set(state *State) {
+ //s.trie = snapshot.trie
+ //s.stateObjects = snapshot.stateObjects
+ self = state
+}
+
+func (s *State) Put(key, object []byte) {
+ s.trie.Update(string(key), string(object))
+}
+
+func (s *State) Root() interface{} {
+ return s.trie.Root
+}
+
+// Object manifest
+//
+// The object manifest is used to keep changes to the state so we can keep track of the changes
+// that occurred during a state transitioning phase.
+type Manifest struct {
+ // XXX These will be handy in the future. Not important for now.
+ objectAddresses map[string]bool
+ storageAddresses map[string]map[string]bool
+
+ objectChanges map[string]*StateObject
+ storageChanges map[string]map[string]*big.Int
+}
+
+func NewManifest() *Manifest {
+ m := &Manifest{objectAddresses: make(map[string]bool), storageAddresses: make(map[string]map[string]bool)}
+ m.Reset()
+
+ return m
+}
+
+func (m *Manifest) Reset() {
+ m.objectChanges = make(map[string]*StateObject)
+ m.storageChanges = make(map[string]map[string]*big.Int)
+}
+
+func (m *Manifest) AddObjectChange(stateObject *StateObject) {
+ m.objectChanges[string(stateObject.Address())] = stateObject
+}
+
+func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) {
+ if m.storageChanges[string(stateObject.Address())] == nil {
+ m.storageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
+ }
+
+ m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage
+}
diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go
new file mode 100644
index 000000000..b0550607f
--- /dev/null
+++ b/ethchain/state_manager.go
@@ -0,0 +1,355 @@
+package ethchain
+
+import (
+ "bytes"
+ "container/list"
+ "fmt"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "math/big"
+ "sync"
+ "time"
+)
+
+var statelogger = ethlog.NewLogger("STATE")
+
+type BlockProcessor interface {
+ ProcessBlock(block *Block)
+}
+
+type Peer interface {
+ Inbound() bool
+ LastSend() time.Time
+ LastPong() int64
+ Host() []byte
+ Port() uint16
+ Version() string
+ PingTime() string
+ Connected() *int32
+}
+
+type EthManager interface {
+ StateManager() *StateManager
+ BlockChain() *BlockChain
+ TxPool() *TxPool
+ Broadcast(msgType ethwire.MsgType, data []interface{})
+ Reactor() *ethutil.ReactorEngine
+ PeerCount() int
+ IsMining() bool
+ IsListening() bool
+ Peers() *list.List
+}
+
+type StateManager struct {
+ // Mutex for locking the block processor. Blocks can only be handled one at a time
+ mutex sync.Mutex
+ // Canonical block chain
+ bc *BlockChain
+ // Stack for processing contracts
+ stack *Stack
+ // non-persistent key/value memory storage
+ mem map[string]*big.Int
+ // Proof of work used for validating
+ Pow PoW
+ // The ethereum manager interface
+ Ethereum EthManager
+ // The managed states
+ // Transiently state. The trans state isn't ever saved, validated and
+ // it could be used for setting account nonces without effecting
+ // the main states.
+ transState *State
+ // Mining state. The mining state is used purely and solely by the mining
+ // operation.
+ miningState *State
+}
+
+func NewStateManager(ethereum EthManager) *StateManager {
+ sm := &StateManager{
+ stack: NewStack(),
+ mem: make(map[string]*big.Int),
+ Pow: &EasyPow{},
+ Ethereum: ethereum,
+ bc: ethereum.BlockChain(),
+ }
+ sm.transState = ethereum.BlockChain().CurrentBlock.State().Copy()
+ sm.miningState = ethereum.BlockChain().CurrentBlock.State().Copy()
+
+ return sm
+}
+
+func (sm *StateManager) CurrentState() *State {
+ return sm.Ethereum.BlockChain().CurrentBlock.State()
+}
+
+func (sm *StateManager) TransState() *State {
+ return sm.transState
+}
+
+func (sm *StateManager) MiningState() *State {
+ return sm.miningState
+}
+
+func (sm *StateManager) NewMiningState() *State {
+ sm.miningState = sm.Ethereum.BlockChain().CurrentBlock.State().Copy()
+
+ return sm.miningState
+}
+
+func (sm *StateManager) BlockChain() *BlockChain {
+ return sm.bc
+}
+
+func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
+ var (
+ receipts Receipts
+ handled, unhandled Transactions
+ totalUsedGas = big.NewInt(0)
+ err error
+ )
+
+done:
+ for i, tx := range txs {
+ txGas := new(big.Int).Set(tx.Gas)
+ st := NewStateTransition(coinbase, tx, state, block)
+ err = st.TransitionState()
+ if err != nil {
+ switch {
+ case IsNonceErr(err):
+ err = nil // ignore error
+ continue
+ case IsGasLimitErr(err):
+ unhandled = txs[i:]
+
+ break done
+ default:
+ statelogger.Infoln(err)
+ err = nil
+ //return nil, nil, nil, err
+ }
+ }
+
+ // Notify all subscribers
+ self.Ethereum.Reactor().Post("newTx:post", tx)
+
+ // Update the state with pending changes
+ state.Update()
+
+ txGas.Sub(txGas, st.gas)
+ accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
+ receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative}
+
+ receipts = append(receipts, receipt)
+ handled = append(handled, tx)
+ }
+
+ parent.GasUsed = totalUsedGas
+
+ return receipts, handled, unhandled, err
+}
+
+func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
+ // Processing a blocks may never happen simultaneously
+ sm.mutex.Lock()
+ defer sm.mutex.Unlock()
+
+ if sm.bc.HasBlock(block.Hash()) {
+ return nil
+ }
+
+ if !sm.bc.HasBlock(block.PrevHash) {
+ return ParentError(block.PrevHash)
+ }
+
+ var (
+ parent = sm.bc.GetBlock(block.PrevHash)
+ state = parent.State()
+ )
+
+ // 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 state.Reset()
+
+ receipts, err := sm.ApplyDiff(state, parent, block)
+ defer func() {
+ if err != nil {
+ if len(receipts) == len(block.Receipts()) {
+ for i, receipt := range block.Receipts() {
+ statelogger.Debugf("diff (r) %v ~ %x <=> (c) %v ~ %x (%x)\n", receipt.CumulativeGasUsed, receipt.PostState[0:4], receipts[i].CumulativeGasUsed, receipts[i].PostState[0:4], receipt.Tx.Hash())
+ }
+ } else {
+ statelogger.Warnln("Unable to print receipt diff. Length didn't match", len(receipts), "for", len(block.Receipts()))
+ }
+ }
+ }()
+
+ if err != nil {
+ return err
+ }
+
+ // Block validation
+ if err = sm.ValidateBlock(block); err != nil {
+ statelogger.Errorln("Error validating block:", err)
+ return err
+ }
+
+ // I'm not sure, but I don't know if there should be thrown
+ // any errors at this time.
+ if err = sm.AccumelateRewards(state, block); err != nil {
+ statelogger.Errorln("Error accumulating reward", err)
+ return err
+ }
+
+ if !block.State().Cmp(state) {
+ err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root)
+ return
+ }
+
+ // Calculate the new total difficulty and sync back to the db
+ if sm.CalculateTD(block) {
+ // Sync the current block's state to the database and cancelling out the deferred Undo
+ state.Sync()
+
+ // Add the block to the chain
+ sm.bc.Add(block)
+ sm.notifyChanges(state)
+
+ statelogger.Infof("Added block #%d (%x)\n", block.Number, block.Hash())
+ if dontReact == false {
+ sm.Ethereum.Reactor().Post("newBlock", block)
+
+ state.manifest.Reset()
+ }
+
+ sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
+
+ sm.Ethereum.TxPool().RemoveInvalid(state)
+ } else {
+ statelogger.Errorln("total diff failed")
+ }
+
+ return nil
+}
+
+func (sm *StateManager) ApplyDiff(state *State, parent, block *Block) (receipts Receipts, err error) {
+ coinbase := state.GetOrNewStateObject(block.Coinbase)
+ coinbase.SetGasPool(block.CalcGasLimit(parent))
+
+ // Process the transactions on to current block
+ receipts, _, _, err = sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions())
+ if err != nil {
+ return nil, err
+ }
+
+ return receipts, nil
+}
+
+func (sm *StateManager) CalculateTD(block *Block) bool {
+ uncleDiff := new(big.Int)
+ for _, uncle := range block.Uncles {
+ uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
+ }
+
+ // TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
+ td := new(big.Int)
+ td = td.Add(sm.bc.TD, uncleDiff)
+ td = td.Add(td, block.Difficulty)
+
+ // The new TD will only be accepted if the new difficulty is
+ // is greater than the previous.
+ if td.Cmp(sm.bc.TD) > 0 {
+ // Set the new total difficulty back to the block chain
+ sm.bc.SetTotalDifficulty(td)
+
+ return true
+ }
+
+ return false
+}
+
+// Validates the current block. Returns an error if the block was invalid,
+// an uncle or anything that isn't on the current block chain.
+// Validation validates easy over difficult (dagger takes longer time = difficult)
+func (sm *StateManager) ValidateBlock(block *Block) error {
+ // TODO
+ // 2. Check if the difficulty is correct
+
+ // Check each uncle's previous hash. In order for it to be valid
+ // is if it has the same block hash as the current
+ previousBlock := sm.bc.GetBlock(block.PrevHash)
+ for _, uncle := range block.Uncles {
+ if bytes.Compare(uncle.PrevHash, previousBlock.PrevHash) != 0 {
+ return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x", previousBlock.PrevHash, uncle.PrevHash)
+ }
+ }
+
+ diff := block.Time - sm.bc.CurrentBlock.Time
+ if diff < 0 {
+ return ValidationError("Block timestamp less then prev block %v", diff)
+ }
+
+ /* XXX
+ // New blocks must be within the 15 minute range of the last block.
+ if diff > int64(15*time.Minute) {
+ return ValidationError("Block is too far in the future of last block (> 15 minutes)")
+ }
+ */
+
+ // Verify the nonce of the block. Return an error if it's not valid
+ if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) {
+ return ValidationError("Block's nonce is invalid (= %v)", ethutil.Hex(block.Nonce))
+ }
+
+ 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 (sm *StateManager) AccumelateRewards(state *State, block *Block) error {
+ // Get the account associated with the coinbase
+ account := state.GetAccount(block.Coinbase)
+ // Reward amount of ether to the coinbase address
+ account.AddAmount(CalculateBlockReward(block, len(block.Uncles)))
+
+ addr := make([]byte, len(block.Coinbase))
+ copy(addr, block.Coinbase)
+ state.UpdateStateObject(account)
+
+ for _, uncle := range block.Uncles {
+ uncleAccount := state.GetAccount(uncle.Coinbase)
+ uncleAccount.AddAmount(CalculateUncleReward(uncle))
+
+ state.UpdateStateObject(uncleAccount)
+ }
+
+ return nil
+}
+
+func (sm *StateManager) Stop() {
+ sm.bc.Stop()
+}
+
+func (sm *StateManager) notifyChanges(state *State) {
+ for addr, stateObject := range state.manifest.objectChanges {
+ sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
+ }
+
+ for stateObjectAddr, mappedObjects := range state.manifest.storageChanges {
+ for addr, value := range mappedObjects {
+ sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &StorageState{[]byte(stateObjectAddr), []byte(addr), value})
+ }
+ }
+}
diff --git a/ethchain/state_object.go b/ethchain/state_object.go
new file mode 100644
index 000000000..edac4f6dc
--- /dev/null
+++ b/ethchain/state_object.go
@@ -0,0 +1,273 @@
+package ethchain
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "strings"
+)
+
+type Code []byte
+
+func (self Code) String() string {
+ return strings.Join(Disassemble(self), " ")
+}
+
+type StateObject struct {
+ // Address of the object
+ address []byte
+ // Shared attributes
+ Amount *big.Int
+ ScriptHash []byte
+ Nonce uint64
+ // Contract related attributes
+ state *State
+ script Code
+ initScript Code
+
+ // Total gas pool is the total amount of gas currently
+ // left if this object is the coinbase. Gas is directly
+ // purchased of the coinbase.
+ gasPool *big.Int
+}
+
+// Converts an transaction in to a state object
+func MakeContract(tx *Transaction, state *State) *StateObject {
+ // Create contract if there's no recipient
+ if tx.IsContract() {
+ addr := tx.CreationAddress()
+
+ contract := state.NewStateObject(addr)
+ contract.initScript = tx.Data
+ contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, ""))
+
+ return contract
+ }
+
+ return nil
+}
+
+func NewStateObject(addr []byte) *StateObject {
+ object := &StateObject{address: addr, Amount: new(big.Int), gasPool: new(big.Int)}
+ object.state = NewState(ethutil.NewTrie(ethutil.Config.Db, ""))
+
+ return object
+}
+
+func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
+ contract := &StateObject{address: address, Amount: Amount, Nonce: 0}
+ contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root)))
+
+ return contract
+}
+
+// Returns a newly created account
+func NewAccount(address []byte, amount *big.Int) *StateObject {
+ account := &StateObject{address: address, Amount: amount, Nonce: 0}
+
+ return account
+}
+
+func NewStateObjectFromBytes(address, data []byte) *StateObject {
+ object := &StateObject{address: address}
+ object.RlpDecode(data)
+
+ return object
+}
+
+func (c *StateObject) State() *State {
+ return c.state
+}
+
+func (c *StateObject) N() *big.Int {
+ return big.NewInt(int64(c.Nonce))
+}
+
+func (c *StateObject) Addr(addr []byte) *ethutil.Value {
+ return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr))))
+}
+
+func (c *StateObject) SetAddr(addr []byte, value interface{}) {
+ c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
+}
+
+func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) {
+ addr := ethutil.BigToBytes(num, 256)
+
+ if val.BigInt().Cmp(ethutil.Big0) == 0 {
+ c.state.trie.Delete(string(addr))
+
+ return
+ }
+
+ c.SetAddr(addr, val)
+}
+
+func (c *StateObject) GetStorage(num *big.Int) *ethutil.Value {
+ nb := ethutil.BigToBytes(num, 256)
+
+ return c.Addr(nb)
+}
+
+/* DEPRECATED */
+func (c *StateObject) GetMem(num *big.Int) *ethutil.Value {
+ return c.GetStorage(num)
+}
+
+func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
+ if int64(len(c.script)-1) < pc.Int64() {
+ return ethutil.NewValue(0)
+ }
+
+ return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]})
+}
+
+// Return the gas back to the origin. Used by the Virtual machine or Closures
+func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
+ /*
+ remainder := new(big.Int).Mul(gas, price)
+ c.AddAmount(remainder)
+ */
+}
+
+func (c *StateObject) AddAmount(amount *big.Int) {
+ c.SetAmount(new(big.Int).Add(c.Amount, amount))
+
+ statelogger.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount)
+}
+
+func (c *StateObject) SubAmount(amount *big.Int) {
+ c.SetAmount(new(big.Int).Sub(c.Amount, amount))
+
+ statelogger.Infof("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount)
+}
+
+func (c *StateObject) SetAmount(amount *big.Int) {
+ c.Amount = amount
+}
+
+func (c *StateObject) ConvertGas(gas, price *big.Int) error {
+ total := new(big.Int).Mul(gas, price)
+ if total.Cmp(c.Amount) > 0 {
+ return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total)
+ }
+
+ c.SubAmount(total)
+
+ return nil
+}
+
+func (self *StateObject) SetGasPool(gasLimit *big.Int) {
+ self.gasPool = new(big.Int).Set(gasLimit)
+
+ statelogger.DebugDetailf("%x: fuel (+ %v)", self.Address(), self.gasPool)
+}
+
+func (self *StateObject) BuyGas(gas, price *big.Int) error {
+ if self.gasPool.Cmp(gas) < 0 {
+ return GasLimitError(self.gasPool, gas)
+ }
+
+ rGas := new(big.Int).Set(gas)
+ rGas.Mul(rGas, price)
+
+ self.AddAmount(rGas)
+
+ return nil
+}
+
+func (self *StateObject) RefundGas(gas, price *big.Int) {
+ self.gasPool.Add(self.gasPool, gas)
+
+ rGas := new(big.Int).Set(gas)
+ rGas.Mul(rGas, price)
+
+ self.Amount.Sub(self.Amount, rGas)
+}
+
+func (self *StateObject) Copy() *StateObject {
+ stateObject := NewStateObject(self.Address())
+ stateObject.Amount.Set(self.Amount)
+ stateObject.ScriptHash = ethutil.CopyBytes(self.ScriptHash)
+ stateObject.Nonce = self.Nonce
+ if self.state != nil {
+ stateObject.state = self.state.Copy()
+ }
+ stateObject.script = ethutil.CopyBytes(self.script)
+ stateObject.initScript = ethutil.CopyBytes(self.initScript)
+ //stateObject.gasPool.Set(self.gasPool)
+
+ return self
+}
+
+func (self *StateObject) Set(stateObject *StateObject) {
+ self = stateObject
+}
+
+/*
+func (self *StateObject) Copy() *StateObject {
+ stCopy := &StateObject{}
+ stCopy.address = make([]byte, len(self.address))
+ copy(stCopy.address, self.address)
+ stCopy.Amount = new(big.Int).Set(self.Amount)
+ stCopy.ScriptHash = make([]byte, len(self.ScriptHash))
+ copy(stCopy.ScriptHash, self.ScriptHash)
+ stCopy.Nonce = self.Nonce
+ if self.state != nil {
+ stCopy.state = self.state.Copy()
+ }
+ stCopy.script = make([]byte, len(self.script))
+ copy(stCopy.script, self.script)
+ stCopy.initScript = make([]byte, len(self.initScript))
+ copy(stCopy.initScript, self.initScript)
+
+ return stCopy
+}
+*/
+
+// Returns the address of the contract/account
+func (c *StateObject) Address() []byte {
+ return c.address
+}
+
+// Returns the main script body
+func (c *StateObject) Script() Code {
+ return c.script
+}
+
+// Returns the initialization script
+func (c *StateObject) Init() Code {
+ return c.initScript
+}
+
+// State object encoding methods
+func (c *StateObject) RlpEncode() []byte {
+ var root interface{}
+ if c.state != nil {
+ root = c.state.trie.Root
+ } else {
+ root = ""
+ }
+
+ return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethutil.Sha3Bin(c.script)})
+}
+
+func (c *StateObject) RlpDecode(data []byte) {
+ decoder := ethutil.NewValueFromBytes(data)
+
+ c.Nonce = decoder.Get(0).Uint()
+ c.Amount = decoder.Get(1).BigInt()
+ c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
+
+ c.ScriptHash = decoder.Get(3).Bytes()
+
+ c.script, _ = ethutil.Config.Db.Get(c.ScriptHash)
+}
+
+// Storage change object. Used by the manifest for notifying changes to
+// the sub channels.
+type StorageState struct {
+ StateAddress []byte
+ Address []byte
+ Value *big.Int
+}
diff --git a/ethchain/state_object_test.go b/ethchain/state_object_test.go
new file mode 100644
index 000000000..2588100d0
--- /dev/null
+++ b/ethchain/state_object_test.go
@@ -0,0 +1,52 @@
+package ethchain
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "testing"
+)
+
+func TestSync(t *testing.T) {
+ ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
+
+ contract.script = []byte{42}
+
+ state.UpdateStateObject(contract)
+ state.Sync()
+
+ object := state.GetStateObject([]byte("aa"))
+ if len(object.Script()) == 0 {
+ t.Fail()
+ }
+}
+
+func TestObjectGet(t *testing.T) {
+ ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
+
+ db, _ := ethdb.NewMemDatabase()
+ ethutil.Config.Db = db
+
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
+ state.UpdateStateObject(contract)
+
+ contract = state.GetStateObject([]byte("aa"))
+ contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello"))
+ o := contract.GetMem(big.NewInt(0))
+ fmt.Println(o)
+
+ state.UpdateStateObject(contract)
+ contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello00"))
+
+ contract = state.GetStateObject([]byte("aa"))
+ o = contract.GetMem(big.NewInt(0))
+ fmt.Println("after", o)
+}
diff --git a/ethchain/state_test.go b/ethchain/state_test.go
new file mode 100644
index 000000000..95be0f373
--- /dev/null
+++ b/ethchain/state_test.go
@@ -0,0 +1,30 @@
+package ethchain
+
+import (
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethutil"
+ "testing"
+)
+
+func TestSnapshot(t *testing.T) {
+ ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ stateObject := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
+ state.UpdateStateObject(stateObject)
+ stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42))
+
+ snapshot := state.Copy()
+
+ stateObject = state.GetStateObject([]byte("aa"))
+ stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(43))
+
+ state.Set(snapshot)
+
+ stateObject = state.GetStateObject([]byte("aa"))
+ if !stateObject.GetStorage(ethutil.Big("0")).Cmp(ethutil.NewValue(42)) {
+ t.Error("Expected storage 0 to be 42")
+ }
+}
diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go
new file mode 100644
index 000000000..94c3de3d9
--- /dev/null
+++ b/ethchain/state_transition.go
@@ -0,0 +1,302 @@
+package ethchain
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+/*
+ * The State transitioning model
+ *
+ * A state transition is a change made when a transaction is applied to the current world state
+ * The state transitioning model does all all the necessary work to work out a valid new state root.
+ * 1) Nonce handling
+ * 2) Pre pay / buy gas of the coinbase (miner)
+ * 3) Create a new state object if the recipient is \0*32
+ * 4) Value transfer
+ * == If contract creation ==
+ * 4a) Attempt to run transaction data
+ * 4b) If valid, use result as code for the new state object
+ * == end ==
+ * 5) Run Script section
+ * 6) Derive new state root
+ */
+type StateTransition struct {
+ coinbase, receiver []byte
+ tx *Transaction
+ gas, gasPrice *big.Int
+ value *big.Int
+ data []byte
+ state *State
+ block *Block
+
+ cb, rec, sen *StateObject
+}
+
+func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition {
+ return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
+}
+
+func (self *StateTransition) Coinbase() *StateObject {
+ if self.cb != nil {
+ return self.cb
+ }
+
+ self.cb = self.state.GetAccount(self.coinbase)
+ return self.cb
+}
+func (self *StateTransition) Sender() *StateObject {
+ if self.sen != nil {
+ return self.sen
+ }
+
+ self.sen = self.state.GetAccount(self.tx.Sender())
+ return self.sen
+}
+func (self *StateTransition) Receiver() *StateObject {
+ if self.tx != nil && self.tx.CreatesContract() {
+ return nil
+ }
+
+ if self.rec != nil {
+ return self.rec
+ }
+
+ self.rec = self.state.GetAccount(self.tx.Recipient)
+ return self.rec
+}
+
+func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject {
+ contract := MakeContract(tx, state)
+
+ return contract
+}
+
+func (self *StateTransition) UseGas(amount *big.Int) error {
+ if self.gas.Cmp(amount) < 0 {
+ return OutOfGasError()
+ }
+ self.gas.Sub(self.gas, amount)
+
+ return nil
+}
+
+func (self *StateTransition) AddGas(amount *big.Int) {
+ self.gas.Add(self.gas, amount)
+}
+
+func (self *StateTransition) BuyGas() error {
+ var err error
+
+ sender := self.Sender()
+ if sender.Amount.Cmp(self.tx.GasValue()) < 0 {
+ return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Amount)
+ }
+
+ coinbase := self.Coinbase()
+ err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
+ if err != nil {
+ return err
+ }
+
+ self.AddGas(self.tx.Gas)
+ sender.SubAmount(self.tx.GasValue())
+
+ return nil
+}
+
+func (self *StateTransition) RefundGas() {
+ coinbase, sender := self.Coinbase(), self.Sender()
+ coinbase.RefundGas(self.gas, self.tx.GasPrice)
+
+ // Return remaining gas
+ remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice)
+ sender.AddAmount(remaining)
+}
+
+func (self *StateTransition) preCheck() (err error) {
+ var (
+ tx = self.tx
+ sender = self.Sender()
+ )
+
+ // Make sure this transaction's nonce is correct
+ if sender.Nonce != tx.Nonce {
+ return NonceError(tx.Nonce, sender.Nonce)
+ }
+
+ // Pre-pay gas / Buy gas of the coinbase account
+ if err = self.BuyGas(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (self *StateTransition) TransitionState() (err error) {
+ statelogger.Infof("(~) %x\n", self.tx.Hash())
+
+ /*
+ defer func() {
+ if r := recover(); r != nil {
+ logger.Infoln(r)
+ err = fmt.Errorf("state transition err %v", r)
+ }
+ }()
+ */
+
+ // XXX Transactions after this point are considered valid.
+ if err = self.preCheck(); err != nil {
+ return
+ }
+
+ var (
+ tx = self.tx
+ sender = self.Sender()
+ receiver *StateObject
+ )
+
+ defer self.RefundGas()
+
+ // Increment the nonce for the next transaction
+ sender.Nonce += 1
+
+ receiver = self.Receiver()
+
+ // Transaction gas
+ if err = self.UseGas(GasTx); err != nil {
+ return
+ }
+
+ // Pay data gas
+ dataPrice := big.NewInt(int64(len(self.data)))
+ dataPrice.Mul(dataPrice, GasData)
+ if err = self.UseGas(dataPrice); err != nil {
+ return
+ }
+
+ // If the receiver is nil it's a contract (\0*32).
+ if receiver == nil {
+ // Create a new state object for the contract
+ receiver = self.MakeStateObject(self.state, tx)
+ if receiver == nil {
+ return fmt.Errorf("Unable to create contract")
+ }
+ }
+
+ // Transfer value from sender to receiver
+ if err = self.transferValue(sender, receiver); err != nil {
+ return
+ }
+
+ // Process the init code and create 'valid' contract
+ if IsContractAddr(self.receiver) {
+ // Evaluate the initialization script
+ // and use the return value as the
+ // script section for the state object.
+ self.data = nil
+
+ code, err, deepErr := self.Eval(receiver.Init(), receiver)
+ if err != nil || deepErr {
+ self.state.ResetStateObject(receiver)
+
+ return fmt.Errorf("Error during init script run %v (deepErr = %v)", err, deepErr)
+ }
+
+ receiver.script = code
+ } else {
+ if len(receiver.Script()) > 0 {
+ var deepErr bool
+ _, err, deepErr = self.Eval(receiver.Script(), receiver)
+ if err != nil {
+ self.state.ResetStateObject(receiver)
+
+ return fmt.Errorf("Error during code execution %v (deepErr = %v)", err, deepErr)
+ }
+ }
+ }
+
+ return
+}
+
+func (self *StateTransition) transferValue(sender, receiver *StateObject) error {
+ if sender.Amount.Cmp(self.value) < 0 {
+ return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
+ }
+
+ // Subtract the amount from the senders account
+ sender.SubAmount(self.value)
+ // Add the amount to receivers account which should conclude this transaction
+ receiver.AddAmount(self.value)
+
+ return nil
+}
+
+func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error, deepErr bool) {
+ var (
+ block = self.block
+ initiator = self.Sender()
+ state = self.state
+ )
+
+ closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice)
+ vm := NewVm(state, nil, RuntimeVars{
+ Origin: initiator.Address(),
+ Block: block,
+ BlockNumber: block.Number,
+ PrevHash: block.PrevHash,
+ Coinbase: block.Coinbase,
+ Time: block.Time,
+ Diff: block.Difficulty,
+ Value: self.value,
+ })
+ vm.Verbose = true
+
+ ret, err, deepErr = Call(vm, closure, self.data)
+
+ return
+}
+
+func Call(vm *Vm, closure *Closure, data []byte) (ret []byte, err error, deepErr bool) {
+ ret, _, err = closure.Call(vm, data, nil)
+ deepErr = vm.err != nil
+
+ Paranoia := ethutil.Config.Paranoia
+ if Paranoia {
+ var (
+ context = closure.object
+ trie = context.state.trie
+ trie2 = ethutil.NewTrie(ethutil.Config.Db, "")
+ )
+
+ trie.NewIterator().Each(func(key string, v *ethutil.Value) {
+ trie2.Update(key, v.Str())
+ })
+
+ a := ethutil.NewValue(trie2.Root).Bytes()
+ b := ethutil.NewValue(context.state.trie.Root).Bytes()
+ if bytes.Compare(a, b) != 0 {
+ // TODO FIXME ASAP
+ context.state.trie = trie2
+ /*
+ statelogger.Debugf("(o): %x\n", trie.Root)
+ trie.NewIterator().Each(func(key string, v *ethutil.Value) {
+ v.Decode()
+ statelogger.Debugf("%x : %x\n", key, v.Str())
+ })
+
+ statelogger.Debugf("(c): %x\n", trie2.Root)
+ trie2.NewIterator().Each(func(key string, v *ethutil.Value) {
+ v.Decode()
+ statelogger.Debugf("%x : %x\n", key, v.Str())
+ })
+ */
+
+ //return nil, fmt.Errorf("PARANOIA: Different state object roots during copy"), false
+ }
+ }
+
+ return
+}
diff --git a/ethchain/transaction.go b/ethchain/transaction.go
new file mode 100644
index 000000000..2ab681030
--- /dev/null
+++ b/ethchain/transaction.go
@@ -0,0 +1,240 @@
+package ethchain
+
+import (
+ "bytes"
+ "fmt"
+ "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}
+
+func IsContractAddr(addr []byte) bool {
+ return bytes.Compare(addr, ContractAddr) == 0
+}
+
+type Transaction struct {
+ Nonce uint64
+ Recipient []byte
+ Value *big.Int
+ Gas *big.Int
+ GasPrice *big.Int
+ Data []byte
+ v byte
+ r, s []byte
+
+ // Indicates whether this tx is a contract creation transaction
+ contractCreation bool
+}
+
+func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
+ return &Transaction{Recipient: ContractAddr, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
+}
+
+func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
+ return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data}
+}
+
+func NewTransactionFromBytes(data []byte) *Transaction {
+ tx := &Transaction{}
+ tx.RlpDecode(data)
+
+ return tx
+}
+
+func NewTransactionFromValue(val *ethutil.Value) *Transaction {
+ tx := &Transaction{}
+ tx.RlpValueDecode(val)
+
+ return tx
+}
+
+func (self *Transaction) GasValue() *big.Int {
+ return new(big.Int).Mul(self.Gas, self.GasPrice)
+}
+
+func (self *Transaction) TotalValue() *big.Int {
+ v := self.GasValue()
+ return v.Add(v, self.Value)
+}
+
+func (tx *Transaction) Hash() []byte {
+ data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
+
+ return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
+}
+
+func (tx *Transaction) CreatesContract() bool {
+ return tx.contractCreation
+}
+
+/* Deprecated */
+func (tx *Transaction) IsContract() bool {
+ return tx.CreatesContract()
+}
+
+func (tx *Transaction) CreationAddress() []byte {
+ return ethutil.Sha3Bin(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:]
+}
+
+func (tx *Transaction) Signature(key []byte) []byte {
+ hash := tx.Hash()
+
+ sig, _ := secp256k1.Sign(hash, key)
+
+ return sig
+}
+
+func (tx *Transaction) PublicKey() []byte {
+ hash := tx.Hash()
+
+ r := make([]byte, 32-len(tx.r))
+ s := make([]byte, 32-len(tx.s))
+ r = append(r, ethutil.CopyBytes(tx.r)...)
+ s = append(s, ethutil.CopyBytes(tx.s)...)
+
+ sig := append(r, s...)
+ sig = append(sig, tx.v-27)
+
+ pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
+
+ return pubkey
+}
+
+func (tx *Transaction) Sender() []byte {
+ pubkey := tx.PublicKey()
+
+ // Validate the returned key.
+ // Return nil if public key isn't in full format
+ if pubkey[0] != 4 {
+ return nil
+ }
+
+ return ethutil.Sha3Bin(pubkey[1:])[12:]
+}
+
+func (tx *Transaction) Sign(privk []byte) error {
+
+ sig := tx.Signature(privk)
+
+ tx.r = sig[:32]
+ tx.s = sig[32:64]
+ tx.v = sig[64] + 27
+
+ return nil
+}
+
+func (tx *Transaction) RlpData() interface{} {
+ data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
+
+ // TODO Remove prefixing zero's
+
+ return append(data, tx.v, tx.r, tx.s)
+}
+
+func (tx *Transaction) RlpValue() *ethutil.Value {
+ return ethutil.NewValue(tx.RlpData())
+}
+
+func (tx *Transaction) RlpEncode() []byte {
+ return tx.RlpValue().Encode()
+}
+
+func (tx *Transaction) RlpDecode(data []byte) {
+ tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
+}
+
+func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
+ tx.Nonce = decoder.Get(0).Uint()
+ tx.GasPrice = decoder.Get(1).BigInt()
+ tx.Gas = decoder.Get(2).BigInt()
+ tx.Recipient = decoder.Get(3).Bytes()
+ tx.Value = decoder.Get(4).BigInt()
+ tx.Data = decoder.Get(5).Bytes()
+ tx.v = byte(decoder.Get(6).Uint())
+
+ tx.r = decoder.Get(7).Bytes()
+ tx.s = decoder.Get(8).Bytes()
+
+ if IsContractAddr(tx.Recipient) {
+ tx.contractCreation = true
+ }
+}
+
+func (tx *Transaction) String() string {
+ return fmt.Sprintf(`
+ TX(%x)
+ Contract: %v
+ From: %x
+ To: %x
+ Nonce: %v
+ GasPrice: %v
+ Gas: %v
+ Value: %v
+ Data: 0x%x
+ V: 0x%x
+ R: 0x%x
+ S: 0x%x
+ `,
+ tx.Hash(),
+ len(tx.Recipient) == 0,
+ tx.Sender(),
+ tx.Recipient,
+ tx.Nonce,
+ tx.GasPrice,
+ tx.Gas,
+ tx.Value,
+ tx.Data,
+ tx.v,
+ tx.r,
+ tx.s)
+}
+
+type Receipt struct {
+ Tx *Transaction
+ PostState []byte
+ CumulativeGasUsed *big.Int
+}
+type Receipts []*Receipt
+
+func NewRecieptFromValue(val *ethutil.Value) *Receipt {
+ r := &Receipt{}
+ r.RlpValueDecode(val)
+
+ return r
+}
+
+func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) {
+ self.Tx = NewTransactionFromValue(decoder.Get(0))
+ self.PostState = decoder.Get(1).Bytes()
+ self.CumulativeGasUsed = decoder.Get(2).BigInt()
+}
+
+func (self *Receipt) RlpData() interface{} {
+ return []interface{}{self.Tx.RlpData(), self.PostState, self.CumulativeGasUsed}
+}
+
+func (self *Receipt) String() string {
+ return fmt.Sprintf(`
+ R
+ Tx:[ %v]
+ PostState: 0x%x
+ CumulativeGasUsed: %v
+ `,
+ self.Tx,
+ self.PostState,
+ self.CumulativeGasUsed)
+}
+
+// Transaction slice type for basic sorting
+type Transactions []*Transaction
+
+func (s Transactions) Len() int { return len(s) }
+func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type TxByNonce struct{ Transactions }
+
+func (s TxByNonce) Less(i, j int) bool {
+ return s.Transactions[i].Nonce < s.Transactions[j].Nonce
+}
diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go
new file mode 100644
index 000000000..6ab8d83d9
--- /dev/null
+++ b/ethchain/transaction_pool.go
@@ -0,0 +1,286 @@
+package ethchain
+
+import (
+ "bytes"
+ "container/list"
+ "fmt"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethwire"
+ "math/big"
+ "sync"
+)
+
+var txplogger = ethlog.NewLogger("TXP")
+
+const (
+ txPoolQueueSize = 50
+)
+
+type TxPoolHook chan *Transaction
+type TxMsgTy byte
+
+const (
+ TxPre = iota
+ TxPost
+ minGasPrice = 1000000
+)
+
+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() {
+ if tx, ok := e.Value.(*Transaction); ok {
+ if finder(tx, e) {
+ return tx
+ }
+ }
+ }
+
+ return nil
+}
+
+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
+// pool is being drained or synced for whatever reason the transactions
+// will simple queue up and handled when the mutex is freed.
+type TxPool struct {
+ Ethereum EthManager
+ // The mutex for accessing the Tx pool.
+ mutex sync.Mutex
+ // Queueing channel for reading and writing incoming
+ // transactions to
+ queueChan chan *Transaction
+ // Quiting channel
+ quit chan bool
+ // The actual pool
+ pool *list.List
+
+ SecondaryProcessor TxProcessor
+
+ subscribers []chan TxMsg
+}
+
+func NewTxPool(ethereum EthManager) *TxPool {
+ return &TxPool{
+ //server: s,
+ mutex: sync.Mutex{},
+ pool: list.New(),
+ queueChan: make(chan *Transaction, txPoolQueueSize),
+ quit: make(chan bool),
+ Ethereum: ethereum,
+ }
+}
+
+// Blocking function. Don't use directly. Use QueueTransaction instead
+func (pool *TxPool) addTransaction(tx *Transaction) {
+ pool.mutex.Lock()
+ defer pool.mutex.Unlock()
+
+ pool.pool.PushBack(tx)
+
+ // Broadcast the transaction to the rest of the peers
+ pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
+}
+
+/*
+// Process transaction validates the Tx and processes funds from the
+// sender to the recipient.
+func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (gas *big.Int, err error) {
+ fmt.Printf("state root before update %x\n", state.Root())
+ defer func() {
+ if r := recover(); r != nil {
+ txplogger.Infoln(r)
+ err = fmt.Errorf("%v", r)
+ }
+ }()
+
+ gas = new(big.Int)
+ addGas := func(g *big.Int) { gas.Add(gas, g) }
+ addGas(GasTx)
+
+ // Get the sender
+ sender := state.GetAccount(tx.Sender())
+
+ if sender.Nonce != tx.Nonce {
+ err = NonceError(tx.Nonce, sender.Nonce)
+ return
+ }
+
+ sender.Nonce += 1
+ defer func() {
+ //state.UpdateStateObject(sender)
+ // Notify all subscribers
+ pool.Ethereum.Reactor().Post("newTx:post", tx)
+ }()
+
+ txTotalBytes := big.NewInt(int64(len(tx.Data)))
+ txTotalBytes.Div(txTotalBytes, ethutil.Big32)
+ addGas(new(big.Int).Mul(txTotalBytes, GasSStore))
+
+ rGas := new(big.Int).Set(gas)
+ rGas.Mul(gas, tx.GasPrice)
+
+ // Make sure there's enough in the sender's account. Having insufficient
+ // funds won't invalidate this transaction but simple ignores it.
+ totAmount := new(big.Int).Add(tx.Value, rGas)
+ if sender.Amount.Cmp(totAmount) < 0 {
+ err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
+ return
+ }
+ state.UpdateStateObject(sender)
+ fmt.Printf("state root after sender update %x\n", state.Root())
+
+ // Get the receiver
+ receiver := state.GetAccount(tx.Recipient)
+
+ // Send Tx to self
+ if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
+ // Subtract the fee
+ sender.SubAmount(rGas)
+ } else {
+ // Subtract the amount from the senders account
+ sender.SubAmount(totAmount)
+
+ // Add the amount to receivers account which should conclude this transaction
+ receiver.AddAmount(tx.Value)
+
+ state.UpdateStateObject(receiver)
+ fmt.Printf("state root after receiver update %x\n", state.Root())
+ }
+
+ txplogger.Infof("[TXPL] Processed Tx %x\n", tx.Hash())
+
+ return
+}
+*/
+
+func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
+ // Get the last block so we can retrieve the sender and receiver from
+ // the merkle trie
+ block := pool.Ethereum.BlockChain().CurrentBlock
+ // Something has gone horribly wrong if this happens
+ if block == nil {
+ return fmt.Errorf("[TXPL] No last block on the block chain")
+ }
+
+ if len(tx.Recipient) != 20 {
+ return fmt.Errorf("[TXPL] Invalid recipient. len = %d", len(tx.Recipient))
+ }
+
+ // Get the sender
+ //sender := pool.Ethereum.StateManager().procState.GetAccount(tx.Sender())
+ sender := pool.Ethereum.StateManager().CurrentState().GetAccount(tx.Sender())
+
+ totAmount := new(big.Int).Set(tx.Value)
+ // Make sure there's enough in the sender's account. Having insufficient
+ // funds won't invalidate this transaction but simple ignores it.
+ if sender.Amount.Cmp(totAmount) < 0 {
+ return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
+ }
+
+ if tx.IsContract() {
+ if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 {
+ return fmt.Errorf("[TXPL] Gasprice too low, %s given should be at least %d.", tx.GasPrice, minGasPrice)
+ }
+ }
+
+ // Increment the nonce making each tx valid only once to prevent replay
+ // attacks
+
+ return nil
+}
+
+func (pool *TxPool) queueHandler() {
+out:
+ for {
+ select {
+ case tx := <-pool.queueChan:
+ hash := tx.Hash()
+ foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool {
+ return bytes.Compare(tx.Hash(), hash) == 0
+ })
+
+ if foundTx != nil {
+ break
+ }
+
+ // Validate the transaction
+ err := pool.ValidateTransaction(tx)
+ if err != nil {
+ txplogger.Debugln("Validating Tx failed", err)
+ } else {
+ // Call blocking version.
+ pool.addTransaction(tx)
+
+ txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tx.Recipient[:4], tx.Value, tx.Hash())
+
+ // Notify the subscribers
+ pool.Ethereum.Reactor().Post("newTx:pre", tx)
+ }
+ case <-pool.quit:
+ break out
+ }
+ }
+}
+
+func (pool *TxPool) QueueTransaction(tx *Transaction) {
+ pool.queueChan <- tx
+}
+
+func (pool *TxPool) CurrentTransactions() []*Transaction {
+ pool.mutex.Lock()
+ defer pool.mutex.Unlock()
+
+ txList := make([]*Transaction, pool.pool.Len())
+ i := 0
+ for e := pool.pool.Front(); e != nil; e = e.Next() {
+ tx := e.Value.(*Transaction)
+
+ txList[i] = tx
+
+ i++
+ }
+
+ return txList
+}
+
+func (pool *TxPool) RemoveInvalid(state *State) {
+ for e := pool.pool.Front(); e != nil; e = e.Next() {
+ tx := e.Value.(*Transaction)
+ sender := state.GetAccount(tx.Sender())
+ err := pool.ValidateTransaction(tx)
+ if err != nil || sender.Nonce >= tx.Nonce {
+ pool.pool.Remove(e)
+ }
+ }
+}
+
+func (pool *TxPool) Flush() []*Transaction {
+ txList := pool.CurrentTransactions()
+
+ // Recreate a new list all together
+ // XXX Is this the fastest way?
+ pool.pool = list.New()
+
+ return txList
+}
+
+func (pool *TxPool) Start() {
+ go pool.queueHandler()
+}
+
+func (pool *TxPool) Stop() {
+ close(pool.quit)
+
+ pool.Flush()
+
+ txplogger.Infoln("Stopped")
+}
diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go
new file mode 100644
index 000000000..3603fd8a7
--- /dev/null
+++ b/ethchain/transaction_test.go
@@ -0,0 +1 @@
+package ethchain
diff --git a/ethchain/types.go b/ethchain/types.go
new file mode 100644
index 000000000..9e7269f74
--- /dev/null
+++ b/ethchain/types.go
@@ -0,0 +1,346 @@
+package ethchain
+
+import (
+ "fmt"
+)
+
+type OpCode int
+
+// Op codes
+const (
+ // 0x0 range - arithmetic ops
+ STOP = 0x00
+ ADD = 0x01
+ MUL = 0x02
+ SUB = 0x03
+ DIV = 0x04
+ SDIV = 0x05
+ MOD = 0x06
+ SMOD = 0x07
+ EXP = 0x08
+ NEG = 0x09
+ LT = 0x0a
+ GT = 0x0b
+ SLT = 0x0c
+ SGT = 0x0d
+ EQ = 0x0e
+ NOT = 0x0f
+
+ // 0x10 range - bit ops
+ AND = 0x10
+ OR = 0x11
+ XOR = 0x12
+ BYTE = 0x13
+
+ // 0x20 range - crypto
+ SHA3 = 0x20
+
+ // 0x30 range - closure state
+ ADDRESS = 0x30
+ BALANCE = 0x31
+ ORIGIN = 0x32
+ CALLER = 0x33
+ CALLVALUE = 0x34
+ CALLDATALOAD = 0x35
+ CALLDATASIZE = 0x36
+ CALLDATACOPY = 0x37
+ CODESIZE = 0x38
+ CODECOPY = 0x39
+ GASPRICE = 0x3a
+
+ // 0x40 range - block operations
+ PREVHASH = 0x40
+ COINBASE = 0x41
+ TIMESTAMP = 0x42
+ NUMBER = 0x43
+ DIFFICULTY = 0x44
+ GASLIMIT = 0x45
+
+ // 0x50 range - 'storage' and execution
+ POP = 0x50
+ DUP = 0x51
+ SWAP = 0x52
+ MLOAD = 0x53
+ MSTORE = 0x54
+ MSTORE8 = 0x55
+ SLOAD = 0x56
+ SSTORE = 0x57
+ JUMP = 0x58
+ JUMPI = 0x59
+ PC = 0x5a
+ MSIZE = 0x5b
+ GAS = 0x5c
+
+ // 0x60 range
+ PUSH1 = 0x60
+ PUSH2 = 0x61
+ PUSH3 = 0x62
+ PUSH4 = 0x63
+ PUSH5 = 0x64
+ PUSH6 = 0x65
+ PUSH7 = 0x66
+ PUSH8 = 0x67
+ PUSH9 = 0x68
+ PUSH10 = 0x69
+ PUSH11 = 0x6a
+ PUSH12 = 0x6b
+ PUSH13 = 0x6c
+ PUSH14 = 0x6d
+ PUSH15 = 0x6e
+ PUSH16 = 0x6f
+ PUSH17 = 0x70
+ PUSH18 = 0x71
+ PUSH19 = 0x72
+ PUSH20 = 0x73
+ PUSH21 = 0x74
+ PUSH22 = 0x75
+ PUSH23 = 0x76
+ PUSH24 = 0x77
+ PUSH25 = 0x78
+ PUSH26 = 0x79
+ PUSH27 = 0x7a
+ PUSH28 = 0x7b
+ PUSH29 = 0x7c
+ PUSH30 = 0x7d
+ PUSH31 = 0x7e
+ PUSH32 = 0x7f
+
+ // 0xf0 range - closures
+ CREATE = 0xf0
+ CALL = 0xf1
+ RETURN = 0xf2
+
+ // 0x70 range - other
+ LOG = 0xfe // XXX Unofficial
+ SUICIDE = 0xff
+)
+
+// Since the opcodes aren't all in order we can't use a regular slice
+var opCodeToString = map[OpCode]string{
+ // 0x0 range - arithmetic ops
+ STOP: "STOP",
+ ADD: "ADD",
+ MUL: "MUL",
+ SUB: "SUB",
+ DIV: "DIV",
+ SDIV: "SDIV",
+ MOD: "MOD",
+ SMOD: "SMOD",
+ EXP: "EXP",
+ NEG: "NEG",
+ LT: "LT",
+ GT: "GT",
+ SLT: "SLT",
+ SGT: "SGT",
+ EQ: "EQ",
+ NOT: "NOT",
+
+ // 0x10 range - bit ops
+ AND: "AND",
+ OR: "OR",
+ XOR: "XOR",
+ BYTE: "BYTE",
+
+ // 0x20 range - crypto
+ SHA3: "SHA3",
+
+ // 0x30 range - closure state
+ ADDRESS: "ADDRESS",
+ BALANCE: "BALANCE",
+ ORIGIN: "ORIGIN",
+ CALLER: "CALLER",
+ CALLVALUE: "CALLVALUE",
+ CALLDATALOAD: "CALLDATALOAD",
+ CALLDATASIZE: "CALLDATASIZE",
+ CALLDATACOPY: "CALLDATACOPY",
+ CODESIZE: "CODESIZE",
+ CODECOPY: "CODECOPY",
+ GASPRICE: "TXGASPRICE",
+
+ // 0x40 range - block operations
+ PREVHASH: "PREVHASH",
+ COINBASE: "COINBASE",
+ TIMESTAMP: "TIMESTAMP",
+ NUMBER: "NUMBER",
+ DIFFICULTY: "DIFFICULTY",
+ GASLIMIT: "GASLIMIT",
+
+ // 0x50 range - 'storage' and execution
+ POP: "POP",
+ DUP: "DUP",
+ SWAP: "SWAP",
+ MLOAD: "MLOAD",
+ MSTORE: "MSTORE",
+ MSTORE8: "MSTORE8",
+ SLOAD: "SLOAD",
+ SSTORE: "SSTORE",
+ JUMP: "JUMP",
+ JUMPI: "JUMPI",
+ PC: "PC",
+ MSIZE: "MSIZE",
+ GAS: "GAS",
+
+ // 0x60 range - push
+ PUSH1: "PUSH1",
+ PUSH2: "PUSH2",
+ PUSH3: "PUSH3",
+ PUSH4: "PUSH4",
+ PUSH5: "PUSH5",
+ PUSH6: "PUSH6",
+ PUSH7: "PUSH7",
+ PUSH8: "PUSH8",
+ PUSH9: "PUSH9",
+ PUSH10: "PUSH10",
+ PUSH11: "PUSH11",
+ PUSH12: "PUSH12",
+ PUSH13: "PUSH13",
+ PUSH14: "PUSH14",
+ PUSH15: "PUSH15",
+ PUSH16: "PUSH16",
+ PUSH17: "PUSH17",
+ PUSH18: "PUSH18",
+ PUSH19: "PUSH19",
+ PUSH20: "PUSH20",
+ PUSH21: "PUSH21",
+ PUSH22: "PUSH22",
+ PUSH23: "PUSH23",
+ PUSH24: "PUSH24",
+ PUSH25: "PUSH25",
+ PUSH26: "PUSH26",
+ PUSH27: "PUSH27",
+ PUSH28: "PUSH28",
+ PUSH29: "PUSH29",
+ PUSH30: "PUSH30",
+ PUSH31: "PUSH31",
+ PUSH32: "PUSH32",
+
+ // 0xf0 range
+ CREATE: "CREATE",
+ CALL: "CALL",
+ RETURN: "RETURN",
+
+ // 0x70 range - other
+ LOG: "LOG",
+ SUICIDE: "SUICIDE",
+}
+
+func (o OpCode) String() string {
+ str := opCodeToString[o]
+ if len(str) == 0 {
+ return fmt.Sprintf("Missing opcode 0x%x", int(o))
+ }
+
+ return str
+}
+
+// Op codes for assembling
+var OpCodes = map[string]byte{
+ // 0x0 range - arithmetic ops
+ "STOP": 0x00,
+ "ADD": 0x01,
+ "MUL": 0x02,
+ "SUB": 0x03,
+ "DIV": 0x04,
+ "SDIV": 0x05,
+ "MOD": 0x06,
+ "SMOD": 0x07,
+ "EXP": 0x08,
+ "NEG": 0x09,
+ "LT": 0x0a,
+ "GT": 0x0b,
+ "EQ": 0x0c,
+ "NOT": 0x0d,
+
+ // 0x10 range - bit ops
+ "AND": 0x10,
+ "OR": 0x11,
+ "XOR": 0x12,
+ "BYTE": 0x13,
+
+ // 0x20 range - crypto
+ "SHA3": 0x20,
+
+ // 0x30 range - closure state
+ "ADDRESS": 0x30,
+ "BALANCE": 0x31,
+ "ORIGIN": 0x32,
+ "CALLER": 0x33,
+ "CALLVALUE": 0x34,
+ "CALLDATALOAD": 0x35,
+ "CALLDATASIZE": 0x36,
+ "GASPRICE": 0x38,
+
+ // 0x40 range - block operations
+ "PREVHASH": 0x40,
+ "COINBASE": 0x41,
+ "TIMESTAMP": 0x42,
+ "NUMBER": 0x43,
+ "DIFFICULTY": 0x44,
+ "GASLIMIT": 0x45,
+
+ // 0x50 range - 'storage' and execution
+ "POP": 0x51,
+ "DUP": 0x52,
+ "SWAP": 0x53,
+ "MLOAD": 0x54,
+ "MSTORE": 0x55,
+ "MSTORE8": 0x56,
+ "SLOAD": 0x57,
+ "SSTORE": 0x58,
+ "JUMP": 0x59,
+ "JUMPI": 0x5a,
+ "PC": 0x5b,
+ "MSIZE": 0x5c,
+
+ // 0x70 range - 'push'
+ "PUSH1": 0x60,
+ "PUSH2": 0x61,
+ "PUSH3": 0x62,
+ "PUSH4": 0x63,
+ "PUSH5": 0x64,
+ "PUSH6": 0x65,
+ "PUSH7": 0x66,
+ "PUSH8": 0x67,
+ "PUSH9": 0x68,
+ "PUSH10": 0x69,
+ "PUSH11": 0x6a,
+ "PUSH12": 0x6b,
+ "PUSH13": 0x6c,
+ "PUSH14": 0x6d,
+ "PUSH15": 0x6e,
+ "PUSH16": 0x6f,
+ "PUSH17": 0x70,
+ "PUSH18": 0x71,
+ "PUSH19": 0x72,
+ "PUSH20": 0x73,
+ "PUSH21": 0x74,
+ "PUSH22": 0x75,
+ "PUSH23": 0x76,
+ "PUSH24": 0x77,
+ "PUSH25": 0x78,
+ "PUSH26": 0x70,
+ "PUSH27": 0x7a,
+ "PUSH28": 0x7b,
+ "PUSH29": 0x7c,
+ "PUSH30": 0x7d,
+ "PUSH31": 0x7e,
+ "PUSH32": 0x7f,
+
+ // 0xf0 range - closures
+ "CREATE": 0xf0,
+ "CALL": 0xf1,
+ "RETURN": 0xf2,
+
+ // 0x70 range - other
+ "LOG": 0xfe,
+ "SUICIDE": 0x7f,
+}
+
+func IsOpCode(s string) bool {
+ for key, _ := range OpCodes {
+ if key == s {
+ return true
+ }
+ }
+ return false
+}
diff --git a/ethchain/vm.go b/ethchain/vm.go
new file mode 100644
index 000000000..3851d0d70
--- /dev/null
+++ b/ethchain/vm.go
@@ -0,0 +1,742 @@
+package ethchain
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethutil"
+ "math"
+ "math/big"
+)
+
+var vmlogger = ethlog.NewLogger("VM")
+
+var (
+ GasStep = big.NewInt(1)
+ GasSha = big.NewInt(20)
+ GasSLoad = big.NewInt(20)
+ GasSStore = big.NewInt(100)
+ GasBalance = big.NewInt(20)
+ GasCreate = big.NewInt(100)
+ GasCall = big.NewInt(20)
+ GasMemory = big.NewInt(1)
+ GasData = big.NewInt(5)
+ GasTx = big.NewInt(500)
+)
+
+func CalculateTxGas(initSize *big.Int) *big.Int {
+ totalGas := new(big.Int)
+
+ txTotalBytes := new(big.Int).Set(initSize)
+ txTotalBytes.Div(txTotalBytes, ethutil.Big32)
+ totalGas.Add(totalGas, new(big.Int).Mul(txTotalBytes, GasSStore))
+
+ return totalGas
+}
+
+type Vm struct {
+ txPool *TxPool
+ // Stack for processing contracts
+ stack *Stack
+ // non-persistent key/value memory storage
+ mem map[string]*big.Int
+
+ vars RuntimeVars
+
+ state *State
+
+ stateManager *StateManager
+
+ Verbose bool
+
+ logStr string
+
+ err error
+}
+
+type RuntimeVars struct {
+ Origin []byte
+ Block *Block
+ BlockNumber *big.Int
+ PrevHash []byte
+ Coinbase []byte
+ Time int64
+ Diff *big.Int
+ TxData []string
+ Value *big.Int
+}
+
+func (self *Vm) Printf(format string, v ...interface{}) *Vm {
+ if self.Verbose {
+ self.logStr += fmt.Sprintf(format, v...)
+ }
+
+ return self
+}
+
+func (self *Vm) Endl() *Vm {
+ if self.Verbose {
+ vmlogger.Infoln(self.logStr)
+ self.logStr = ""
+ }
+
+ return self
+}
+
+func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
+ return &Vm{vars: vars, state: state, stateManager: stateManager}
+}
+
+var Pow256 = ethutil.BigPow(2, 256)
+
+var isRequireError = false
+
+func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) {
+ // Recover from any require exception
+ defer func() {
+ if r := recover(); r != nil {
+ ret = closure.Return(nil)
+ err = fmt.Errorf("%v", r)
+ vmlogger.Errorln("vm err", err)
+ }
+ }()
+
+ vmlogger.Debugf("(~) %x gas: %v (d) %x\n", closure.object.Address(), closure.Gas, closure.Args)
+
+ var (
+ op OpCode
+
+ mem = &Memory{}
+ stack = NewStack()
+ pc = big.NewInt(0)
+ step = 0
+ prevStep = 0
+ require = func(m int) {
+ if stack.Len() < m {
+ isRequireError = true
+ panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m))
+ }
+ }
+ )
+
+ for {
+ prevStep = step
+ // The base for all big integer arithmetic
+ base := new(big.Int)
+
+ step++
+ // Get the memory location of pc
+ val := closure.Get(pc)
+ // Get the opcode (it must be an opcode!)
+ op = OpCode(val.Uint())
+
+ gas := new(big.Int)
+ addStepGasUsage := func(amount *big.Int) {
+ if amount.Cmp(ethutil.Big0) >= 0 {
+ gas.Add(gas, amount)
+ }
+ }
+
+ addStepGasUsage(GasStep)
+
+ var newMemSize uint64 = 0
+ switch op {
+ case STOP:
+ gas.Set(ethutil.Big0)
+ case SUICIDE:
+ gas.Set(ethutil.Big0)
+ case SLOAD:
+ gas.Set(GasSLoad)
+ case SSTORE:
+ var mult *big.Int
+ y, x := stack.Peekn()
+ val := closure.GetMem(x)
+ if val.IsEmpty() && len(y.Bytes()) > 0 {
+ mult = ethutil.Big2
+ } else if !val.IsEmpty() && len(y.Bytes()) == 0 {
+ mult = ethutil.Big0
+ } else {
+ mult = ethutil.Big1
+ }
+ gas = new(big.Int).Mul(mult, GasSStore)
+ case BALANCE:
+ gas.Set(GasBalance)
+ case MSTORE:
+ require(2)
+ newMemSize = stack.Peek().Uint64() + 32
+ case MLOAD:
+
+ case MSTORE8:
+ require(2)
+ newMemSize = stack.Peek().Uint64() + 1
+ case RETURN:
+ require(2)
+
+ newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
+ case SHA3:
+ require(2)
+
+ gas.Set(GasSha)
+
+ newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
+ case CALLDATACOPY:
+ require(3)
+
+ newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
+ case CODECOPY:
+ require(3)
+
+ newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
+ case CALL:
+ require(7)
+ gas.Set(GasCall)
+ addStepGasUsage(stack.data[stack.Len()-1])
+
+ x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64()
+ y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64()
+
+ newMemSize = uint64(math.Max(float64(x), float64(y)))
+ case CREATE:
+ require(3)
+ gas.Set(GasCreate)
+
+ newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64()
+ }
+
+ newMemSize = (newMemSize + 31) / 32 * 32
+ if newMemSize > uint64(mem.Len()) {
+ m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32
+ addStepGasUsage(big.NewInt(int64(m)))
+ }
+
+ if !closure.UseGas(gas) {
+ err := fmt.Errorf("Insufficient gas for %v. req %v has %v", op, gas, closure.Gas)
+
+ closure.UseGas(closure.Gas)
+
+ return closure.Return(nil), err
+ }
+
+ vm.Printf("(pc) %-3d -o- %-14s", pc, op.String())
+ vm.Printf(" (g) %-3v (%v)", gas, closure.Gas)
+
+ mem.Resize(newMemSize)
+
+ switch op {
+ case LOG:
+ stack.Print()
+ mem.Print()
+ // 0x20 range
+ case ADD:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v + %v", y, x)
+
+ base.Add(y, x)
+
+ vm.Printf(" = %v", base)
+ // Pop result back on the stack
+ stack.Push(base)
+ case SUB:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v - %v", y, x)
+
+ base.Sub(y, x)
+
+ vm.Printf(" = %v", base)
+ // Pop result back on the stack
+ stack.Push(base)
+ case MUL:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v * %v", y, x)
+
+ base.Mul(y, x)
+
+ vm.Printf(" = %v", base)
+ // Pop result back on the stack
+ stack.Push(base)
+ case DIV:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v / %v", y, x)
+
+ base.Div(y, x)
+
+ vm.Printf(" = %v", base)
+ // Pop result back on the stack
+ stack.Push(base)
+ case SDIV:
+ require(2)
+ x, y := 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
+ stack.Push(z)
+ case MOD:
+ require(2)
+ x, y := stack.Popn()
+
+ vm.Printf(" %v %% %v", y, x)
+
+ base.Mod(y, x)
+
+ vm.Printf(" = %v", base)
+ stack.Push(base)
+ case SMOD:
+ require(2)
+ x, y := 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
+ stack.Push(z)
+ case EXP:
+ require(2)
+ x, y := stack.Popn()
+
+ vm.Printf(" %v ** %v", y, x)
+
+ base.Exp(y, x, Pow256)
+
+ vm.Printf(" = %v", base)
+
+ stack.Push(base)
+ case NEG:
+ require(1)
+ base.Sub(Pow256, stack.Pop())
+ stack.Push(base)
+ case LT:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v < %v", y, x)
+ // x < y
+ if y.Cmp(x) < 0 {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+ case GT:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v > %v", y, x)
+
+ // x > y
+ if y.Cmp(x) > 0 {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+
+ case SLT:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v < %v", y, x)
+ // x < y
+ if y.Cmp(x) < 0 {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+ case SGT:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v > %v", y, x)
+
+ // x > y
+ if y.Cmp(x) > 0 {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+
+ case EQ:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v == %v", y, x)
+
+ // x == y
+ if x.Cmp(y) == 0 {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+ case NOT:
+ require(1)
+ x := stack.Pop()
+ if x.Cmp(ethutil.BigFalse) > 0 {
+ stack.Push(ethutil.BigFalse)
+ } else {
+ stack.Push(ethutil.BigTrue)
+ }
+
+ // 0x10 range
+ case AND:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v & %v", y, x)
+
+ stack.Push(base.And(y, x))
+ case OR:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v | %v", y, x)
+
+ stack.Push(base.Or(y, x))
+ case XOR:
+ require(2)
+ x, y := stack.Popn()
+ vm.Printf(" %v ^ %v", y, x)
+
+ stack.Push(base.Xor(y, x))
+ case BYTE:
+ require(2)
+ val, th := stack.Popn()
+ if th.Cmp(big.NewInt(32)) < 0 {
+ stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64()))
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+
+ // 0x20 range
+ case SHA3:
+ require(2)
+ size, offset := stack.Popn()
+ data := ethutil.Sha3Bin(mem.Get(offset.Int64(), size.Int64()))
+
+ stack.Push(ethutil.BigD(data))
+ // 0x30 range
+ case ADDRESS:
+ stack.Push(ethutil.BigD(closure.Object().Address()))
+ case BALANCE:
+ stack.Push(closure.object.Amount)
+ case ORIGIN:
+ stack.Push(ethutil.BigD(vm.vars.Origin))
+ case CALLER:
+ caller := closure.caller.Address()
+ stack.Push(ethutil.BigD(caller))
+
+ vm.Printf(" => %x", caller)
+ case CALLVALUE:
+ stack.Push(vm.vars.Value)
+ case CALLDATALOAD:
+ require(1)
+ offset := stack.Pop().Int64()
+
+ data := make([]byte, 32)
+ if len(closure.Args) >= int(offset) {
+ l := int64(math.Min(float64(offset+32), float64(len(closure.Args))))
+
+ copy(data, closure.Args[offset:l])
+ }
+
+ vm.Printf(" => 0x%x", data)
+
+ stack.Push(ethutil.BigD(data))
+ case CALLDATASIZE:
+ l := int64(len(closure.Args))
+ stack.Push(big.NewInt(l))
+
+ vm.Printf(" => %d", l)
+ case CALLDATACOPY:
+ var (
+ size = int64(len(closure.Args))
+ mOff = stack.Pop().Int64()
+ cOff = stack.Pop().Int64()
+ l = stack.Pop().Int64()
+ )
+
+ if cOff > size {
+ cOff = 0
+ l = 0
+ } else if cOff+l > size {
+ l = 0
+ }
+
+ code := closure.Args[cOff : cOff+l]
+
+ mem.Set(mOff, l, code)
+ case CODESIZE:
+ l := big.NewInt(int64(len(closure.Script)))
+ stack.Push(l)
+
+ vm.Printf(" => %d", l)
+ case CODECOPY:
+ var (
+ size = int64(len(closure.Script))
+ mOff = stack.Pop().Int64()
+ cOff = stack.Pop().Int64()
+ l = stack.Pop().Int64()
+ )
+
+ if cOff > size {
+ cOff = 0
+ l = 0
+ } else if cOff+l > size {
+ l = 0
+ }
+
+ code := closure.Script[cOff : cOff+l]
+
+ mem.Set(mOff, l, code)
+ case GASPRICE:
+ stack.Push(closure.Price)
+
+ // 0x40 range
+ case PREVHASH:
+ stack.Push(ethutil.BigD(vm.vars.PrevHash))
+ case COINBASE:
+ stack.Push(ethutil.BigD(vm.vars.Coinbase))
+ case TIMESTAMP:
+ stack.Push(big.NewInt(vm.vars.Time))
+ case NUMBER:
+ stack.Push(vm.vars.BlockNumber)
+ case DIFFICULTY:
+ stack.Push(vm.vars.Diff)
+ case GASLIMIT:
+ // TODO
+ stack.Push(big.NewInt(0))
+
+ // 0x50 range
+ case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
+ a := big.NewInt(int64(op) - int64(PUSH1) + 1)
+ pc.Add(pc, ethutil.Big1)
+ data := closure.Gets(pc, a)
+ val := ethutil.BigD(data.Bytes())
+ // Push value to stack
+ stack.Push(val)
+ pc.Add(pc, a.Sub(a, big.NewInt(1)))
+
+ step += int(op) - int(PUSH1) + 1
+
+ vm.Printf(" => 0x%x", data.Bytes())
+ case POP:
+ require(1)
+ stack.Pop()
+ case DUP:
+ require(1)
+ stack.Push(stack.Peek())
+
+ vm.Printf(" => 0x%x", stack.Peek().Bytes())
+ case SWAP:
+ require(2)
+ x, y := stack.Popn()
+ stack.Push(y)
+ stack.Push(x)
+ case MLOAD:
+ require(1)
+ offset := stack.Pop()
+ val := ethutil.BigD(mem.Get(offset.Int64(), 32))
+ stack.Push(val)
+
+ vm.Printf(" => 0x%x", val.Bytes())
+ case MSTORE: // Store the value at stack top-1 in to memory at location stack top
+ require(2)
+ // Pop value of the stack
+ val, mStart := stack.Popn()
+ mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
+
+ vm.Printf(" => 0x%x", val)
+ case MSTORE8:
+ require(2)
+ val, mStart := stack.Popn()
+ base.And(val, new(big.Int).SetInt64(0xff))
+ mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
+
+ vm.Printf(" => 0x%x", val)
+ case SLOAD:
+ require(1)
+ loc := stack.Pop()
+ val := closure.GetMem(loc)
+
+ stack.Push(val.BigInt())
+
+ vm.Printf(" {0x%x} 0x%x", loc.Bytes(), val.Bytes())
+ case SSTORE:
+ require(2)
+ val, loc := stack.Popn()
+ closure.SetStorage(loc, ethutil.NewValue(val))
+
+ // Add the change to manifest
+ vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
+
+ vm.Printf(" {0x%x} 0x%x", loc, val)
+ case JUMP:
+ require(1)
+ pc = stack.Pop()
+ // Reduce pc by one because of the increment that's at the end of this for loop
+ vm.Printf(" ~> %v", pc).Endl()
+
+ continue
+ case JUMPI:
+ require(2)
+ cond, pos := stack.Popn()
+ if cond.Cmp(ethutil.BigTrue) >= 0 {
+ pc = pos
+
+ vm.Printf(" ~> %v (t)", pc).Endl()
+
+ continue
+ } else {
+ vm.Printf(" (f)")
+ }
+ case PC:
+ stack.Push(pc)
+ case MSIZE:
+ stack.Push(big.NewInt(int64(mem.Len())))
+ case GAS:
+ stack.Push(closure.Gas)
+ // 0x60 range
+ case CREATE:
+ require(3)
+
+ value := stack.Pop()
+ size, offset := stack.Popn()
+
+ // Snapshot the current stack so we are able to
+ // revert back to it later.
+ snapshot := vm.state.Copy()
+
+ // Generate a new address
+ addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N())
+
+ vm.Printf(" (*) %x", addr).Endl()
+
+ // Create a new contract
+ contract := vm.state.NewStateObject(addr)
+ contract.Amount = value
+
+ // Set the init script
+ contract.initScript = ethutil.BigD(mem.Get(offset.Int64(), size.Int64())).Bytes()
+ // Transfer all remaining gas to the new
+ // contract so it may run the init script
+ gas := new(big.Int).Set(closure.Gas)
+
+ // Create the closure
+ c := NewClosure(closure.caller,
+ closure.Object(),
+ contract.initScript,
+ vm.state,
+ gas,
+ closure.Price)
+ // Call the closure and set the return value as
+ // main script.
+ var err error
+ c.Script, gas, err = c.Call(vm, nil, hook)
+
+ if err != nil {
+ stack.Push(ethutil.BigFalse)
+
+ // Revert the state as it was before.
+ vm.state.Set(snapshot)
+ } else {
+ stack.Push(ethutil.BigD(addr))
+ }
+ case CALL:
+ require(7)
+
+ vm.Endl()
+
+ gas := stack.Pop()
+ // Pop gas and value of the stack.
+ value, addr := stack.Popn()
+ // Pop input size and offset
+ inSize, inOffset := stack.Popn()
+ // Pop return size and offset
+ retSize, retOffset := stack.Popn()
+
+ // Get the arguments from the memory
+ args := mem.Get(inOffset.Int64(), inSize.Int64())
+
+ if closure.object.Amount.Cmp(value) < 0 {
+ vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
+
+ stack.Push(ethutil.BigFalse)
+ } else {
+ snapshot := vm.state.Copy()
+
+ stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
+
+ closure.object.SubAmount(value)
+ // Add the value to the state object
+ stateObject.AddAmount(value)
+
+ // Create a new callable closure
+ closure := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price)
+ // Executer the closure and get the return value (if any)
+ //ret, _, err := closure.Call(vm, args, hook)
+ ret, err, _ := Call(vm, closure, args)
+ if err != nil {
+ stack.Push(ethutil.BigFalse)
+
+ vmlogger.Debugf("Closure execution failed. %v\n", err)
+
+ vm.err = err
+ vm.state.Set(snapshot)
+ } else {
+ stack.Push(ethutil.BigTrue)
+
+ mem.Set(retOffset.Int64(), retSize.Int64(), ret)
+ }
+ }
+ case RETURN:
+ require(2)
+ size, offset := stack.Popn()
+ ret := mem.Get(offset.Int64(), size.Int64())
+
+ vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
+
+ return closure.Return(ret), nil
+ case SUICIDE:
+ require(1)
+
+ receiver := vm.state.GetAccount(stack.Pop().Bytes())
+ receiver.AddAmount(closure.object.Amount)
+
+ trie := closure.object.state.trie
+ trie.NewIterator().Each(func(key string, v *ethutil.Value) {
+ trie.Delete(key)
+ })
+
+ fallthrough
+ case STOP: // Stop the closure
+ vm.Endl()
+
+ return closure.Return(nil), nil
+ default:
+ vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
+ fmt.Println(Code(closure.Script))
+
+ return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
+ }
+
+ pc.Add(pc, ethutil.Big1)
+
+ vm.Endl()
+
+ if hook != nil {
+ if !hook(prevStep, op, mem, stack, closure.Object()) {
+ return nil, nil
+ }
+ }
+ }
+}
diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go
new file mode 100644
index 000000000..c8023cd79
--- /dev/null
+++ b/ethchain/vm_test.go
@@ -0,0 +1,68 @@
+package ethchain
+
+import (
+ _ "bytes"
+ "fmt"
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "testing"
+)
+
+func TestRun4(t *testing.T) {
+ ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ callerScript, err := ethutil.Compile(`
+ this.store[this.origin()] = 10**20
+ hello := "world"
+
+ return lambda {
+ big to = this.data[0]
+ big from = this.origin()
+ big value = this.data[1]
+
+ if this.store[from] >= value {
+ this.store[from] = this.store[from] - value
+ this.store[to] = this.store[to] + value
+ }
+ }
+ `)
+ if err != nil {
+ fmt.Println(err)
+ }
+ fmt.Println(Disassemble(callerScript))
+
+ callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript)
+ callerTx.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
+
+ // Contract addr as test address
+ gas := big.NewInt(1000)
+ gasPrice := big.NewInt(10)
+ account := NewAccount(ContractAddr, big.NewInt(10000000))
+ fmt.Println("account.Amount =", account.Amount)
+ c := MakeContract(callerTx, state)
+ e := account.ConvertGas(gas, gasPrice)
+ if e != nil {
+ fmt.Println(err)
+ }
+ fmt.Println("account.Amount =", account.Amount)
+ callerClosure := NewClosure(account, c, callerScript, state, gas, gasPrice)
+
+ vm := NewVm(state, nil, RuntimeVars{
+ Origin: account.Address(),
+ BlockNumber: big.NewInt(1),
+ PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
+ Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
+ Time: 1,
+ Diff: big.NewInt(256),
+ })
+ var ret []byte
+ ret, _, e = callerClosure.Call(vm, nil, nil)
+ if e != nil {
+ fmt.Println("error", e)
+ }
+ fmt.Println(ret)
+}
diff --git a/ethdb/.gitignore b/ethdb/.gitignore
new file mode 100644
index 000000000..f725d58d1
--- /dev/null
+++ b/ethdb/.gitignore
@@ -0,0 +1,12 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile ~/.gitignore_global
+
+/tmp
+*/**/*un~
+*un~
+.DS_Store
+*/**/.DS_Store
+
diff --git a/ethdb/README.md b/ethdb/README.md
new file mode 100644
index 000000000..5bed8eedc
--- /dev/null
+++ b/ethdb/README.md
@@ -0,0 +1,11 @@
+# ethdb
+
+The ethdb package contains the ethereum database interfaces
+
+# Installation
+
+`go get github.com/ethereum/ethdb-go`
+
+# Usage
+
+Todo :-)
diff --git a/ethdb/database.go b/ethdb/database.go
new file mode 100644
index 000000000..09e9d8c7d
--- /dev/null
+++ b/ethdb/database.go
@@ -0,0 +1,80 @@
+package ethdb
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/syndtr/goleveldb/leveldb"
+ "path"
+)
+
+type LDBDatabase struct {
+ db *leveldb.DB
+}
+
+func NewLDBDatabase(name string) (*LDBDatabase, error) {
+ dbPath := path.Join(ethutil.Config.ExecPath, name)
+
+ // Open the db
+ db, err := leveldb.OpenFile(dbPath, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ database := &LDBDatabase{db: db}
+
+ return database, nil
+}
+
+func (db *LDBDatabase) Put(key []byte, value []byte) {
+ err := db.db.Put(key, value, nil)
+ if err != nil {
+ fmt.Println("Error put", err)
+ }
+}
+
+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) Db() *leveldb.DB {
+ return db.db
+}
+
+func (db *LDBDatabase) LastKnownTD() []byte {
+ data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil)
+
+ if len(data) == 0 {
+ data = []byte{0x0}
+ }
+
+ 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, nil)
+ for iter.Next() {
+ key := iter.Key()
+ value := iter.Value()
+
+ fmt.Printf("%x(%d): ", key, len(key))
+ node := ethutil.NewValueFromBytes(value)
+ fmt.Printf("%v\n", node)
+ }
+}
diff --git a/ethdb/database_test.go b/ethdb/database_test.go
new file mode 100644
index 000000000..bb1b4de2a
--- /dev/null
+++ b/ethdb/database_test.go
@@ -0,0 +1,6 @@
+package ethdb
+
+import (
+ _ "fmt"
+ _ "testing"
+)
diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go
new file mode 100644
index 000000000..1e9d2899a
--- /dev/null
+++ b/ethdb/memory_database.go
@@ -0,0 +1,62 @@
+package ethdb
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+)
+
+/*
+ * This is a test memory database. Do not use for any production it does not get persisted
+ */
+type MemDatabase struct {
+ db map[string][]byte
+}
+
+func NewMemDatabase() (*MemDatabase, error) {
+ db := &MemDatabase{db: make(map[string][]byte)}
+
+ return db, nil
+}
+
+func (db *MemDatabase) Put(key []byte, value []byte) {
+ db.db[string(key)] = value
+}
+
+func (db *MemDatabase) Get(key []byte) ([]byte, error) {
+ return db.db[string(key)], nil
+}
+
+/*
+func (db *MemDatabase) 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))
+
+ return nil
+}
+
+func (db *MemDatabase) Print() {
+ for key, val := range db.db {
+ fmt.Printf("%x(%d): ", key, len(key))
+ node := ethutil.NewValueFromBytes(val)
+ fmt.Printf("%q\n", node.Interface())
+ }
+}
+
+func (db *MemDatabase) Close() {
+}
+
+func (db *MemDatabase) LastKnownTD() []byte {
+ data, _ := db.Get([]byte("LastKnownTotalDifficulty"))
+
+ if len(data) == 0 || data == nil {
+ data = []byte{0x0}
+ }
+
+ return data
+}
diff --git a/ethereum.go b/ethereum.go
new file mode 100644
index 000000000..a3df23e92
--- /dev/null
+++ b/ethereum.go
@@ -0,0 +1,499 @@
+package eth
+
+import (
+ "container/list"
+ "fmt"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethrpc"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "io/ioutil"
+ "math/rand"
+ "net"
+ "net/http"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+var ethlogger = ethlog.NewLogger("SERV")
+
+func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
+ // Loop thru the peers and close them (if we had them)
+ for e := peers.Front(); e != nil; e = e.Next() {
+ if peer, ok := e.Value.(*Peer); ok {
+ callback(peer, e)
+ }
+ }
+}
+
+const (
+ processReapingTimeout = 60 // TODO increase
+)
+
+type Ethereum struct {
+ // Channel for shutting down the ethereum
+ shutdownChan chan bool
+ quit chan bool
+ // DB interface
+ //db *ethdb.LDBDatabase
+ db ethutil.Database
+ // State manager for processing new blocks and managing the over all states
+ stateManager *ethchain.StateManager
+ // The transaction pool. Transaction can be pushed on this pool
+ // for later including in the blocks
+ txPool *ethchain.TxPool
+ // The canonical chain
+ blockChain *ethchain.BlockChain
+ // Peers (NYI)
+ peers *list.List
+ // Nonce
+ Nonce uint64
+
+ Addr net.Addr
+ Port string
+
+ peerMut sync.Mutex
+
+ // Capabilities for outgoing peers
+ serverCaps Caps
+
+ nat NAT
+
+ // Specifies the desired amount of maximum peers
+ MaxPeers int
+
+ Mining bool
+
+ listening bool
+
+ reactor *ethutil.ReactorEngine
+
+ RpcServer *ethrpc.JsonRpcServer
+}
+
+func New(caps Caps, usePnp bool) (*Ethereum, error) {
+ db, err := ethdb.NewLDBDatabase("database")
+ //db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ return nil, err
+ }
+
+ var nat NAT
+ if usePnp {
+ nat, err = Discover()
+ if err != nil {
+ ethlogger.Debugln("UPnP failed", err)
+ }
+ }
+
+ ethutil.Config.Db = db
+
+ nonce, _ := ethutil.RandomUint64()
+ ethereum := &Ethereum{
+ shutdownChan: make(chan bool),
+ quit: make(chan bool),
+ db: db,
+ peers: list.New(),
+ Nonce: nonce,
+ serverCaps: caps,
+ nat: nat,
+ }
+ ethereum.reactor = ethutil.NewReactorEngine()
+
+ ethereum.txPool = ethchain.NewTxPool(ethereum)
+ ethereum.blockChain = ethchain.NewBlockChain(ethereum)
+ ethereum.stateManager = ethchain.NewStateManager(ethereum)
+
+ // Start the tx pool
+ ethereum.txPool.Start()
+
+ return ethereum, nil
+}
+
+func (s *Ethereum) Reactor() *ethutil.ReactorEngine {
+ return s.reactor
+}
+
+func (s *Ethereum) BlockChain() *ethchain.BlockChain {
+ return s.blockChain
+}
+
+func (s *Ethereum) StateManager() *ethchain.StateManager {
+ return s.stateManager
+}
+
+func (s *Ethereum) TxPool() *ethchain.TxPool {
+ return s.txPool
+}
+
+func (s *Ethereum) ServerCaps() Caps {
+ return s.serverCaps
+}
+func (s *Ethereum) IsMining() bool {
+ return s.Mining
+}
+func (s *Ethereum) PeerCount() int {
+ return s.peers.Len()
+}
+func (s *Ethereum) IsUpToDate() bool {
+ upToDate := true
+ eachPeer(s.peers, func(peer *Peer, e *list.Element) {
+ if atomic.LoadInt32(&peer.connected) == 1 {
+ if peer.catchingUp == true {
+ upToDate = false
+ }
+ }
+ })
+ return upToDate
+}
+func (s *Ethereum) PushPeer(peer *Peer) {
+ s.peers.PushBack(peer)
+}
+func (s *Ethereum) IsListening() bool {
+ return s.listening
+}
+
+func (s *Ethereum) AddPeer(conn net.Conn) {
+ peer := NewPeer(conn, s, true)
+
+ if peer != nil {
+ if s.peers.Len() < s.MaxPeers {
+ peer.Start()
+ } else {
+ ethlogger.Debugf("Max connected peers reached. Not adding incoming peer.")
+ }
+ }
+}
+
+func (s *Ethereum) ProcessPeerList(addrs []string) {
+ for _, addr := range addrs {
+ // TODO Probably requires some sanity checks
+ s.ConnectToPeer(addr)
+ }
+}
+
+func (s *Ethereum) ConnectToPeer(addr string) error {
+ if s.peers.Len() < s.MaxPeers {
+ var alreadyConnected bool
+
+ ahost, _, _ := net.SplitHostPort(addr)
+ var chost string
+
+ ips, err := net.LookupIP(ahost)
+
+ if err != nil {
+ return err
+ } else {
+ // If more then one ip is available try stripping away the ipv6 ones
+ if len(ips) > 1 {
+ var ipsv4 []net.IP
+ // For now remove the ipv6 addresses
+ for _, ip := range ips {
+ if strings.Contains(ip.String(), "::") {
+ continue
+ } else {
+ ipsv4 = append(ipsv4, ip)
+ }
+ }
+ if len(ipsv4) == 0 {
+ return fmt.Errorf("[SERV] No IPV4 addresses available for hostname")
+ }
+
+ // Pick a random ipv4 address, simulating round-robin DNS.
+ rand.Seed(time.Now().UTC().UnixNano())
+ i := rand.Intn(len(ipsv4))
+ chost = ipsv4[i].String()
+ } else {
+ if len(ips) == 0 {
+ return fmt.Errorf("[SERV] No IPs resolved for the given hostname")
+ return nil
+ }
+ chost = ips[0].String()
+ }
+ }
+
+ eachPeer(s.peers, func(p *Peer, v *list.Element) {
+ if p.conn == nil {
+ return
+ }
+ phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
+
+ if phost == chost {
+ alreadyConnected = true
+ //ethlogger.Debugf("Peer %s already added.\n", chost)
+ return
+ }
+ })
+
+ if alreadyConnected {
+ return nil
+ }
+
+ NewOutboundPeer(addr, s, s.serverCaps)
+ }
+
+ return nil
+}
+
+func (s *Ethereum) OutboundPeers() []*Peer {
+ // Create a new peer slice with at least the length of the total peers
+ outboundPeers := make([]*Peer, s.peers.Len())
+ length := 0
+ eachPeer(s.peers, func(p *Peer, e *list.Element) {
+ if !p.inbound && p.conn != nil {
+ outboundPeers[length] = p
+ length++
+ }
+ })
+
+ return outboundPeers[:length]
+}
+
+func (s *Ethereum) InboundPeers() []*Peer {
+ // Create a new peer slice with at least the length of the total peers
+ inboundPeers := make([]*Peer, s.peers.Len())
+ length := 0
+ eachPeer(s.peers, func(p *Peer, e *list.Element) {
+ if p.inbound {
+ inboundPeers[length] = p
+ length++
+ }
+ })
+
+ return inboundPeers[:length]
+}
+
+func (s *Ethereum) InOutPeers() []*Peer {
+ // Reap the dead peers first
+ s.reapPeers()
+
+ // Create a new peer slice with at least the length of the total peers
+ inboundPeers := make([]*Peer, s.peers.Len())
+ length := 0
+ eachPeer(s.peers, func(p *Peer, e *list.Element) {
+ // Only return peers with an actual ip
+ if len(p.host) > 0 {
+ inboundPeers[length] = p
+ length++
+ }
+ })
+
+ return inboundPeers[:length]
+}
+
+func (s *Ethereum) Broadcast(msgType ethwire.MsgType, data []interface{}) {
+ msg := ethwire.NewMessage(msgType, data)
+ s.BroadcastMsg(msg)
+}
+
+func (s *Ethereum) BroadcastMsg(msg *ethwire.Msg) {
+ eachPeer(s.peers, func(p *Peer, e *list.Element) {
+ p.QueueMessage(msg)
+ })
+}
+
+func (s *Ethereum) Peers() *list.List {
+ return s.peers
+}
+
+func (s *Ethereum) reapPeers() {
+ eachPeer(s.peers, func(p *Peer, e *list.Element) {
+ if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
+ s.removePeerElement(e)
+ }
+ })
+}
+
+func (s *Ethereum) removePeerElement(e *list.Element) {
+ s.peerMut.Lock()
+ defer s.peerMut.Unlock()
+
+ s.peers.Remove(e)
+
+ s.reactor.Post("peerList", s.peers)
+}
+
+func (s *Ethereum) RemovePeer(p *Peer) {
+ eachPeer(s.peers, func(peer *Peer, e *list.Element) {
+ if peer == p {
+ s.removePeerElement(e)
+ }
+ })
+}
+
+func (s *Ethereum) ReapDeadPeerHandler() {
+ reapTimer := time.NewTicker(processReapingTimeout * time.Second)
+
+ for {
+ select {
+ case <-reapTimer.C:
+ s.reapPeers()
+ }
+ }
+}
+
+// Start the ethereum
+func (s *Ethereum) Start(seed bool) {
+ // Bind to addr and port
+ ln, err := net.Listen("tcp", ":"+s.Port)
+ if err != nil {
+ ethlogger.Warnf("Port %s in use. Connection listening disabled. Acting as client", s.Port)
+ s.listening = false
+ } else {
+ s.listening = true
+ // Starting accepting connections
+ ethlogger.Infoln("Ready and accepting connections")
+ // Start the peer handler
+ go s.peerHandler(ln)
+ }
+
+ if s.nat != nil {
+ go s.upnpUpdateThread()
+ }
+
+ // Start the reaping processes
+ go s.ReapDeadPeerHandler()
+
+ if seed {
+ s.Seed()
+ }
+ ethlogger.Infoln("Server started")
+}
+
+func (s *Ethereum) Seed() {
+ ethlogger.Debugln("Retrieving seed nodes")
+
+ // Eth-Go Bootstrapping
+ ips, er := net.LookupIP("seed.bysh.me")
+ if er == nil {
+ peers := []string{}
+ for _, ip := range ips {
+ node := fmt.Sprintf("%s:%d", ip.String(), 30303)
+ ethlogger.Debugln("Found DNS Go Peer:", node)
+ peers = append(peers, node)
+ }
+ s.ProcessPeerList(peers)
+ }
+
+ // Official DNS Bootstrapping
+ _, nodes, err := net.LookupSRV("eth", "tcp", "ethereum.org")
+ if err == nil {
+ peers := []string{}
+ // Iterate SRV nodes
+ for _, n := range nodes {
+ target := n.Target
+ port := strconv.Itoa(int(n.Port))
+ // Resolve target to ip (Go returns list, so may resolve to multiple ips?)
+ addr, err := net.LookupHost(target)
+ if err == nil {
+ for _, a := range addr {
+ // Build string out of SRV port and Resolved IP
+ peer := net.JoinHostPort(a, port)
+ ethlogger.Debugln("Found DNS Bootstrap Peer:", peer)
+ peers = append(peers, peer)
+ }
+ } else {
+ ethlogger.Debugln("Couldn't resolve :", target)
+ }
+ }
+ // Connect to Peer list
+ s.ProcessPeerList(peers)
+ } else {
+ // Fallback to servers.poc3.txt
+ resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt")
+ if err != nil {
+ ethlogger.Warnln("Fetching seed failed:", err)
+ return
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ ethlogger.Warnln("Reading seed failed:", err)
+ return
+ }
+
+ s.ConnectToPeer(string(body))
+ }
+}
+
+func (s *Ethereum) peerHandler(listener net.Listener) {
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ ethlogger.Debugln(err)
+
+ continue
+ }
+
+ go s.AddPeer(conn)
+ }
+}
+
+func (s *Ethereum) Stop() {
+ // Close the database
+ defer s.db.Close()
+
+ eachPeer(s.peers, func(p *Peer, e *list.Element) {
+ p.Stop()
+ })
+
+ close(s.quit)
+
+ if s.RpcServer != nil {
+ s.RpcServer.Stop()
+ }
+ s.txPool.Stop()
+ s.stateManager.Stop()
+
+ ethlogger.Infoln("Server stopped")
+ close(s.shutdownChan)
+}
+
+// This function will wait for a shutdown and resumes main thread execution
+func (s *Ethereum) WaitForShutdown() {
+ <-s.shutdownChan
+}
+
+func (s *Ethereum) upnpUpdateThread() {
+ // Go off immediately to prevent code duplication, thereafter we renew
+ // lease every 15 minutes.
+ timer := time.NewTimer(5 * time.Minute)
+ lport, _ := strconv.ParseInt(s.Port, 10, 16)
+ first := true
+out:
+ for {
+ select {
+ case <-timer.C:
+ var err error
+ _, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
+ if err != nil {
+ ethlogger.Debugln("can't add UPnP port mapping:", err)
+ break out
+ }
+ if first && err == nil {
+ _, err = s.nat.GetExternalAddress()
+ if err != nil {
+ ethlogger.Debugln("UPnP can't get external address:", err)
+ continue out
+ }
+ first = false
+ }
+ timer.Reset(time.Minute * 15)
+ case <-s.quit:
+ break out
+ }
+ }
+
+ timer.Stop()
+
+ if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
+ ethlogger.Debugln("unable to remove UPnP port mapping:", err)
+ } else {
+ ethlogger.Debugln("succesfully disestablished UPnP port mapping")
+ }
+}
diff --git a/ethlog/README.md b/ethlog/README.md
new file mode 100644
index 000000000..d9b69e106
--- /dev/null
+++ b/ethlog/README.md
@@ -0,0 +1,62 @@
+## Features
+
+- packages use tagged logger sending log messages to shared (process-wide) logging engine
+- log writers (interface ethlog.LogSystem) can be added to the logging engine by wrappers/guis/clients
+- shared logging engine dispatching to multiple log systems
+- log level can be set separately per log system
+- async logging thread: logging IO does not block main thread
+- log messages are synchronously stringified to avoid incorrectly logging of changed states
+- log level enum: ethlog.LogLevel: Silence, ErrorLevel, WarnLevel, InfoLevel, DebugLevel, DebugDetailLevel
+
+## Usage
+
+In an ethereum component package:
+
+ import "github.com/ethereum/eth-go/ethlog"
+
+ // package-wide logger using tag
+ var logger = ethlog.NewLogger("TAG")
+
+Logger provides named Printf and Println style methods for all loglevels
+
+ logger.Infoln("this is info") # > [TAG] This is info
+ logger.Infof("this %v is info", object) # > [TAG] This object is info
+
+Ethereum wrappers should register log systems conforming to ethlog.LogSystem
+
+ import "github.com/ethereum/eth-go/ethlog"
+
+ type CustomLogWriter struct {
+ logLevel ethlog.LogLevel
+ }
+
+ func (t *TestLogSystem) SetLogLevel(i LogLevel) {
+ t.level = i
+ }
+
+ func (t *TestLogSystem) GetLogLevel() LogLevel {
+ return t.level
+ }
+
+ func (c *CustomLogWriter) Printf(format string, v...interface{}) {
+ //....
+ }
+
+ func (c *CustomLogWriter) Println(v...interface{}) {
+ //....
+ }
+
+ ethlog.AddLogWriter(&CustomLogWriter{})
+
+ethlog also provides constructors for that wrap io.Writers into a standard logger with a settable level:
+
+ filename := "test.log"
+ file, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
+ fileLogSystem := NewStdLogSystem(file, 0, WarnLevel)
+ AddLogSystem(fileLogSystem)
+ stdOutLogSystem := NewStdLogSystem(os.Stdout, 0, WarnLevel)
+ AddLogSystem(stdOutLogSystem)
+
+
+
+
diff --git a/ethlog/loggers.go b/ethlog/loggers.go
new file mode 100644
index 000000000..9ebe59096
--- /dev/null
+++ b/ethlog/loggers.go
@@ -0,0 +1,188 @@
+package ethlog
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "sync"
+)
+
+type LogSystem interface {
+ GetLogLevel() LogLevel
+ SetLogLevel(i LogLevel)
+ Println(v ...interface{})
+ Printf(format string, v ...interface{})
+}
+
+type logMessage struct {
+ LogLevel LogLevel
+ format bool
+ msg string
+}
+
+func newPrintlnLogMessage(level LogLevel, tag string, v ...interface{}) *logMessage {
+ return &logMessage{level, false, fmt.Sprintf("[%s] %s", tag, fmt.Sprint(v...))}
+}
+
+func newPrintfLogMessage(level LogLevel, tag string, format string, v ...interface{}) *logMessage {
+ return &logMessage{level, true, fmt.Sprintf("[%s] %s", tag, fmt.Sprintf(format, v...))}
+}
+
+func (msg *logMessage) send(logger LogSystem) {
+ if msg.format {
+ logger.Printf(msg.msg)
+ } else {
+ logger.Println(msg.msg)
+ }
+}
+
+var logMessages chan (*logMessage)
+var logSystems []LogSystem
+var drained = true
+
+type LogLevel uint8
+
+const (
+ Silence LogLevel = iota
+ ErrorLevel
+ WarnLevel
+ InfoLevel
+ DebugLevel
+ DebugDetailLevel
+)
+
+// log messages are dispatched to log writers
+func start() {
+ for {
+ select {
+ case msg := <-logMessages:
+ for _, logSystem := range logSystems {
+ if logSystem.GetLogLevel() >= msg.LogLevel {
+ msg.send(logSystem)
+ }
+ }
+ default:
+ drained = true
+ }
+ }
+}
+
+// waits until log messages are drained (dispatched to log writers)
+func Flush() {
+ for !drained {
+ }
+}
+
+type Logger struct {
+ tag string
+}
+
+func NewLogger(tag string) *Logger {
+ return &Logger{tag}
+}
+
+func AddLogSystem(logSystem LogSystem) {
+ var mutex = &sync.Mutex{}
+ mutex.Lock()
+ defer mutex.Unlock()
+ if logSystems == nil {
+ logMessages = make(chan *logMessage)
+ go start()
+ }
+ logSystems = append(logSystems, logSystem)
+}
+
+func (logger *Logger) sendln(level LogLevel, v ...interface{}) {
+ if logMessages != nil {
+ msg := newPrintlnLogMessage(level, logger.tag, v...)
+ drained = false
+ logMessages <- msg
+ }
+}
+
+func (logger *Logger) sendf(level LogLevel, format string, v ...interface{}) {
+ if logMessages != nil {
+ msg := newPrintfLogMessage(level, logger.tag, format, v...)
+ drained = false
+ logMessages <- msg
+ }
+}
+
+func (logger *Logger) Errorln(v ...interface{}) {
+ logger.sendln(ErrorLevel, v...)
+}
+
+func (logger *Logger) Warnln(v ...interface{}) {
+ logger.sendln(WarnLevel, v...)
+}
+
+func (logger *Logger) Infoln(v ...interface{}) {
+ logger.sendln(InfoLevel, v...)
+}
+
+func (logger *Logger) Debugln(v ...interface{}) {
+ logger.sendln(DebugLevel, v...)
+}
+
+func (logger *Logger) DebugDetailln(v ...interface{}) {
+ logger.sendln(DebugDetailLevel, v...)
+}
+
+func (logger *Logger) Errorf(format string, v ...interface{}) {
+ logger.sendf(ErrorLevel, format, v...)
+}
+
+func (logger *Logger) Warnf(format string, v ...interface{}) {
+ logger.sendf(WarnLevel, format, v...)
+}
+
+func (logger *Logger) Infof(format string, v ...interface{}) {
+ logger.sendf(InfoLevel, format, v...)
+}
+
+func (logger *Logger) Debugf(format string, v ...interface{}) {
+ logger.sendf(DebugLevel, format, v...)
+}
+
+func (logger *Logger) DebugDetailf(format string, v ...interface{}) {
+ logger.sendf(DebugDetailLevel, format, v...)
+}
+
+func (logger *Logger) Fatalln(v ...interface{}) {
+ logger.sendln(ErrorLevel, v...)
+ Flush()
+ os.Exit(0)
+}
+
+func (logger *Logger) Fatalf(format string, v ...interface{}) {
+ logger.sendf(ErrorLevel, format, v...)
+ Flush()
+ os.Exit(0)
+}
+
+type StdLogSystem struct {
+ logger *log.Logger
+ level LogLevel
+}
+
+func (t *StdLogSystem) Println(v ...interface{}) {
+ t.logger.Println(v...)
+}
+
+func (t *StdLogSystem) Printf(format string, v ...interface{}) {
+ t.logger.Printf(format, v...)
+}
+
+func (t *StdLogSystem) SetLogLevel(i LogLevel) {
+ t.level = i
+}
+
+func (t *StdLogSystem) GetLogLevel() LogLevel {
+ return t.level
+}
+
+func NewStdLogSystem(writer io.Writer, flags int, level LogLevel) *StdLogSystem {
+ logger := log.New(writer, "", flags)
+ return &StdLogSystem{logger, level}
+}
diff --git a/ethlog/loggers_test.go b/ethlog/loggers_test.go
new file mode 100644
index 000000000..89f416681
--- /dev/null
+++ b/ethlog/loggers_test.go
@@ -0,0 +1,109 @@
+package ethlog
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+type TestLogSystem struct {
+ Output string
+ level LogLevel
+}
+
+func (t *TestLogSystem) Println(v ...interface{}) {
+ t.Output += fmt.Sprintln(v...)
+}
+
+func (t *TestLogSystem) Printf(format string, v ...interface{}) {
+ t.Output += fmt.Sprintf(format, v...)
+}
+
+func (t *TestLogSystem) SetLogLevel(i LogLevel) {
+ t.level = i
+}
+
+func (t *TestLogSystem) GetLogLevel() LogLevel {
+ return t.level
+}
+
+func quote(s string) string {
+ return fmt.Sprintf("'%s'", s)
+}
+
+func TestLoggerPrintln(t *testing.T) {
+ logger := NewLogger("TEST")
+ testLogSystem := &TestLogSystem{level: WarnLevel}
+ AddLogSystem(testLogSystem)
+ logger.Errorln("error")
+ logger.Warnln("warn")
+ logger.Infoln("info")
+ logger.Debugln("debug")
+ Flush()
+ output := testLogSystem.Output
+ fmt.Println(quote(output))
+ if output != "[TEST] error\n[TEST] warn\n" {
+ t.Error("Expected logger output '[TEST] error\\n[TEST] warn\\n', got ", quote(testLogSystem.Output))
+ }
+}
+
+func TestLoggerPrintf(t *testing.T) {
+ logger := NewLogger("TEST")
+ testLogSystem := &TestLogSystem{level: WarnLevel}
+ AddLogSystem(testLogSystem)
+ logger.Errorf("error to %v\n", *testLogSystem)
+ logger.Warnf("warn")
+ logger.Infof("info")
+ logger.Debugf("debug")
+ Flush()
+ output := testLogSystem.Output
+ fmt.Println(quote(output))
+ if output != "[TEST] error to { 2}\n[TEST] warn" {
+ t.Error("Expected logger output '[TEST] error to { 2}\\n[TEST] warn', got ", quote(testLogSystem.Output))
+ }
+}
+
+func TestMultipleLogSystems(t *testing.T) {
+ logger := NewLogger("TEST")
+ testLogSystem0 := &TestLogSystem{level: ErrorLevel}
+ testLogSystem1 := &TestLogSystem{level: WarnLevel}
+ AddLogSystem(testLogSystem0)
+ AddLogSystem(testLogSystem1)
+ logger.Errorln("error")
+ logger.Warnln("warn")
+ Flush()
+ output0 := testLogSystem0.Output
+ output1 := testLogSystem1.Output
+ if output0 != "[TEST] error\n" {
+ t.Error("Expected logger 0 output '[TEST] error\\n', got ", quote(testLogSystem0.Output))
+ }
+ if output1 != "[TEST] error\n[TEST] warn\n" {
+ t.Error("Expected logger 1 output '[TEST] error\\n[TEST] warn\\n', got ", quote(testLogSystem1.Output))
+ }
+}
+
+func TestFileLogSystem(t *testing.T) {
+ logger := NewLogger("TEST")
+ filename := "test.log"
+ file, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
+ testLogSystem := NewStdLogSystem(file, 0, WarnLevel)
+ AddLogSystem(testLogSystem)
+ logger.Errorf("error to %s\n", filename)
+ logger.Warnln("warn")
+ Flush()
+ contents, _ := ioutil.ReadFile(filename)
+ output := string(contents)
+ fmt.Println(quote(output))
+ if output != "[TEST] error to test.log\n[TEST] warn\n" {
+ t.Error("Expected contents of file 'test.log': '[TEST] error to test.log\\n[TEST] warn\\n', got ", quote(output))
+ } else {
+ os.Remove(filename)
+ }
+}
+
+func TestNoLogSystem(t *testing.T) {
+ logger := NewLogger("TEST")
+ logger.Warnln("warn")
+ Flush()
+}
diff --git a/ethminer/miner.go b/ethminer/miner.go
new file mode 100644
index 000000000..71d4b2428
--- /dev/null
+++ b/ethminer/miner.go
@@ -0,0 +1,186 @@
+package ethminer
+
+import (
+ "bytes"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "sort"
+)
+
+var logger = ethlog.NewLogger("MINER")
+
+type Miner struct {
+ pow ethchain.PoW
+ ethereum ethchain.EthManager
+ coinbase []byte
+ reactChan chan ethutil.React
+ txs ethchain.Transactions
+ uncles []*ethchain.Block
+ block *ethchain.Block
+ powChan chan []byte
+ powQuitChan chan ethutil.React
+ quitChan chan bool
+}
+
+func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner {
+ reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in
+ powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block
+ powQuitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread
+ quitChan := make(chan bool, 1)
+
+ ethereum.Reactor().Subscribe("newBlock", reactChan)
+ ethereum.Reactor().Subscribe("newTx:pre", reactChan)
+
+ // We need the quit chan to be a Reactor event.
+ // The POW search method is actually blocking and if we don't
+ // listen to the reactor events inside of the pow itself
+ // The miner overseer will never get the reactor events themselves
+ // Only after the miner will find the sha
+ ethereum.Reactor().Subscribe("newBlock", powQuitChan)
+ ethereum.Reactor().Subscribe("newTx:pre", powQuitChan)
+
+ miner := Miner{
+ pow: &ethchain.EasyPow{},
+ ethereum: ethereum,
+ coinbase: coinbase,
+ reactChan: reactChan,
+ powChan: powChan,
+ powQuitChan: powQuitChan,
+ quitChan: quitChan,
+ }
+
+ // Insert initial TXs in our little miner 'pool'
+ miner.txs = ethereum.TxPool().Flush()
+ miner.block = ethereum.BlockChain().NewBlock(miner.coinbase)
+
+ return miner
+}
+
+func (miner *Miner) Start() {
+ // Prepare inital block
+ //miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
+ go miner.listener()
+ logger.Infoln("Started")
+}
+
+func (miner *Miner) listener() {
+out:
+ for {
+ select {
+ case <-miner.quitChan:
+ logger.Infoln("Stopped")
+ break out
+ case chanMessage := <-miner.reactChan:
+
+ if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
+ //logger.Infoln("Got new block via Reactor")
+ if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 {
+ // TODO: Perhaps continue mining to get some uncle rewards
+ //logger.Infoln("New top block found resetting state")
+
+ // Filter out which Transactions we have that were not in this block
+ var newtxs []*ethchain.Transaction
+ for _, tx := range miner.txs {
+ found := false
+ for _, othertx := range block.Transactions() {
+ if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 {
+ found = true
+ }
+ }
+ if found == false {
+ newtxs = append(newtxs, tx)
+ }
+ }
+ miner.txs = newtxs
+
+ // Setup a fresh state to mine on
+ //miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
+
+ } else {
+ if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 {
+ logger.Infoln("Adding uncle block")
+ miner.uncles = append(miner.uncles, block)
+ }
+ }
+ }
+
+ if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok {
+ found := false
+ for _, ctx := range miner.txs {
+ if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found {
+ break
+ }
+
+ }
+ if found == false {
+ // Undo all previous commits
+ miner.block.Undo()
+ // Apply new transactions
+ miner.txs = append(miner.txs, tx)
+ }
+ }
+ default:
+ miner.mineNewBlock()
+ }
+ }
+}
+
+func (self *Miner) Stop() {
+ logger.Infoln("Stopping...")
+ self.quitChan <- true
+
+ close(self.powQuitChan)
+ close(self.quitChan)
+}
+
+func (self *Miner) mineNewBlock() {
+ stateManager := self.ethereum.StateManager()
+
+ self.block = self.ethereum.BlockChain().NewBlock(self.coinbase)
+
+ // Apply uncles
+ if len(self.uncles) > 0 {
+ self.block.SetUncles(self.uncles)
+ }
+
+ // Sort the transactions by nonce in case of odd network propagation
+ sort.Sort(ethchain.TxByNonce{self.txs})
+
+ // Accumulate all valid transactions and apply them to the new state
+ // Error may be ignored. It's not important during mining
+ parent := self.ethereum.BlockChain().GetBlock(self.block.PrevHash)
+ coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase)
+ coinbase.SetGasPool(self.block.CalcGasLimit(parent))
+ receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs)
+ if err != nil {
+ logger.Debugln(err)
+ }
+ self.txs = append(txs, unhandledTxs...)
+
+ // Set the transactions to the block so the new SHA3 can be calculated
+ self.block.SetReceipts(receipts, txs)
+
+ // Accumulate the rewards included for this block
+ stateManager.AccumelateRewards(self.block.State(), self.block)
+
+ self.block.State().Update()
+
+ logger.Infof("Mining on block. Includes %v transactions", len(self.txs))
+
+ // Find a valid nonce
+ self.block.Nonce = self.pow.Search(self.block, self.powQuitChan)
+ if self.block.Nonce != nil {
+ err := self.ethereum.StateManager().Process(self.block, false)
+ if err != nil {
+ logger.Infoln(err)
+ } else {
+ self.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{self.block.Value().Val})
+ logger.Infof("🔨 Mined block %x\n", self.block.Hash())
+ logger.Infoln(self.block)
+ // Gather the new batch of transactions currently in the tx pool
+ self.txs = self.ethereum.TxPool().CurrentTransactions()
+ }
+ }
+}
diff --git a/ethpub/pub.go b/ethpub/pub.go
new file mode 100644
index 000000000..250ba71f3
--- /dev/null
+++ b/ethpub/pub.go
@@ -0,0 +1,232 @@
+package ethpub
+
+import (
+ "bytes"
+ "encoding/hex"
+ "encoding/json"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "strings"
+ "sync/atomic"
+)
+
+var logger = ethlog.NewLogger("PUB")
+
+type PEthereum struct {
+ manager ethchain.EthManager
+ stateManager *ethchain.StateManager
+ blockChain *ethchain.BlockChain
+ txPool *ethchain.TxPool
+}
+
+func NewPEthereum(manager ethchain.EthManager) *PEthereum {
+ return &PEthereum{
+ manager,
+ manager.StateManager(),
+ manager.BlockChain(),
+ manager.TxPool(),
+ }
+}
+
+func (lib *PEthereum) GetBlock(hexHash string) *PBlock {
+ hash := ethutil.FromHex(hexHash)
+ block := lib.blockChain.GetBlock(hash)
+
+ return NewPBlock(block)
+}
+
+func (lib *PEthereum) GetKey() *PKey {
+ keyPair := ethutil.GetKeyRing().Get(0)
+
+ return NewPKey(keyPair)
+}
+
+func (lib *PEthereum) GetStateObject(address string) *PStateObject {
+ stateObject := lib.stateManager.CurrentState().GetStateObject(ethutil.FromHex(address))
+ if stateObject != nil {
+ return NewPStateObject(stateObject)
+ }
+
+ // See GetStorage for explanation on "nil"
+ return NewPStateObject(nil)
+}
+
+func (lib *PEthereum) GetPeerCount() int {
+ return lib.manager.PeerCount()
+}
+
+func (lib *PEthereum) GetPeers() []PPeer {
+ var peers []PPeer
+ for peer := lib.manager.Peers().Front(); peer != nil; peer = peer.Next() {
+ p := peer.Value.(ethchain.Peer)
+ // we only want connected peers
+ if atomic.LoadInt32(p.Connected()) != 0 {
+ peers = append(peers, *NewPPeer(p))
+ }
+ }
+
+ return peers
+}
+
+func (lib *PEthereum) GetIsMining() bool {
+ return lib.manager.IsMining()
+}
+
+func (lib *PEthereum) GetIsListening() bool {
+ return lib.manager.IsListening()
+}
+
+func (lib *PEthereum) GetCoinBase() string {
+ data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
+ keyRing := ethutil.NewValueFromBytes(data)
+ key := keyRing.Get(0).Bytes()
+
+ return lib.SecretToAddress(hex.EncodeToString(key))
+}
+
+func (lib *PEthereum) GetTransactionsFor(address string, asJson bool) interface{} {
+ sBlk := lib.manager.BlockChain().LastBlockHash
+ blk := lib.manager.BlockChain().GetBlock(sBlk)
+ addr := []byte(ethutil.FromHex(address))
+
+ var txs []*PTx
+
+ for ; blk != nil; blk = lib.manager.BlockChain().GetBlock(sBlk) {
+ sBlk = blk.PrevHash
+
+ // Loop through all transactions to see if we missed any while being offline
+ for _, tx := range blk.Transactions() {
+ if bytes.Compare(tx.Sender(), addr) == 0 || bytes.Compare(tx.Recipient, addr) == 0 {
+ ptx := NewPTx(tx)
+ //TODO: somehow move this to NewPTx
+ ptx.Confirmations = int(lib.manager.BlockChain().LastBlockNumber - blk.BlockInfo().Number)
+ txs = append(txs, ptx)
+ }
+ }
+ }
+ if asJson {
+ txJson, err := json.Marshal(txs)
+ if err != nil {
+ return nil
+ }
+ return string(txJson)
+ }
+ return txs
+}
+
+func (lib *PEthereum) GetStorage(address, storageAddress string) string {
+ return lib.GetStateObject(address).GetStorage(storageAddress)
+}
+
+func (lib *PEthereum) GetTxCountAt(address string) int {
+ return lib.GetStateObject(address).Nonce()
+}
+
+func (lib *PEthereum) IsContract(address string) bool {
+ return lib.GetStateObject(address).IsContract()
+}
+
+func (lib *PEthereum) SecretToAddress(key string) string {
+ pair, err := ethutil.NewKeyPairFromSec(ethutil.FromHex(key))
+ if err != nil {
+ return ""
+ }
+
+ return ethutil.Hex(pair.Address())
+}
+
+func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) {
+ return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr)
+}
+
+func (lib *PEthereum) Create(key, valueStr, gasStr, gasPriceStr, script string) (*PReceipt, error) {
+ return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, script)
+}
+
+var namereg = ethutil.FromHex("bb5f186604d057c1c5240ca2ae0f6430138ac010")
+
+func GetAddressFromNameReg(stateManager *ethchain.StateManager, name string) []byte {
+ recp := new(big.Int).SetBytes([]byte(name))
+ object := stateManager.CurrentState().GetStateObject(namereg)
+ if object != nil {
+ reg := object.GetStorage(recp)
+
+ return reg.Bytes()
+ }
+
+ return nil
+}
+func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, scriptStr string) (*PReceipt, error) {
+ var hash []byte
+ var contractCreation bool
+ if len(recipient) == 0 {
+ contractCreation = true
+ } else {
+ // Check if an address is stored by this address
+ addr := GetAddressFromNameReg(lib.stateManager, recipient)
+ if len(addr) > 0 {
+ hash = addr
+ } else {
+ hash = ethutil.FromHex(recipient)
+ }
+ }
+
+ var keyPair *ethutil.KeyPair
+ var err error
+ if key[0:2] == "0x" {
+ keyPair, err = ethutil.NewKeyPairFromSec([]byte(ethutil.FromHex(key[2:])))
+ } else {
+ keyPair, err = ethutil.NewKeyPairFromSec([]byte(ethutil.FromHex(key)))
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ value := ethutil.Big(valueStr)
+ gas := ethutil.Big(gasStr)
+ gasPrice := ethutil.Big(gasPriceStr)
+ var tx *ethchain.Transaction
+ // Compile and assemble the given data
+ if contractCreation {
+ var script []byte
+ var err error
+ if ethutil.IsHex(scriptStr) {
+ script = ethutil.FromHex(scriptStr)
+ } else {
+ script, err = ethutil.Compile(scriptStr)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script)
+ } else {
+ data := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
+ slice := strings.Split(s, "\n")
+ for _, dataItem := range slice {
+ d := ethutil.FormatData(dataItem)
+ ret = append(ret, d...)
+ }
+ return
+ })
+
+ tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data)
+ }
+
+ acc := lib.stateManager.TransState().GetOrNewStateObject(keyPair.Address())
+ tx.Nonce = acc.Nonce
+ acc.Nonce += 1
+ lib.stateManager.TransState().UpdateStateObject(acc)
+
+ tx.Sign(keyPair.PrivateKey)
+ lib.txPool.QueueTransaction(tx)
+
+ if contractCreation {
+ logger.Infof("Contract addr %x", tx.CreationAddress())
+ }
+
+ return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil
+}
diff --git a/ethpub/types.go b/ethpub/types.go
new file mode 100644
index 000000000..0ced68ad1
--- /dev/null
+++ b/ethpub/types.go
@@ -0,0 +1,264 @@
+package ethpub
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+ "strings"
+)
+
+// Peer interface exposed to QML
+
+type PPeer struct {
+ ref *ethchain.Peer
+ Inbound bool `json:"isInbound"`
+ LastSend int64 `json:"lastSend"`
+ LastPong int64 `json:"lastPong"`
+ Ip string `json:"ip"`
+ Port int `json:"port"`
+ Version string `json:"version"`
+ LastResponse string `json:"lastResponse"`
+ Latency string `json:"latency"`
+}
+
+func NewPPeer(peer ethchain.Peer) *PPeer {
+ if peer == nil {
+ return nil
+ }
+
+ // TODO: There must be something build in to do this?
+ var ip []string
+ for _, i := range peer.Host() {
+ ip = append(ip, fmt.Sprintf("%d", i))
+ }
+ ipAddress := strings.Join(ip, ".")
+
+ return &PPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime()}
+}
+
+// Block interface exposed to QML
+type PBlock struct {
+ ref *ethchain.Block
+ Number int `json:"number"`
+ Hash string `json:"hash"`
+ Transactions string `json:"transactions"`
+ Time int64 `json:"time"`
+ Coinbase string `json:"coinbase"`
+ GasLimit string `json:"gasLimit"`
+ GasUsed string `json:"gasUsed"`
+}
+
+// Creates a new QML Block from a chain block
+func NewPBlock(block *ethchain.Block) *PBlock {
+ if block == nil {
+ return nil
+ }
+
+ var ptxs []PTx
+ for _, tx := range block.Transactions() {
+ ptxs = append(ptxs, *NewPTx(tx))
+ }
+
+ txJson, err := json.Marshal(ptxs)
+ if err != nil {
+ return nil
+ }
+
+ return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Hex(block.Coinbase)}
+}
+
+func (self *PBlock) ToString() string {
+ if self.ref != nil {
+ return self.ref.String()
+ }
+
+ return ""
+}
+
+func (self *PBlock) GetTransaction(hash string) *PTx {
+ tx := self.ref.GetTransaction(ethutil.FromHex(hash))
+ if tx == nil {
+ return nil
+ }
+
+ return NewPTx(tx)
+}
+
+type PTx struct {
+ ref *ethchain.Transaction
+
+ Value string `json:"value"`
+ Gas string `json:"gas"`
+ GasPrice string `json:"gasPrice"`
+ Hash string `json:"hash"`
+ Address string `json:"address"`
+ Sender string `json:"sender"`
+ RawData string `json:"rawData"`
+ Data string `json:"data"`
+ Contract bool `json:"isContract"`
+ CreatesContract bool `json:"createsContract"`
+ Confirmations int `json:"confirmations"`
+}
+
+func NewPTx(tx *ethchain.Transaction) *PTx {
+ hash := hex.EncodeToString(tx.Hash())
+ receiver := hex.EncodeToString(tx.Recipient)
+ if receiver == "0000000000000000000000000000000000000000" {
+ receiver = hex.EncodeToString(tx.CreationAddress())
+ }
+ sender := hex.EncodeToString(tx.Sender())
+ createsContract := tx.CreatesContract()
+
+ var data string
+ if tx.CreatesContract() {
+ data = strings.Join(ethchain.Disassemble(tx.Data), "\n")
+ } else {
+ data = hex.EncodeToString(tx.Data)
+ }
+
+ return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)}
+}
+
+func (self *PTx) ToString() string {
+ return self.ref.String()
+}
+
+type PKey struct {
+ Address string `json:"address"`
+ PrivateKey string `json:"privateKey"`
+ PublicKey string `json:"publicKey"`
+}
+
+func NewPKey(key *ethutil.KeyPair) *PKey {
+ return &PKey{ethutil.Hex(key.Address()), ethutil.Hex(key.PrivateKey), ethutil.Hex(key.PublicKey)}
+}
+
+type PReceipt struct {
+ CreatedContract bool `json:"createdContract"`
+ Address string `json:"address"`
+ Hash string `json:"hash"`
+ Sender string `json:"sender"`
+}
+
+func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
+ return &PReceipt{
+ contractCreation,
+ ethutil.Hex(creationAddress),
+ ethutil.Hex(hash),
+ ethutil.Hex(address),
+ }
+}
+
+type PStateObject struct {
+ object *ethchain.StateObject
+}
+
+func NewPStateObject(object *ethchain.StateObject) *PStateObject {
+ return &PStateObject{object: object}
+}
+
+func (c *PStateObject) GetStorage(address string) string {
+ // Because somehow, even if you return nil to QML it
+ // still has some magical object so we can't rely on
+ // undefined or null at the QML side
+ if c.object != nil {
+ val := c.object.GetMem(ethutil.Big("0x" + address))
+
+ return val.BigInt().String()
+ }
+
+ return ""
+}
+
+func (c *PStateObject) Value() string {
+ if c.object != nil {
+ return c.object.Amount.String()
+ }
+
+ return ""
+}
+
+func (c *PStateObject) Address() string {
+ if c.object != nil {
+ return ethutil.Hex(c.object.Address())
+ }
+
+ return ""
+}
+
+func (c *PStateObject) Nonce() int {
+ if c.object != nil {
+ return int(c.object.Nonce)
+ }
+
+ return 0
+}
+
+func (c *PStateObject) Root() string {
+ if c.object != nil {
+ return ethutil.Hex(ethutil.NewValue(c.object.State().Root()).Bytes())
+ }
+
+ return "<err>"
+}
+
+func (c *PStateObject) IsContract() bool {
+ if c.object != nil {
+ return len(c.object.Script()) > 0
+ }
+
+ return false
+}
+
+type KeyVal struct {
+ Key string
+ Value string
+}
+
+func (c *PStateObject) StateKeyVal(asJson bool) interface{} {
+ var values []KeyVal
+ if c.object != nil {
+ c.object.State().EachStorage(func(name string, value *ethutil.Value) {
+ values = append(values, KeyVal{name, ethutil.Hex(value.Bytes())})
+ })
+ }
+
+ if asJson {
+ valuesJson, err := json.Marshal(values)
+ if err != nil {
+ return nil
+ }
+ fmt.Println(string(valuesJson))
+ return string(valuesJson)
+ }
+
+ return values
+}
+
+func (c *PStateObject) Script() string {
+ if c.object != nil {
+ return strings.Join(ethchain.Disassemble(c.object.Script()), " ")
+ }
+
+ return ""
+}
+
+func (c *PStateObject) HexScript() string {
+ if c.object != nil {
+ return ethutil.Hex(c.object.Script())
+ }
+
+ return ""
+}
+
+type PStorageState struct {
+ StateAddress string
+ Address string
+ Value string
+}
+
+func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState {
+ return &PStorageState{ethutil.Hex(storageObject.StateAddress), ethutil.Hex(storageObject.Address), storageObject.Value.String()}
+}
diff --git a/ethrpc/packages.go b/ethrpc/packages.go
new file mode 100644
index 000000000..710275780
--- /dev/null
+++ b/ethrpc/packages.go
@@ -0,0 +1,287 @@
+package ethrpc
+
+import (
+ "encoding/json"
+ "errors"
+ "github.com/ethereum/eth-go/ethpub"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "strings"
+)
+
+type EthereumApi struct {
+ ethp *ethpub.PEthereum
+}
+
+type JsonArgs interface {
+ requirements() error
+}
+
+type BlockResponse struct {
+ JsonResponse
+}
+type GetBlockArgs struct {
+ BlockNumber int
+ Hash string
+}
+
+type ErrorResponse struct {
+ Error bool `json:"error"`
+ ErrorText string `json:"errorText"`
+}
+
+type JsonResponse interface {
+}
+
+type SuccessRes struct {
+ Error bool `json:"error"`
+ Result JsonResponse `json:"result"`
+}
+
+func NewSuccessRes(object JsonResponse) string {
+ e := SuccessRes{Error: false, Result: object}
+ res, err := json.Marshal(e)
+ if err != nil {
+ // This should never happen
+ panic("Creating json error response failed, help")
+ }
+ success := string(res)
+ return success
+}
+
+func NewErrorResponse(msg string) error {
+ e := ErrorResponse{Error: true, ErrorText: msg}
+ res, err := json.Marshal(e)
+ if err != nil {
+ // This should never happen
+ panic("Creating json error response failed, help")
+ }
+ newErr := errors.New(string(res))
+ return newErr
+}
+
+func (b *GetBlockArgs) requirements() error {
+ if b.BlockNumber == 0 && b.Hash == "" {
+ return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument")
+ }
+ return nil
+}
+
+func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error {
+ err := args.requirements()
+ if err != nil {
+ return err
+ }
+ // Do something
+ block := p.ethp.GetBlock(args.Hash)
+ *reply = NewSuccessRes(block)
+ return nil
+}
+
+type NewTxArgs struct {
+ Sec string
+ Recipient string
+ Value string
+ Gas string
+ GasPrice string
+ Init string
+ Body string
+}
+type TxResponse struct {
+ Hash string
+}
+
+func (a *NewTxArgs) requirements() error {
+ if a.Recipient == "" {
+ return NewErrorResponse("Transact requires a 'recipient' address as argument")
+ }
+ if a.Value == "" {
+ return NewErrorResponse("Transact requires a 'value' as argument")
+ }
+ if a.Gas == "" {
+ return NewErrorResponse("Transact requires a 'gas' value as argument")
+ }
+ if a.GasPrice == "" {
+ return NewErrorResponse("Transact requires a 'gasprice' value as argument")
+ }
+ return nil
+}
+
+func (a *NewTxArgs) requirementsContract() error {
+ if a.Value == "" {
+ return NewErrorResponse("Create requires a 'value' as argument")
+ }
+ if a.Gas == "" {
+ return NewErrorResponse("Create requires a 'gas' value as argument")
+ }
+ if a.GasPrice == "" {
+ return NewErrorResponse("Create requires a 'gasprice' value as argument")
+ }
+ if a.Body == "" {
+ return NewErrorResponse("Create requires a 'body' value as argument")
+ }
+ return nil
+}
+
+func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error {
+ err := args.requirements()
+ if err != nil {
+ return err
+ }
+ result, _ := p.ethp.Transact(p.ethp.GetKey().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body)
+ *reply = NewSuccessRes(result)
+ return nil
+}
+
+func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error {
+ err := args.requirementsContract()
+ if err != nil {
+ return err
+ }
+ result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Body)
+ *reply = NewSuccessRes(result)
+ return nil
+}
+
+func (p *EthereumApi) GetKey(args interface{}, reply *string) error {
+ *reply = NewSuccessRes(p.ethp.GetKey())
+ return nil
+}
+
+type GetStorageArgs struct {
+ Address string
+ Key string
+}
+
+func (a *GetStorageArgs) requirements() error {
+ if a.Address == "" {
+ return NewErrorResponse("GetStorageAt requires an 'address' value as argument")
+ }
+ if a.Key == "" {
+ return NewErrorResponse("GetStorageAt requires an 'key' value as argument")
+ }
+ return nil
+}
+
+type GetStorageAtRes struct {
+ Key string `json:"key"`
+ Value string `json:"value"`
+ Address string `json:"address"`
+}
+
+func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error {
+ err := args.requirements()
+ if err != nil {
+ return err
+ }
+ state := p.ethp.GetStateObject(args.Address)
+
+ var hx string
+ if strings.Index(args.Key, "0x") == 0 {
+ hx = string([]byte(args.Key)[2:])
+ } else {
+ // Convert the incoming string (which is a bigint) into hex
+ i, _ := new(big.Int).SetString(args.Key, 10)
+ hx = ethutil.Hex(i.Bytes())
+ }
+ logger.Debugf("GetStorageAt(%s, %s)\n", args.Address, hx)
+ value := state.GetStorage(hx)
+ *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value})
+ return nil
+}
+
+type GetTxCountArgs struct {
+ Address string `json:"address"`
+}
+type GetTxCountRes struct {
+ Nonce int `json:"nonce"`
+}
+
+func (a *GetTxCountArgs) requirements() error {
+ if a.Address == "" {
+ return NewErrorResponse("GetTxCountAt requires an 'address' value as argument")
+ }
+ return nil
+}
+
+type GetPeerCountRes struct {
+ PeerCount int `json:"peerCount"`
+}
+
+func (p *EthereumApi) GetPeerCount(args *interface{}, reply *string) error {
+ *reply = NewSuccessRes(GetPeerCountRes{PeerCount: p.ethp.GetPeerCount()})
+ return nil
+}
+
+type GetListeningRes struct {
+ IsListening bool `json:"isListening"`
+}
+
+func (p *EthereumApi) GetIsListening(args *interface{}, reply *string) error {
+ *reply = NewSuccessRes(GetListeningRes{IsListening: p.ethp.GetIsListening()})
+ return nil
+}
+
+type GetCoinbaseRes struct {
+ Coinbase string `json:"coinbase"`
+}
+
+func (p *EthereumApi) GetCoinbase(args *interface{}, reply *string) error {
+ *reply = NewSuccessRes(GetCoinbaseRes{Coinbase: p.ethp.GetCoinBase()})
+ return nil
+}
+
+type GetMiningRes struct {
+ IsMining bool `json:"isMining"`
+}
+
+func (p *EthereumApi) GetIsMining(args *interface{}, reply *string) error {
+ *reply = NewSuccessRes(GetMiningRes{IsMining: p.ethp.GetIsMining()})
+ return nil
+}
+
+func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *string) error {
+ err := args.requirements()
+ if err != nil {
+ return err
+ }
+ state := p.ethp.GetTxCountAt(args.Address)
+ *reply = NewSuccessRes(GetTxCountRes{Nonce: state})
+ return nil
+}
+
+type GetBalanceArgs struct {
+ Address string
+}
+
+func (a *GetBalanceArgs) requirements() error {
+ if a.Address == "" {
+ return NewErrorResponse("GetBalanceAt requires an 'address' value as argument")
+ }
+ return nil
+}
+
+type BalanceRes struct {
+ Balance string `json:"balance"`
+ Address string `json:"address"`
+}
+
+func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error {
+ err := args.requirements()
+ if err != nil {
+ return err
+ }
+ state := p.ethp.GetStateObject(args.Address)
+ *reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address})
+ return nil
+}
+
+type TestRes struct {
+ JsonResponse `json:"-"`
+ Answer int `json:"answer"`
+}
+
+func (p *EthereumApi) Test(args *GetBlockArgs, reply *string) error {
+ *reply = NewSuccessRes(TestRes{Answer: 15})
+ return nil
+}
diff --git a/ethrpc/server.go b/ethrpc/server.go
new file mode 100644
index 000000000..d9d6f695b
--- /dev/null
+++ b/ethrpc/server.go
@@ -0,0 +1,66 @@
+package ethrpc
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethpub"
+ "net"
+ "net/rpc"
+ "net/rpc/jsonrpc"
+)
+
+var logger = ethlog.NewLogger("JSON")
+
+type JsonRpcServer struct {
+ quit chan bool
+ listener net.Listener
+ ethp *ethpub.PEthereum
+}
+
+func (s *JsonRpcServer) exitHandler() {
+out:
+ for {
+ select {
+ case <-s.quit:
+ s.listener.Close()
+ break out
+ }
+ }
+
+ logger.Infoln("Shutdown JSON-RPC server")
+}
+
+func (s *JsonRpcServer) Stop() {
+ close(s.quit)
+}
+
+func (s *JsonRpcServer) Start() {
+ logger.Infoln("Starting JSON-RPC server")
+ go s.exitHandler()
+ rpc.Register(&EthereumApi{ethp: s.ethp})
+ rpc.HandleHTTP()
+
+ for {
+ conn, err := s.listener.Accept()
+ if err != nil {
+ logger.Infoln("Error starting JSON-RPC:", err)
+ break
+ }
+ logger.Debugln("Incoming request.")
+ go jsonrpc.ServeConn(conn)
+ }
+}
+
+func NewJsonRpcServer(ethp *ethpub.PEthereum, port int) (*JsonRpcServer, error) {
+ sport := fmt.Sprintf(":%d", port)
+ l, err := net.Listen("tcp", sport)
+ if err != nil {
+ return nil, err
+ }
+
+ return &JsonRpcServer{
+ listener: l,
+ quit: make(chan bool),
+ ethp: ethp,
+ }, nil
+}
diff --git a/ethutil/.gitignore b/ethutil/.gitignore
new file mode 100644
index 000000000..f725d58d1
--- /dev/null
+++ b/ethutil/.gitignore
@@ -0,0 +1,12 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile ~/.gitignore_global
+
+/tmp
+*/**/*un~
+*un~
+.DS_Store
+*/**/.DS_Store
+
diff --git a/ethutil/.travis.yml b/ethutil/.travis.yml
new file mode 100644
index 000000000..69359072d
--- /dev/null
+++ b/ethutil/.travis.yml
@@ -0,0 +1,3 @@
+language: go
+go:
+ - 1.2
diff --git a/ethutil/README.md b/ethutil/README.md
new file mode 100644
index 000000000..1ed56b71b
--- /dev/null
+++ b/ethutil/README.md
@@ -0,0 +1,139 @@
+# ethutil
+
+[![Build
+Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
+
+The ethutil package contains the ethereum utility library.
+
+# Installation
+
+`go get github.com/ethereum/ethutil-go`
+
+# Usage
+
+## RLP (Recursive Linear Prefix) Encoding
+
+RLP Encoding is an encoding scheme utilized by the Ethereum project. It
+encodes any native value or list to string.
+
+More in depth information about the Encoding scheme see the [Wiki](http://wiki.ethereum.org/index.php/RLP)
+article.
+
+```go
+rlp := ethutil.Encode("doge")
+fmt.Printf("%q\n", rlp) // => "\0x83dog"
+
+rlp = ethutil.Encode([]interface{}{"dog", "cat"})
+fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat"
+decoded := ethutil.Decode(rlp)
+fmt.Println(decoded) // => ["dog" "cat"]
+```
+
+## Patricia Trie
+
+Patricie Tree is a merkle trie utilized by the Ethereum project.
+
+More in depth information about the (modified) Patricia Trie can be
+found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree).
+
+The patricia trie uses a db as backend and could be anything as long as
+it satisfies the Database interface found in `ethutil/db.go`.
+
+```go
+db := NewDatabase()
+
+// db, root
+trie := ethutil.NewTrie(db, "")
+
+trie.Put("puppy", "dog")
+trie.Put("horse", "stallion")
+trie.Put("do", "verb")
+trie.Put("doge", "coin")
+
+// Look up the key "do" in the trie
+out := trie.Get("do")
+fmt.Println(out) // => verb
+
+trie.Delete("puppy")
+```
+
+The patricia trie, in combination with RLP, provides a robust,
+cryptographically authenticated data structure that can be used to store
+all (key, value) bindings.
+
+```go
+// ... Create db/trie
+
+// Note that RLP uses interface slices as list
+value := ethutil.Encode([]interface{}{"one", 2, "three", []interface{}{42}})
+// Store the RLP encoded value of the list
+trie.Put("mykey", value)
+```
+
+## Value
+
+Value is a Generic Value which is used in combination with RLP data or
+`([])interface{}` structures. It may serve as a bridge between RLP data
+and actual real values and takes care of all the type checking and
+casting. Unlike Go's `reflect.Value` it does not panic if it's unable to
+cast to the requested value. It simple returns the base value of that
+type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
+
+### Creating a new Value
+
+`NewEmptyValue()` returns a new \*Value with it's initial value set to a
+`[]interface{}`
+
+`AppendList()` appends a list to the current value.
+
+`Append(v)` appends the value (v) to the current value/list.
+
+```go
+val := ethutil.NewEmptyValue().Append(1).Append("2")
+val.AppendList().Append(3)
+```
+
+### Retrieving values
+
+`Get(i)` returns the `i` item in the list.
+
+`Uint()` returns the value as an unsigned int64.
+
+`Slice()` returns the value as a interface slice.
+
+`Str()` returns the value as a string.
+
+`Bytes()` returns the value as a byte slice.
+
+`Len()` assumes current to be a slice and returns its length.
+
+`Byte()` returns the value as a single byte.
+
+```go
+val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}})
+val.Get(0).Uint() // => 1
+val.Get(1).Str() // => "2"
+s := val.Get(2) // => Value([]interface{}{3})
+s.Get(0).Uint() // => 3
+```
+
+## Decoding
+
+Decoding streams of RLP data is simplified
+
+```go
+val := ethutil.NewValueFromBytes(rlpData)
+val.Get(0).Uint()
+```
+
+## Encoding
+
+Encoding from Value to RLP is done with the `Encode` method. The
+underlying value can be anything RLP can encode (int, str, lists, bytes)
+
+```go
+val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}})
+rlp := val.Encode()
+// Store the rlp data
+Store(rlp)
+```
diff --git a/ethutil/big.go b/ethutil/big.go
new file mode 100644
index 000000000..7af6f7414
--- /dev/null
+++ b/ethutil/big.go
@@ -0,0 +1,75 @@
+package ethutil
+
+import (
+ "math/big"
+)
+
+var BigInt0 *big.Int = big.NewInt(0)
+
+// True
+var BigTrue *big.Int = big.NewInt(1)
+
+// False
+var BigFalse *big.Int = big.NewInt(0)
+
+// Big pow
+//
+// Returns the power of two big integers
+func BigPow(a, b int) *big.Int {
+ c := new(big.Int)
+ c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0))
+
+ return c
+}
+
+// Big
+//
+// Shortcut for new(big.Int).SetString(..., 0)
+func Big(num string) *big.Int {
+ n := new(big.Int)
+ n.SetString(num, 0)
+
+ return n
+}
+
+// BigD
+//
+// Shortcut for new(big.Int).SetBytes(...)
+func BigD(data []byte) *big.Int {
+ n := new(big.Int)
+ n.SetBytes(data)
+
+ return n
+}
+
+// Big to bytes
+//
+// Returns the bytes of a big integer with the size specified by **base**
+// Attempts to pad the byte array with zeros.
+func BigToBytes(num *big.Int, base int) []byte {
+ ret := make([]byte, base/8)
+
+ if len(num.Bytes()) > base/8 {
+ return num.Bytes()
+ }
+
+ return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
+}
+
+// Big copy
+//
+// Creates a copy of the given big integer
+func BigCopy(src *big.Int) *big.Int {
+ return new(big.Int).Set(src)
+}
+
+// Big max
+//
+// Returns the maximum size big integer
+func BigMax(x, y *big.Int) *big.Int {
+ if x.Cmp(y) <= 0 {
+ return y
+ }
+
+ return x
+}
diff --git a/ethutil/bytes.go b/ethutil/bytes.go
new file mode 100644
index 000000000..5e3ee4a6f
--- /dev/null
+++ b/ethutil/bytes.go
@@ -0,0 +1,119 @@
+package ethutil
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math/big"
+ "strings"
+)
+
+// Number to bytes
+//
+// Returns the number in bytes with the specified base
+func NumberToBytes(num interface{}, bits int) []byte {
+ buf := new(bytes.Buffer)
+ err := binary.Write(buf, binary.BigEndian, num)
+ if err != nil {
+ fmt.Println("NumberToBytes failed:", err)
+ }
+
+ return buf.Bytes()[buf.Len()-(bits/8):]
+}
+
+// Bytes to number
+//
+// Attempts to cast a byte slice to a unsigned integer
+func BytesToNumber(b []byte) uint64 {
+ var number uint64
+
+ // Make sure the buffer is 64bits
+ data := make([]byte, 8)
+ data = append(data[:len(b)], b...)
+
+ buf := bytes.NewReader(data)
+ err := binary.Read(buf, binary.BigEndian, &number)
+ if err != nil {
+ fmt.Println("BytesToNumber failed:", err)
+ }
+
+ return number
+}
+
+// Read variable int
+//
+// Read a variable length number in big endian byte order
+func ReadVarint(reader *bytes.Reader) (ret uint64) {
+ if reader.Len() == 8 {
+ var num uint64
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ } else if reader.Len() == 4 {
+ var num uint32
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ } else if reader.Len() == 2 {
+ var num uint16
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ } else {
+ var num uint8
+ binary.Read(reader, binary.BigEndian, &num)
+ ret = uint64(num)
+ }
+
+ return ret
+}
+
+// Binary length
+//
+// Returns the true binary length of the given number
+func BinaryLength(num int) int {
+ if num == 0 {
+ return 0
+ }
+
+ return 1 + BinaryLength(num>>8)
+}
+
+// Copy bytes
+//
+// Returns an exact copy of the provided bytes
+func CopyBytes(b []byte) (copiedBytes []byte) {
+ copiedBytes = make([]byte, len(b))
+ copy(copiedBytes, b)
+
+ return
+}
+
+func IsHex(str string) bool {
+ l := len(str)
+ return l >= 4 && l%2 == 0 && str[0:2] == "0x"
+}
+
+func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) {
+ if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") {
+ ret = FromHex(str[2:])
+ } else {
+ ret = cb(str)
+ }
+
+ return
+}
+
+func FormatData(data string) []byte {
+ if len(data) == 0 {
+ return nil
+ }
+ // Simple stupid
+ d := new(big.Int)
+ if data[0:1] == "\"" && data[len(data)-1:] == "\"" {
+ d.SetBytes([]byte(data[1 : len(data)-1]))
+ } else if len(data) > 1 && data[:2] == "0x" {
+ d.SetBytes(FromHex(data[2:]))
+ } else {
+ d.SetString(data, 0)
+ }
+
+ return BigToBytes(d, 256)
+}
diff --git a/ethutil/common.go b/ethutil/common.go
new file mode 100644
index 000000000..f63ba5d83
--- /dev/null
+++ b/ethutil/common.go
@@ -0,0 +1,61 @@
+package ethutil
+
+import (
+ "fmt"
+ "math/big"
+)
+
+// The different number of units
+var (
+ Douglas = BigPow(10, 42)
+ Einstein = BigPow(10, 21)
+ Ether = BigPow(10, 18)
+ Finney = BigPow(10, 15)
+ Szabo = BigPow(10, 12)
+ Shannon = BigPow(10, 9)
+ Babbage = BigPow(10, 6)
+ Ada = BigPow(10, 3)
+ Wei = big.NewInt(1)
+)
+
+//
+// Currency to string
+// Returns a string representing a human readable format
+func CurrencyToString(num *big.Int) string {
+ switch {
+ case num.Cmp(Douglas) >= 0:
+ return fmt.Sprintf("%v Douglas", new(big.Int).Div(num, Douglas))
+ case num.Cmp(Einstein) >= 0:
+ return fmt.Sprintf("%v Einstein", new(big.Int).Div(num, Einstein))
+ 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(Shannon) >= 0:
+ return fmt.Sprintf("%v Shannon", new(big.Int).Div(num, Shannon))
+ case num.Cmp(Babbage) >= 0:
+ return fmt.Sprintf("%v Babbage", new(big.Int).Div(num, Babbage))
+ case num.Cmp(Ada) >= 0:
+ return fmt.Sprintf("%v Ada", new(big.Int).Div(num, Ada))
+ }
+
+ return fmt.Sprintf("%v Wei", num)
+}
+
+// Common big integers often used
+var (
+ Big1 = big.NewInt(1)
+ Big2 = big.NewInt(2)
+ Big0 = big.NewInt(0)
+ Big32 = big.NewInt(32)
+ Big256 = big.NewInt(0xff)
+)
+
+// Creates an ethereum address given the bytes and the nonce
+func CreateAddress(b []byte, nonce *big.Int) []byte {
+ addrBytes := append(b, nonce.Bytes()...)
+
+ return Sha3Bin(addrBytes)[12:]
+}
diff --git a/ethutil/common_test.go b/ethutil/common_test.go
new file mode 100644
index 000000000..2667eaf3a
--- /dev/null
+++ b/ethutil/common_test.go
@@ -0,0 +1,44 @@
+package ethutil
+
+import (
+ "math/big"
+ "testing"
+)
+
+func TestCommon(t *testing.T) {
+ ether := CurrencyToString(BigPow(10, 19))
+ finney := CurrencyToString(BigPow(10, 16))
+ szabo := CurrencyToString(BigPow(10, 13))
+ vito := CurrencyToString(BigPow(10, 10))
+ turing := CurrencyToString(BigPow(10, 7))
+ eins := CurrencyToString(BigPow(10, 4))
+ wei := CurrencyToString(big.NewInt(10))
+
+ if ether != "10 Ether" {
+ t.Error("Got", ether)
+ }
+
+ if finney != "10 Finney" {
+ t.Error("Got", finney)
+ }
+
+ if szabo != "10 Szabo" {
+ t.Error("Got", szabo)
+ }
+
+ if vito != "10 Shannon" {
+ t.Error("Got", vito)
+ }
+
+ if turing != "10 Babbage" {
+ t.Error("Got", turing)
+ }
+
+ if eins != "10 Ada" {
+ t.Error("Got", eins)
+ }
+
+ if wei != "10 Wei" {
+ t.Error("Got", wei)
+ }
+}
diff --git a/ethutil/config.go b/ethutil/config.go
new file mode 100644
index 000000000..6ebb5e8cd
--- /dev/null
+++ b/ethutil/config.go
@@ -0,0 +1,78 @@
+package ethutil
+
+import (
+ "flag"
+ "fmt"
+ "github.com/rakyll/globalconf"
+ "os"
+ "runtime"
+)
+
+// Config struct
+type config struct {
+ Db Database
+
+ ExecPath string
+ Debug bool
+ Paranoia bool
+ Ver string
+ ClientString string
+ Pubkey []byte
+ Identifier string
+
+ conf *globalconf.GlobalConf
+}
+
+var Config *config
+
+// Read config
+//
+// Initialize Config from Config File
+func ReadConfig(ConfigFile string, Datadir string, Identifier string, EnvPrefix string) *config {
+ if Config == nil {
+ // create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags
+ _, err := os.Stat(ConfigFile)
+ if err != nil && os.IsNotExist(err) {
+ fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile)
+ os.Create(ConfigFile)
+ }
+ g, err := globalconf.NewWithOptions(&globalconf.Options{
+ Filename: ConfigFile,
+ EnvPrefix: EnvPrefix,
+ })
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ g.ParseAll()
+ }
+ Config = &config{ExecPath: Datadir, Debug: true, Ver: "0.5.15", conf: g, Identifier: Identifier, Paranoia: true}
+ Config.SetClientString("Ethereum(G)")
+ }
+ return Config
+}
+
+// Set client string
+//
+func (c *config) SetClientString(str string) {
+ os := runtime.GOOS
+ cust := c.Identifier
+ Config.ClientString = fmt.Sprintf("%s/v%s/%s/%s/Go", str, c.Ver, cust, os)
+}
+
+func (c *config) SetIdentifier(id string) {
+ c.Identifier = id
+ c.Set("id", id)
+}
+
+// provides persistence for flags
+func (c *config) Set(key, value string) {
+ f := &flag.Flag{Name: key, Value: &confValue{value}}
+ c.conf.Set("", f)
+}
+
+type confValue struct {
+ value string
+}
+
+func (self confValue) String() string { return self.value }
+func (self confValue) Set(s string) error { self.value = s; return nil }
diff --git a/ethutil/db.go b/ethutil/db.go
new file mode 100644
index 000000000..e02a80fca
--- /dev/null
+++ b/ethutil/db.go
@@ -0,0 +1,12 @@
+package ethutil
+
+// Database interface
+type Database interface {
+ Put(key []byte, value []byte)
+ Get(key []byte) ([]byte, error)
+ //GetKeys() []*Key
+ Delete(key []byte) error
+ LastKnownTD() []byte
+ Close()
+ Print()
+}
diff --git a/ethutil/encoding.go b/ethutil/encoding.go
new file mode 100644
index 000000000..9fcdf3edf
--- /dev/null
+++ b/ethutil/encoding.go
@@ -0,0 +1,76 @@
+package ethutil
+
+import (
+ "bytes"
+ "encoding/hex"
+ "strings"
+)
+
+func CompactEncode(hexSlice []int) string {
+ terminator := 0
+ if hexSlice[len(hexSlice)-1] == 16 {
+ terminator = 1
+ }
+
+ if terminator == 1 {
+ hexSlice = hexSlice[:len(hexSlice)-1]
+ }
+
+ oddlen := len(hexSlice) % 2
+ flags := 2*terminator + oddlen
+ if oddlen != 0 {
+ hexSlice = append([]int{flags}, hexSlice...)
+ } else {
+ hexSlice = append([]int{flags, 0}, hexSlice...)
+ }
+
+ var buff bytes.Buffer
+ for i := 0; i < len(hexSlice); i += 2 {
+ buff.WriteByte(byte(16*hexSlice[i] + hexSlice[i+1]))
+ }
+
+ return buff.String()
+}
+
+func CompactDecode(str string) []int {
+ base := CompactHexDecode(str)
+ base = base[:len(base)-1]
+ if base[0] >= 2 {
+ base = append(base, 16)
+ }
+ if base[0]%2 == 1 {
+ base = base[1:]
+ } else {
+ base = base[2:]
+ }
+
+ return base
+}
+
+func CompactHexDecode(str string) []int {
+ base := "0123456789abcdef"
+ hexSlice := make([]int, 0)
+
+ enc := hex.EncodeToString([]byte(str))
+ for _, v := range enc {
+ hexSlice = append(hexSlice, strings.IndexByte(base, byte(v)))
+ }
+ hexSlice = append(hexSlice, 16)
+
+ return hexSlice
+}
+
+func DecodeCompact(key []int) string {
+ base := "0123456789abcdef"
+ var str string
+
+ for _, v := range key {
+ if v < 16 {
+ str += string(base[v])
+ }
+ }
+
+ res, _ := hex.DecodeString(str)
+
+ return string(res)
+}
diff --git a/ethutil/encoding_test.go b/ethutil/encoding_test.go
new file mode 100644
index 000000000..10e1995c0
--- /dev/null
+++ b/ethutil/encoding_test.go
@@ -0,0 +1,67 @@
+package ethutil
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestCompactEncode(t *testing.T) {
+ test1 := []int{1, 2, 3, 4, 5}
+ if res := CompactEncode(test1); res != "\x11\x23\x45" {
+ t.Error(fmt.Sprintf("even compact encode failed. Got: %q", res))
+ }
+
+ test2 := []int{0, 1, 2, 3, 4, 5}
+ if res := CompactEncode(test2); res != "\x00\x01\x23\x45" {
+ t.Error(fmt.Sprintf("odd compact encode failed. Got: %q", res))
+ }
+
+ test3 := []int{0, 15, 1, 12, 11, 8 /*term*/, 16}
+ if res := CompactEncode(test3); res != "\x20\x0f\x1c\xb8" {
+ t.Error(fmt.Sprintf("odd terminated compact encode failed. Got: %q", res))
+ }
+
+ test4 := []int{15, 1, 12, 11, 8 /*term*/, 16}
+ if res := CompactEncode(test4); res != "\x3f\x1c\xb8" {
+ t.Error(fmt.Sprintf("even terminated compact encode failed. Got: %q", res))
+ }
+}
+
+func TestCompactHexDecode(t *testing.T) {
+ exp := []int{7, 6, 6, 5, 7, 2, 6, 2, 16}
+ res := CompactHexDecode("verb")
+
+ if !CompareIntSlice(res, exp) {
+ t.Error("Error compact hex decode. Expected", exp, "got", res)
+ }
+}
+
+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)
+ }
+}
diff --git a/ethutil/helpers.go b/ethutil/helpers.go
new file mode 100644
index 000000000..aa0f79a04
--- /dev/null
+++ b/ethutil/helpers.go
@@ -0,0 +1,64 @@
+package ethutil
+
+import (
+ "code.google.com/p/go.crypto/ripemd160"
+ "crypto/sha256"
+ "encoding/hex"
+ "github.com/obscuren/sha3"
+ "strconv"
+)
+
+func Uitoa(i uint32) string {
+ return strconv.FormatUint(uint64(i), 10)
+}
+
+func Sha256Bin(data []byte) []byte {
+ hash := sha256.Sum256(data)
+
+ return hash[:]
+}
+
+func Ripemd160(data []byte) []byte {
+ ripemd := ripemd160.New()
+ ripemd.Write(data)
+
+ return ripemd.Sum(nil)
+}
+
+func Sha3Bin(data []byte) []byte {
+ d := sha3.NewKeccak256()
+ d.Write(data)
+
+ return d.Sum(nil)
+}
+
+// Helper function for comparing slices
+func CompareIntSlice(a, b []int) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, v := range a {
+ if v != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// Returns the amount of nibbles that match each other from 0 ...
+func MatchingNibbleLength(a, b []int) int {
+ i := 0
+ for CompareIntSlice(a[:i+1], b[:i+1]) && i < len(b) {
+ i += 1
+ }
+
+ return i
+}
+
+func Hex(d []byte) string {
+ return hex.EncodeToString(d)
+}
+func FromHex(str string) []byte {
+ h, _ := hex.DecodeString(str)
+ return h
+}
diff --git a/ethutil/keypair.go b/ethutil/keypair.go
new file mode 100644
index 000000000..29fb1bac5
--- /dev/null
+++ b/ethutil/keypair.go
@@ -0,0 +1,115 @@
+package ethutil
+
+import (
+ "github.com/obscuren/secp256k1-go"
+)
+
+type KeyPair struct {
+ PrivateKey []byte
+ PublicKey []byte
+
+ // The associated account
+ account *StateObject
+}
+
+func GenerateNewKeyPair() (*KeyPair, error) {
+ _, prv := secp256k1.GenerateKeyPair()
+
+ return NewKeyPairFromSec(prv)
+}
+
+func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) {
+ pubkey, err := secp256k1.GeneratePubKey(seckey)
+ if err != nil {
+ return nil, err
+ }
+
+ return &KeyPair{PrivateKey: seckey, PublicKey: pubkey}, nil
+}
+
+func NewKeyPairFromValue(val *Value) *KeyPair {
+ v, _ := NewKeyPairFromSec(val.Bytes())
+
+ return v
+}
+
+func (k *KeyPair) Address() []byte {
+ return Sha3Bin(k.PublicKey[1:])[12:]
+}
+
+func (k *KeyPair) RlpEncode() []byte {
+ return k.RlpValue().Encode()
+}
+
+func (k *KeyPair) RlpValue() *Value {
+ return NewValue(k.PrivateKey)
+}
+
+type KeyRing struct {
+ keys []*KeyPair
+}
+
+func (k *KeyRing) Add(pair *KeyPair) {
+ k.keys = append(k.keys, pair)
+}
+
+func (k *KeyRing) Get(i int) *KeyPair {
+ if len(k.keys) > i {
+ return k.keys[i]
+ }
+
+ return nil
+}
+
+func (k *KeyRing) Len() int {
+ return len(k.keys)
+}
+
+func (k *KeyRing) NewKeyPair(sec []byte) (*KeyPair, error) {
+ keyPair, err := NewKeyPairFromSec(sec)
+ if err != nil {
+ return nil, err
+ }
+
+ k.Add(keyPair)
+ Config.Db.Put([]byte("KeyRing"), k.RlpValue().Encode())
+
+ return keyPair, nil
+}
+
+func (k *KeyRing) Reset() {
+ Config.Db.Put([]byte("KeyRing"), nil)
+ k.keys = nil
+}
+
+func (k *KeyRing) RlpValue() *Value {
+ v := EmptyValue()
+ for _, keyPair := range k.keys {
+ v.Append(keyPair.RlpValue())
+ }
+
+ return v
+}
+
+// The public "singleton" keyring
+var keyRing *KeyRing
+
+func GetKeyRing() *KeyRing {
+ if keyRing == nil {
+ keyRing = &KeyRing{}
+
+ data, _ := Config.Db.Get([]byte("KeyRing"))
+ it := NewValueFromBytes(data).NewIterator()
+ for it.Next() {
+ v := it.Value()
+
+ key, err := NewKeyPairFromSec(v.Bytes())
+ if err != nil {
+ panic(err)
+ }
+ keyRing.Add(key)
+ }
+ }
+
+ return keyRing
+}
diff --git a/ethutil/mnemonic.go b/ethutil/mnemonic.go
new file mode 100644
index 000000000..00f089e3b
--- /dev/null
+++ b/ethutil/mnemonic.go
@@ -0,0 +1,1690 @@
+package ethutil
+
+import (
+ "fmt"
+ "strconv"
+)
+
+// TODO: See if we can refactor this into a shared util lib if we need it multiple times
+func IndexOf(slice []string, value string) int64 {
+ for p, v := range slice {
+ if v == value {
+ return int64(p)
+ }
+ }
+ return -1
+}
+
+func MnemonicEncode(message string) []string {
+ var out []string
+ n := int64(len(words))
+
+ for i := 0; i < len(message); i += (len(message) / 8) {
+ x := message[i : i+8]
+ bit, _ := strconv.ParseInt(x, 16, 64)
+ w1 := (bit % n)
+ w2 := ((bit / n) + w1) % n
+ w3 := ((bit / n / n) + w2) % n
+ out = append(out, words[w1], words[w2], words[w3])
+ }
+ return out
+}
+
+func MnemonicDecode(wordsar []string) string {
+ var out string
+ n := int64(len(words))
+
+ for i := 0; i < len(wordsar); i += 3 {
+ word1 := wordsar[i]
+ word2 := wordsar[i+1]
+ word3 := wordsar[i+2]
+ w1 := IndexOf(words, word1)
+ w2 := IndexOf(words, word2)
+ w3 := IndexOf(words, word3)
+
+ y := (w2 - w1) % n
+ z := (w3 - w2) % n
+
+ // Golang handles modulo with negative numbers different then most languages
+ // The modulo can be negative, we don't want that.
+ if z < 0 {
+ z += n
+ }
+ if y < 0 {
+ y += n
+ }
+ x := w1 + n*(y) + n*n*(z)
+ out += fmt.Sprintf("%08x", x)
+ }
+ return out
+}
+
+// Electrum word list
+var words []string = []string{
+ "like",
+ "just",
+ "love",
+ "know",
+ "never",
+ "want",
+ "time",
+ "out",
+ "there",
+ "make",
+ "look",
+ "eye",
+ "down",
+ "only",
+ "think",
+ "heart",
+ "back",
+ "then",
+ "into",
+ "about",
+ "more",
+ "away",
+ "still",
+ "them",
+ "take",
+ "thing",
+ "even",
+ "through",
+ "long",
+ "always",
+ "world",
+ "too",
+ "friend",
+ "tell",
+ "try",
+ "hand",
+ "thought",
+ "over",
+ "here",
+ "other",
+ "need",
+ "smile",
+ "again",
+ "much",
+ "cry",
+ "been",
+ "night",
+ "ever",
+ "little",
+ "said",
+ "end",
+ "some",
+ "those",
+ "around",
+ "mind",
+ "people",
+ "girl",
+ "leave",
+ "dream",
+ "left",
+ "turn",
+ "myself",
+ "give",
+ "nothing",
+ "really",
+ "off",
+ "before",
+ "something",
+ "find",
+ "walk",
+ "wish",
+ "good",
+ "once",
+ "place",
+ "ask",
+ "stop",
+ "keep",
+ "watch",
+ "seem",
+ "everything",
+ "wait",
+ "got",
+ "yet",
+ "made",
+ "remember",
+ "start",
+ "alone",
+ "run",
+ "hope",
+ "maybe",
+ "believe",
+ "body",
+ "hate",
+ "after",
+ "close",
+ "talk",
+ "stand",
+ "own",
+ "each",
+ "hurt",
+ "help",
+ "home",
+ "god",
+ "soul",
+ "new",
+ "many",
+ "two",
+ "inside",
+ "should",
+ "true",
+ "first",
+ "fear",
+ "mean",
+ "better",
+ "play",
+ "another",
+ "gone",
+ "change",
+ "use",
+ "wonder",
+ "someone",
+ "hair",
+ "cold",
+ "open",
+ "best",
+ "any",
+ "behind",
+ "happen",
+ "water",
+ "dark",
+ "laugh",
+ "stay",
+ "forever",
+ "name",
+ "work",
+ "show",
+ "sky",
+ "break",
+ "came",
+ "deep",
+ "door",
+ "put",
+ "black",
+ "together",
+ "upon",
+ "happy",
+ "such",
+ "great",
+ "white",
+ "matter",
+ "fill",
+ "past",
+ "please",
+ "burn",
+ "cause",
+ "enough",
+ "touch",
+ "moment",
+ "soon",
+ "voice",
+ "scream",
+ "anything",
+ "stare",
+ "sound",
+ "red",
+ "everyone",
+ "hide",
+ "kiss",
+ "truth",
+ "death",
+ "beautiful",
+ "mine",
+ "blood",
+ "broken",
+ "very",
+ "pass",
+ "next",
+ "forget",
+ "tree",
+ "wrong",
+ "air",
+ "mother",
+ "understand",
+ "lip",
+ "hit",
+ "wall",
+ "memory",
+ "sleep",
+ "free",
+ "high",
+ "realize",
+ "school",
+ "might",
+ "skin",
+ "sweet",
+ "perfect",
+ "blue",
+ "kill",
+ "breath",
+ "dance",
+ "against",
+ "fly",
+ "between",
+ "grow",
+ "strong",
+ "under",
+ "listen",
+ "bring",
+ "sometimes",
+ "speak",
+ "pull",
+ "person",
+ "become",
+ "family",
+ "begin",
+ "ground",
+ "real",
+ "small",
+ "father",
+ "sure",
+ "feet",
+ "rest",
+ "young",
+ "finally",
+ "land",
+ "across",
+ "today",
+ "different",
+ "guy",
+ "line",
+ "fire",
+ "reason",
+ "reach",
+ "second",
+ "slowly",
+ "write",
+ "eat",
+ "smell",
+ "mouth",
+ "step",
+ "learn",
+ "three",
+ "floor",
+ "promise",
+ "breathe",
+ "darkness",
+ "push",
+ "earth",
+ "guess",
+ "save",
+ "song",
+ "above",
+ "along",
+ "both",
+ "color",
+ "house",
+ "almost",
+ "sorry",
+ "anymore",
+ "brother",
+ "okay",
+ "dear",
+ "game",
+ "fade",
+ "already",
+ "apart",
+ "warm",
+ "beauty",
+ "heard",
+ "notice",
+ "question",
+ "shine",
+ "began",
+ "piece",
+ "whole",
+ "shadow",
+ "secret",
+ "street",
+ "within",
+ "finger",
+ "point",
+ "morning",
+ "whisper",
+ "child",
+ "moon",
+ "green",
+ "story",
+ "glass",
+ "kid",
+ "silence",
+ "since",
+ "soft",
+ "yourself",
+ "empty",
+ "shall",
+ "angel",
+ "answer",
+ "baby",
+ "bright",
+ "dad",
+ "path",
+ "worry",
+ "hour",
+ "drop",
+ "follow",
+ "power",
+ "war",
+ "half",
+ "flow",
+ "heaven",
+ "act",
+ "chance",
+ "fact",
+ "least",
+ "tired",
+ "children",
+ "near",
+ "quite",
+ "afraid",
+ "rise",
+ "sea",
+ "taste",
+ "window",
+ "cover",
+ "nice",
+ "trust",
+ "lot",
+ "sad",
+ "cool",
+ "force",
+ "peace",
+ "return",
+ "blind",
+ "easy",
+ "ready",
+ "roll",
+ "rose",
+ "drive",
+ "held",
+ "music",
+ "beneath",
+ "hang",
+ "mom",
+ "paint",
+ "emotion",
+ "quiet",
+ "clear",
+ "cloud",
+ "few",
+ "pretty",
+ "bird",
+ "outside",
+ "paper",
+ "picture",
+ "front",
+ "rock",
+ "simple",
+ "anyone",
+ "meant",
+ "reality",
+ "road",
+ "sense",
+ "waste",
+ "bit",
+ "leaf",
+ "thank",
+ "happiness",
+ "meet",
+ "men",
+ "smoke",
+ "truly",
+ "decide",
+ "self",
+ "age",
+ "book",
+ "form",
+ "alive",
+ "carry",
+ "escape",
+ "damn",
+ "instead",
+ "able",
+ "ice",
+ "minute",
+ "throw",
+ "catch",
+ "leg",
+ "ring",
+ "course",
+ "goodbye",
+ "lead",
+ "poem",
+ "sick",
+ "corner",
+ "desire",
+ "known",
+ "problem",
+ "remind",
+ "shoulder",
+ "suppose",
+ "toward",
+ "wave",
+ "drink",
+ "jump",
+ "woman",
+ "pretend",
+ "sister",
+ "week",
+ "human",
+ "joy",
+ "crack",
+ "grey",
+ "pray",
+ "surprise",
+ "dry",
+ "knee",
+ "less",
+ "search",
+ "bleed",
+ "caught",
+ "clean",
+ "embrace",
+ "future",
+ "king",
+ "son",
+ "sorrow",
+ "chest",
+ "hug",
+ "remain",
+ "sat",
+ "worth",
+ "blow",
+ "daddy",
+ "final",
+ "parent",
+ "tight",
+ "also",
+ "create",
+ "lonely",
+ "safe",
+ "cross",
+ "dress",
+ "evil",
+ "silent",
+ "bone",
+ "fate",
+ "perhaps",
+ "anger",
+ "class",
+ "scar",
+ "snow",
+ "tiny",
+ "tonight",
+ "continue",
+ "control",
+ "dog",
+ "edge",
+ "mirror",
+ "month",
+ "suddenly",
+ "comfort",
+ "given",
+ "loud",
+ "quickly",
+ "gaze",
+ "plan",
+ "rush",
+ "stone",
+ "town",
+ "battle",
+ "ignore",
+ "spirit",
+ "stood",
+ "stupid",
+ "yours",
+ "brown",
+ "build",
+ "dust",
+ "hey",
+ "kept",
+ "pay",
+ "phone",
+ "twist",
+ "although",
+ "ball",
+ "beyond",
+ "hidden",
+ "nose",
+ "taken",
+ "fail",
+ "float",
+ "pure",
+ "somehow",
+ "wash",
+ "wrap",
+ "angry",
+ "cheek",
+ "creature",
+ "forgotten",
+ "heat",
+ "rip",
+ "single",
+ "space",
+ "special",
+ "weak",
+ "whatever",
+ "yell",
+ "anyway",
+ "blame",
+ "job",
+ "choose",
+ "country",
+ "curse",
+ "drift",
+ "echo",
+ "figure",
+ "grew",
+ "laughter",
+ "neck",
+ "suffer",
+ "worse",
+ "yeah",
+ "disappear",
+ "foot",
+ "forward",
+ "knife",
+ "mess",
+ "somewhere",
+ "stomach",
+ "storm",
+ "beg",
+ "idea",
+ "lift",
+ "offer",
+ "breeze",
+ "field",
+ "five",
+ "often",
+ "simply",
+ "stuck",
+ "win",
+ "allow",
+ "confuse",
+ "enjoy",
+ "except",
+ "flower",
+ "seek",
+ "strength",
+ "calm",
+ "grin",
+ "gun",
+ "heavy",
+ "hill",
+ "large",
+ "ocean",
+ "shoe",
+ "sigh",
+ "straight",
+ "summer",
+ "tongue",
+ "accept",
+ "crazy",
+ "everyday",
+ "exist",
+ "grass",
+ "mistake",
+ "sent",
+ "shut",
+ "surround",
+ "table",
+ "ache",
+ "brain",
+ "destroy",
+ "heal",
+ "nature",
+ "shout",
+ "sign",
+ "stain",
+ "choice",
+ "doubt",
+ "glance",
+ "glow",
+ "mountain",
+ "queen",
+ "stranger",
+ "throat",
+ "tomorrow",
+ "city",
+ "either",
+ "fish",
+ "flame",
+ "rather",
+ "shape",
+ "spin",
+ "spread",
+ "ash",
+ "distance",
+ "finish",
+ "image",
+ "imagine",
+ "important",
+ "nobody",
+ "shatter",
+ "warmth",
+ "became",
+ "feed",
+ "flesh",
+ "funny",
+ "lust",
+ "shirt",
+ "trouble",
+ "yellow",
+ "attention",
+ "bare",
+ "bite",
+ "money",
+ "protect",
+ "amaze",
+ "appear",
+ "born",
+ "choke",
+ "completely",
+ "daughter",
+ "fresh",
+ "friendship",
+ "gentle",
+ "probably",
+ "six",
+ "deserve",
+ "expect",
+ "grab",
+ "middle",
+ "nightmare",
+ "river",
+ "thousand",
+ "weight",
+ "worst",
+ "wound",
+ "barely",
+ "bottle",
+ "cream",
+ "regret",
+ "relationship",
+ "stick",
+ "test",
+ "crush",
+ "endless",
+ "fault",
+ "itself",
+ "rule",
+ "spill",
+ "art",
+ "circle",
+ "join",
+ "kick",
+ "mask",
+ "master",
+ "passion",
+ "quick",
+ "raise",
+ "smooth",
+ "unless",
+ "wander",
+ "actually",
+ "broke",
+ "chair",
+ "deal",
+ "favorite",
+ "gift",
+ "note",
+ "number",
+ "sweat",
+ "box",
+ "chill",
+ "clothes",
+ "lady",
+ "mark",
+ "park",
+ "poor",
+ "sadness",
+ "tie",
+ "animal",
+ "belong",
+ "brush",
+ "consume",
+ "dawn",
+ "forest",
+ "innocent",
+ "pen",
+ "pride",
+ "stream",
+ "thick",
+ "clay",
+ "complete",
+ "count",
+ "draw",
+ "faith",
+ "press",
+ "silver",
+ "struggle",
+ "surface",
+ "taught",
+ "teach",
+ "wet",
+ "bless",
+ "chase",
+ "climb",
+ "enter",
+ "letter",
+ "melt",
+ "metal",
+ "movie",
+ "stretch",
+ "swing",
+ "vision",
+ "wife",
+ "beside",
+ "crash",
+ "forgot",
+ "guide",
+ "haunt",
+ "joke",
+ "knock",
+ "plant",
+ "pour",
+ "prove",
+ "reveal",
+ "steal",
+ "stuff",
+ "trip",
+ "wood",
+ "wrist",
+ "bother",
+ "bottom",
+ "crawl",
+ "crowd",
+ "fix",
+ "forgive",
+ "frown",
+ "grace",
+ "loose",
+ "lucky",
+ "party",
+ "release",
+ "surely",
+ "survive",
+ "teacher",
+ "gently",
+ "grip",
+ "speed",
+ "suicide",
+ "travel",
+ "treat",
+ "vein",
+ "written",
+ "cage",
+ "chain",
+ "conversation",
+ "date",
+ "enemy",
+ "however",
+ "interest",
+ "million",
+ "page",
+ "pink",
+ "proud",
+ "sway",
+ "themselves",
+ "winter",
+ "church",
+ "cruel",
+ "cup",
+ "demon",
+ "experience",
+ "freedom",
+ "pair",
+ "pop",
+ "purpose",
+ "respect",
+ "shoot",
+ "softly",
+ "state",
+ "strange",
+ "bar",
+ "birth",
+ "curl",
+ "dirt",
+ "excuse",
+ "lord",
+ "lovely",
+ "monster",
+ "order",
+ "pack",
+ "pants",
+ "pool",
+ "scene",
+ "seven",
+ "shame",
+ "slide",
+ "ugly",
+ "among",
+ "blade",
+ "blonde",
+ "closet",
+ "creek",
+ "deny",
+ "drug",
+ "eternity",
+ "gain",
+ "grade",
+ "handle",
+ "key",
+ "linger",
+ "pale",
+ "prepare",
+ "swallow",
+ "swim",
+ "tremble",
+ "wheel",
+ "won",
+ "cast",
+ "cigarette",
+ "claim",
+ "college",
+ "direction",
+ "dirty",
+ "gather",
+ "ghost",
+ "hundred",
+ "loss",
+ "lung",
+ "orange",
+ "present",
+ "swear",
+ "swirl",
+ "twice",
+ "wild",
+ "bitter",
+ "blanket",
+ "doctor",
+ "everywhere",
+ "flash",
+ "grown",
+ "knowledge",
+ "numb",
+ "pressure",
+ "radio",
+ "repeat",
+ "ruin",
+ "spend",
+ "unknown",
+ "buy",
+ "clock",
+ "devil",
+ "early",
+ "false",
+ "fantasy",
+ "pound",
+ "precious",
+ "refuse",
+ "sheet",
+ "teeth",
+ "welcome",
+ "add",
+ "ahead",
+ "block",
+ "bury",
+ "caress",
+ "content",
+ "depth",
+ "despite",
+ "distant",
+ "marry",
+ "purple",
+ "threw",
+ "whenever",
+ "bomb",
+ "dull",
+ "easily",
+ "grasp",
+ "hospital",
+ "innocence",
+ "normal",
+ "receive",
+ "reply",
+ "rhyme",
+ "shade",
+ "someday",
+ "sword",
+ "toe",
+ "visit",
+ "asleep",
+ "bought",
+ "center",
+ "consider",
+ "flat",
+ "hero",
+ "history",
+ "ink",
+ "insane",
+ "muscle",
+ "mystery",
+ "pocket",
+ "reflection",
+ "shove",
+ "silently",
+ "smart",
+ "soldier",
+ "spot",
+ "stress",
+ "train",
+ "type",
+ "view",
+ "whether",
+ "bus",
+ "energy",
+ "explain",
+ "holy",
+ "hunger",
+ "inch",
+ "magic",
+ "mix",
+ "noise",
+ "nowhere",
+ "prayer",
+ "presence",
+ "shock",
+ "snap",
+ "spider",
+ "study",
+ "thunder",
+ "trail",
+ "admit",
+ "agree",
+ "bag",
+ "bang",
+ "bound",
+ "butterfly",
+ "cute",
+ "exactly",
+ "explode",
+ "familiar",
+ "fold",
+ "further",
+ "pierce",
+ "reflect",
+ "scent",
+ "selfish",
+ "sharp",
+ "sink",
+ "spring",
+ "stumble",
+ "universe",
+ "weep",
+ "women",
+ "wonderful",
+ "action",
+ "ancient",
+ "attempt",
+ "avoid",
+ "birthday",
+ "branch",
+ "chocolate",
+ "core",
+ "depress",
+ "drunk",
+ "especially",
+ "focus",
+ "fruit",
+ "honest",
+ "match",
+ "palm",
+ "perfectly",
+ "pillow",
+ "pity",
+ "poison",
+ "roar",
+ "shift",
+ "slightly",
+ "thump",
+ "truck",
+ "tune",
+ "twenty",
+ "unable",
+ "wipe",
+ "wrote",
+ "coat",
+ "constant",
+ "dinner",
+ "drove",
+ "egg",
+ "eternal",
+ "flight",
+ "flood",
+ "frame",
+ "freak",
+ "gasp",
+ "glad",
+ "hollow",
+ "motion",
+ "peer",
+ "plastic",
+ "root",
+ "screen",
+ "season",
+ "sting",
+ "strike",
+ "team",
+ "unlike",
+ "victim",
+ "volume",
+ "warn",
+ "weird",
+ "attack",
+ "await",
+ "awake",
+ "built",
+ "charm",
+ "crave",
+ "despair",
+ "fought",
+ "grant",
+ "grief",
+ "horse",
+ "limit",
+ "message",
+ "ripple",
+ "sanity",
+ "scatter",
+ "serve",
+ "split",
+ "string",
+ "trick",
+ "annoy",
+ "blur",
+ "boat",
+ "brave",
+ "clearly",
+ "cling",
+ "connect",
+ "fist",
+ "forth",
+ "imagination",
+ "iron",
+ "jock",
+ "judge",
+ "lesson",
+ "milk",
+ "misery",
+ "nail",
+ "naked",
+ "ourselves",
+ "poet",
+ "possible",
+ "princess",
+ "sail",
+ "size",
+ "snake",
+ "society",
+ "stroke",
+ "torture",
+ "toss",
+ "trace",
+ "wise",
+ "bloom",
+ "bullet",
+ "cell",
+ "check",
+ "cost",
+ "darling",
+ "during",
+ "footstep",
+ "fragile",
+ "hallway",
+ "hardly",
+ "horizon",
+ "invisible",
+ "journey",
+ "midnight",
+ "mud",
+ "nod",
+ "pause",
+ "relax",
+ "shiver",
+ "sudden",
+ "value",
+ "youth",
+ "abuse",
+ "admire",
+ "blink",
+ "breast",
+ "bruise",
+ "constantly",
+ "couple",
+ "creep",
+ "curve",
+ "difference",
+ "dumb",
+ "emptiness",
+ "gotta",
+ "honor",
+ "plain",
+ "planet",
+ "recall",
+ "rub",
+ "ship",
+ "slam",
+ "soar",
+ "somebody",
+ "tightly",
+ "weather",
+ "adore",
+ "approach",
+ "bond",
+ "bread",
+ "burst",
+ "candle",
+ "coffee",
+ "cousin",
+ "crime",
+ "desert",
+ "flutter",
+ "frozen",
+ "grand",
+ "heel",
+ "hello",
+ "language",
+ "level",
+ "movement",
+ "pleasure",
+ "powerful",
+ "random",
+ "rhythm",
+ "settle",
+ "silly",
+ "slap",
+ "sort",
+ "spoken",
+ "steel",
+ "threaten",
+ "tumble",
+ "upset",
+ "aside",
+ "awkward",
+ "bee",
+ "blank",
+ "board",
+ "button",
+ "card",
+ "carefully",
+ "complain",
+ "crap",
+ "deeply",
+ "discover",
+ "drag",
+ "dread",
+ "effort",
+ "entire",
+ "fairy",
+ "giant",
+ "gotten",
+ "greet",
+ "illusion",
+ "jeans",
+ "leap",
+ "liquid",
+ "march",
+ "mend",
+ "nervous",
+ "nine",
+ "replace",
+ "rope",
+ "spine",
+ "stole",
+ "terror",
+ "accident",
+ "apple",
+ "balance",
+ "boom",
+ "childhood",
+ "collect",
+ "demand",
+ "depression",
+ "eventually",
+ "faint",
+ "glare",
+ "goal",
+ "group",
+ "honey",
+ "kitchen",
+ "laid",
+ "limb",
+ "machine",
+ "mere",
+ "mold",
+ "murder",
+ "nerve",
+ "painful",
+ "poetry",
+ "prince",
+ "rabbit",
+ "shelter",
+ "shore",
+ "shower",
+ "soothe",
+ "stair",
+ "steady",
+ "sunlight",
+ "tangle",
+ "tease",
+ "treasure",
+ "uncle",
+ "begun",
+ "bliss",
+ "canvas",
+ "cheer",
+ "claw",
+ "clutch",
+ "commit",
+ "crimson",
+ "crystal",
+ "delight",
+ "doll",
+ "existence",
+ "express",
+ "fog",
+ "football",
+ "gay",
+ "goose",
+ "guard",
+ "hatred",
+ "illuminate",
+ "mass",
+ "math",
+ "mourn",
+ "rich",
+ "rough",
+ "skip",
+ "stir",
+ "student",
+ "style",
+ "support",
+ "thorn",
+ "tough",
+ "yard",
+ "yearn",
+ "yesterday",
+ "advice",
+ "appreciate",
+ "autumn",
+ "bank",
+ "beam",
+ "bowl",
+ "capture",
+ "carve",
+ "collapse",
+ "confusion",
+ "creation",
+ "dove",
+ "feather",
+ "girlfriend",
+ "glory",
+ "government",
+ "harsh",
+ "hop",
+ "inner",
+ "loser",
+ "moonlight",
+ "neighbor",
+ "neither",
+ "peach",
+ "pig",
+ "praise",
+ "screw",
+ "shield",
+ "shimmer",
+ "sneak",
+ "stab",
+ "subject",
+ "throughout",
+ "thrown",
+ "tower",
+ "twirl",
+ "wow",
+ "army",
+ "arrive",
+ "bathroom",
+ "bump",
+ "cease",
+ "cookie",
+ "couch",
+ "courage",
+ "dim",
+ "guilt",
+ "howl",
+ "hum",
+ "husband",
+ "insult",
+ "led",
+ "lunch",
+ "mock",
+ "mostly",
+ "natural",
+ "nearly",
+ "needle",
+ "nerd",
+ "peaceful",
+ "perfection",
+ "pile",
+ "price",
+ "remove",
+ "roam",
+ "sanctuary",
+ "serious",
+ "shiny",
+ "shook",
+ "sob",
+ "stolen",
+ "tap",
+ "vain",
+ "void",
+ "warrior",
+ "wrinkle",
+ "affection",
+ "apologize",
+ "blossom",
+ "bounce",
+ "bridge",
+ "cheap",
+ "crumble",
+ "decision",
+ "descend",
+ "desperately",
+ "dig",
+ "dot",
+ "flip",
+ "frighten",
+ "heartbeat",
+ "huge",
+ "lazy",
+ "lick",
+ "odd",
+ "opinion",
+ "process",
+ "puzzle",
+ "quietly",
+ "retreat",
+ "score",
+ "sentence",
+ "separate",
+ "situation",
+ "skill",
+ "soak",
+ "square",
+ "stray",
+ "taint",
+ "task",
+ "tide",
+ "underneath",
+ "veil",
+ "whistle",
+ "anywhere",
+ "bedroom",
+ "bid",
+ "bloody",
+ "burden",
+ "careful",
+ "compare",
+ "concern",
+ "curtain",
+ "decay",
+ "defeat",
+ "describe",
+ "double",
+ "dreamer",
+ "driver",
+ "dwell",
+ "evening",
+ "flare",
+ "flicker",
+ "grandma",
+ "guitar",
+ "harm",
+ "horrible",
+ "hungry",
+ "indeed",
+ "lace",
+ "melody",
+ "monkey",
+ "nation",
+ "object",
+ "obviously",
+ "rainbow",
+ "salt",
+ "scratch",
+ "shown",
+ "shy",
+ "stage",
+ "stun",
+ "third",
+ "tickle",
+ "useless",
+ "weakness",
+ "worship",
+ "worthless",
+ "afternoon",
+ "beard",
+ "boyfriend",
+ "bubble",
+ "busy",
+ "certain",
+ "chin",
+ "concrete",
+ "desk",
+ "diamond",
+ "doom",
+ "drawn",
+ "due",
+ "felicity",
+ "freeze",
+ "frost",
+ "garden",
+ "glide",
+ "harmony",
+ "hopefully",
+ "hunt",
+ "jealous",
+ "lightning",
+ "mama",
+ "mercy",
+ "peel",
+ "physical",
+ "position",
+ "pulse",
+ "punch",
+ "quit",
+ "rant",
+ "respond",
+ "salty",
+ "sane",
+ "satisfy",
+ "savior",
+ "sheep",
+ "slept",
+ "social",
+ "sport",
+ "tuck",
+ "utter",
+ "valley",
+ "wolf",
+ "aim",
+ "alas",
+ "alter",
+ "arrow",
+ "awaken",
+ "beaten",
+ "belief",
+ "brand",
+ "ceiling",
+ "cheese",
+ "clue",
+ "confidence",
+ "connection",
+ "daily",
+ "disguise",
+ "eager",
+ "erase",
+ "essence",
+ "everytime",
+ "expression",
+ "fan",
+ "flag",
+ "flirt",
+ "foul",
+ "fur",
+ "giggle",
+ "glorious",
+ "ignorance",
+ "law",
+ "lifeless",
+ "measure",
+ "mighty",
+ "muse",
+ "north",
+ "opposite",
+ "paradise",
+ "patience",
+ "patient",
+ "pencil",
+ "petal",
+ "plate",
+ "ponder",
+ "possibly",
+ "practice",
+ "slice",
+ "spell",
+ "stock",
+ "strife",
+ "strip",
+ "suffocate",
+ "suit",
+ "tender",
+ "tool",
+ "trade",
+ "velvet",
+ "verse",
+ "waist",
+ "witch",
+ "aunt",
+ "bench",
+ "bold",
+ "cap",
+ "certainly",
+ "click",
+ "companion",
+ "creator",
+ "dart",
+ "delicate",
+ "determine",
+ "dish",
+ "dragon",
+ "drama",
+ "drum",
+ "dude",
+ "everybody",
+ "feast",
+ "forehead",
+ "former",
+ "fright",
+ "fully",
+ "gas",
+ "hook",
+ "hurl",
+ "invite",
+ "juice",
+ "manage",
+ "moral",
+ "possess",
+ "raw",
+ "rebel",
+ "royal",
+ "scale",
+ "scary",
+ "several",
+ "slight",
+ "stubborn",
+ "swell",
+ "talent",
+ "tea",
+ "terrible",
+ "thread",
+ "torment",
+ "trickle",
+ "usually",
+ "vast",
+ "violence",
+ "weave",
+ "acid",
+ "agony",
+ "ashamed",
+ "awe",
+ "belly",
+ "blend",
+ "blush",
+ "character",
+ "cheat",
+ "common",
+ "company",
+ "coward",
+ "creak",
+ "danger",
+ "deadly",
+ "defense",
+ "define",
+ "depend",
+ "desperate",
+ "destination",
+ "dew",
+ "duck",
+ "dusty",
+ "embarrass",
+ "engine",
+ "example",
+ "explore",
+ "foe",
+ "freely",
+ "frustrate",
+ "generation",
+ "glove",
+ "guilty",
+ "health",
+ "hurry",
+ "idiot",
+ "impossible",
+ "inhale",
+ "jaw",
+ "kingdom",
+ "mention",
+ "mist",
+ "moan",
+ "mumble",
+ "mutter",
+ "observe",
+ "ode",
+ "pathetic",
+ "pattern",
+ "pie",
+ "prefer",
+ "puff",
+ "rape",
+ "rare",
+ "revenge",
+ "rude",
+ "scrape",
+ "spiral",
+ "squeeze",
+ "strain",
+ "sunset",
+ "suspend",
+ "sympathy",
+ "thigh",
+ "throne",
+ "total",
+ "unseen",
+ "weapon",
+ "weary",
+}
diff --git a/ethutil/mnemonic_test.go b/ethutil/mnemonic_test.go
new file mode 100644
index 000000000..ccf3f9883
--- /dev/null
+++ b/ethutil/mnemonic_test.go
@@ -0,0 +1,74 @@
+package ethutil
+
+import (
+ "testing"
+)
+
+func TestMnDecode(t *testing.T) {
+ words := []string{
+ "ink",
+ "balance",
+ "gain",
+ "fear",
+ "happen",
+ "melt",
+ "mom",
+ "surface",
+ "stir",
+ "bottle",
+ "unseen",
+ "expression",
+ "important",
+ "curl",
+ "grant",
+ "fairy",
+ "across",
+ "back",
+ "figure",
+ "breast",
+ "nobody",
+ "scratch",
+ "worry",
+ "yesterday",
+ }
+ encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
+ result := MnemonicDecode(words)
+ if encode != result {
+ t.Error("We expected", encode, "got", result, "instead")
+ }
+}
+func TestMnEncode(t *testing.T) {
+ encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
+ result := []string{
+ "ink",
+ "balance",
+ "gain",
+ "fear",
+ "happen",
+ "melt",
+ "mom",
+ "surface",
+ "stir",
+ "bottle",
+ "unseen",
+ "expression",
+ "important",
+ "curl",
+ "grant",
+ "fairy",
+ "across",
+ "back",
+ "figure",
+ "breast",
+ "nobody",
+ "scratch",
+ "worry",
+ "yesterday",
+ }
+ words := MnemonicEncode(encode)
+ for i, word := range words {
+ if word != result[i] {
+ t.Error("Mnenonic does not match:", words, result)
+ }
+ }
+}
diff --git a/ethutil/package.go b/ethutil/package.go
new file mode 100644
index 000000000..e5df989d2
--- /dev/null
+++ b/ethutil/package.go
@@ -0,0 +1,123 @@
+package ethutil
+
+import (
+ "archive/zip"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "strings"
+)
+
+// Manifest object
+//
+// The manifest object holds all the relevant information supplied with the
+// the manifest specified in the package
+type Manifest struct {
+ Entry string
+ Height, Width int
+}
+
+// External package
+//
+// External package contains the main html file and manifest
+type ExtPackage struct {
+ EntryHtml string
+ Manifest *Manifest
+}
+
+// Read file
+//
+// Read a given compressed file and returns the read bytes.
+// Returns an error otherwise
+func ReadFile(f *zip.File) ([]byte, error) {
+ rc, err := f.Open()
+ if err != nil {
+ return nil, err
+ }
+ defer rc.Close()
+
+ content, err := ioutil.ReadAll(rc)
+ if err != nil {
+ return nil, err
+ }
+
+ return content, nil
+}
+
+// Reads manifest
+//
+// Reads and returns a manifest object. Returns error otherwise
+func ReadManifest(m []byte) (*Manifest, error) {
+ var manifest Manifest
+
+ dec := json.NewDecoder(strings.NewReader(string(m)))
+ if err := dec.Decode(&manifest); err == io.EOF {
+ } else if err != nil {
+ return nil, err
+ }
+
+ return &manifest, nil
+}
+
+// Find file in archive
+//
+// Returns the index of the given file name if it exists. -1 if file not found
+func FindFileInArchive(fn string, files []*zip.File) (index int) {
+ index = -1
+ // Find the manifest first
+ for i, f := range files {
+ if f.Name == fn {
+ index = i
+ }
+ }
+
+ return
+}
+
+// Open package
+//
+// Opens a prepared ethereum package
+// Reads the manifest file and determines file contents and returns and
+// the external package.
+func OpenPackage(fn string) (*ExtPackage, error) {
+ r, err := zip.OpenReader(fn)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+
+ manifestIndex := FindFileInArchive("manifest.json", r.File)
+
+ if manifestIndex < 0 {
+ return nil, fmt.Errorf("No manifest file found in archive")
+ }
+
+ f, err := ReadFile(r.File[manifestIndex])
+ if err != nil {
+ return nil, err
+ }
+
+ manifest, err := ReadManifest(f)
+ if err != nil {
+ return nil, err
+ }
+
+ if manifest.Entry == "" {
+ return nil, fmt.Errorf("Entry file specified but appears to be empty: %s", manifest.Entry)
+ }
+
+ entryIndex := FindFileInArchive(manifest.Entry, r.File)
+ if entryIndex < 0 {
+ return nil, fmt.Errorf("Entry file not found: '%s'", manifest.Entry)
+ }
+
+ f, err = ReadFile(r.File[entryIndex])
+ if err != nil {
+ return nil, err
+ }
+
+ extPackage := &ExtPackage{string(f), manifest}
+
+ return extPackage, nil
+}
diff --git a/ethutil/rand.go b/ethutil/rand.go
new file mode 100644
index 000000000..91dafec7e
--- /dev/null
+++ b/ethutil/rand.go
@@ -0,0 +1,24 @@
+package ethutil
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "io"
+)
+
+func randomUint64(r io.Reader) (uint64, error) {
+ b := make([]byte, 8)
+ n, err := r.Read(b)
+ if n != len(b) {
+ return 0, io.ErrShortBuffer
+ }
+ if err != nil {
+ return 0, err
+ }
+ return binary.BigEndian.Uint64(b), nil
+}
+
+// RandomUint64 returns a cryptographically random uint64 value.
+func RandomUint64() (uint64, error) {
+ return randomUint64(rand.Reader)
+}
diff --git a/ethutil/reactor.go b/ethutil/reactor.go
new file mode 100644
index 000000000..7cf145245
--- /dev/null
+++ b/ethutil/reactor.go
@@ -0,0 +1,87 @@
+package ethutil
+
+import (
+ "sync"
+)
+
+type ReactorEvent struct {
+ mut sync.Mutex
+ event string
+ chans []chan React
+}
+
+// Post the specified reactor resource on the channels
+// currently subscribed
+func (e *ReactorEvent) Post(react React) {
+ e.mut.Lock()
+ defer e.mut.Unlock()
+
+ for _, ch := range e.chans {
+ go func(ch chan React) {
+ ch <- react
+ }(ch)
+ }
+}
+
+// Add a subscriber to this event
+func (e *ReactorEvent) Add(ch chan React) {
+ e.mut.Lock()
+ defer e.mut.Unlock()
+
+ e.chans = append(e.chans, ch)
+}
+
+// Remove a subscriber
+func (e *ReactorEvent) Remove(ch chan React) {
+ e.mut.Lock()
+ defer e.mut.Unlock()
+
+ for i, c := range e.chans {
+ if c == ch {
+ e.chans = append(e.chans[:i], e.chans[i+1:]...)
+ }
+ }
+}
+
+// Basic reactor resource
+type React struct {
+ Resource interface{}
+ Event string
+}
+
+// The reactor basic engine. Acts as bridge
+// between the events and the subscribers/posters
+type ReactorEngine struct {
+ patterns map[string]*ReactorEvent
+}
+
+func NewReactorEngine() *ReactorEngine {
+ return &ReactorEngine{patterns: make(map[string]*ReactorEvent)}
+}
+
+// Subscribe a channel to the specified event
+func (reactor *ReactorEngine) Subscribe(event string, ch chan React) {
+ ev := reactor.patterns[event]
+ // Create a new event if one isn't available
+ if ev == nil {
+ ev = &ReactorEvent{event: event}
+ reactor.patterns[event] = ev
+ }
+
+ // Add the channel to reactor event handler
+ ev.Add(ch)
+}
+
+func (reactor *ReactorEngine) Unsubscribe(event string, ch chan React) {
+ ev := reactor.patterns[event]
+ if ev != nil {
+ ev.Remove(ch)
+ }
+}
+
+func (reactor *ReactorEngine) Post(event string, resource interface{}) {
+ ev := reactor.patterns[event]
+ if ev != nil {
+ ev.Post(React{Resource: resource, Event: event})
+ }
+}
diff --git a/ethutil/reactor_test.go b/ethutil/reactor_test.go
new file mode 100644
index 000000000..48c2f0df3
--- /dev/null
+++ b/ethutil/reactor_test.go
@@ -0,0 +1,30 @@
+package ethutil
+
+import "testing"
+
+func TestReactorAdd(t *testing.T) {
+ engine := NewReactorEngine()
+ ch := make(chan React)
+ engine.Subscribe("test", ch)
+ if len(engine.patterns) != 1 {
+ t.Error("Expected patterns to be 1, got", len(engine.patterns))
+ }
+}
+
+func TestReactorEvent(t *testing.T) {
+ engine := NewReactorEngine()
+
+ // Buffer 1, so it doesn't block for this test
+ ch := make(chan React, 1)
+ engine.Subscribe("test", ch)
+ engine.Post("test", "hello")
+
+ value := <-ch
+ if val, ok := value.Resource.(string); ok {
+ if val != "hello" {
+ t.Error("Expected Resource to be 'hello', got", val)
+ }
+ } else {
+ t.Error("Unable to cast")
+ }
+}
diff --git a/ethutil/rlp.go b/ethutil/rlp.go
new file mode 100644
index 000000000..195ef0efb
--- /dev/null
+++ b/ethutil/rlp.go
@@ -0,0 +1,225 @@
+package ethutil
+
+import (
+ "bytes"
+ _ "encoding/binary"
+ "fmt"
+ _ "log"
+ _ "math"
+ "math/big"
+)
+
+type RlpEncodable interface {
+ RlpEncode() []byte
+ RlpValue() []interface{}
+}
+
+type RlpEncoder struct {
+ rlpData []byte
+}
+
+func NewRlpEncoder() *RlpEncoder {
+ encoder := &RlpEncoder{}
+
+ return encoder
+}
+func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
+ return Encode(rlpData)
+}
+
+const (
+ RlpEmptyList = 0x80
+ RlpEmptyStr = 0x40
+)
+
+func Char(c []byte) int {
+ if len(c) > 0 {
+ return int(c[0])
+ }
+
+ return 0
+}
+
+func DecodeWithReader(reader *bytes.Buffer) interface{} {
+ var slice []interface{}
+
+ // Read the next byte
+ char := Char(reader.Next(1))
+ switch {
+ case char == 0:
+ return nil
+ case char <= 0x7f:
+ return char
+
+ case char <= 0xb7:
+ return reader.Next(int(char - 0x80))
+
+ case char <= 0xbf:
+ buff := bytes.NewReader(reader.Next(int(char - 0xb8)))
+ length := ReadVarint(buff)
+
+ return reader.Next(int(length))
+
+ case char <= 0xf7:
+ length := int(char - 0xc0)
+ for i := 0; i < length; i++ {
+ obj := DecodeWithReader(reader)
+ if obj != nil {
+ slice = append(slice, obj)
+ } else {
+ break
+ }
+ }
+
+ return slice
+
+ }
+
+ return slice
+}
+
+// TODO Use a bytes.Buffer instead of a raw byte slice.
+// Cleaner code, and use draining instead of seeking the next bytes to read
+func Decode(data []byte, pos uint64) (interface{}, uint64) {
+ var slice []interface{}
+ char := int(data[pos])
+ switch {
+ case char <= 0x7f:
+ return data[pos], pos + 1
+
+ case char <= 0xb7:
+ b := uint64(data[pos]) - 0x80
+
+ return data[pos+1 : pos+1+b], pos + 1 + b
+
+ case char <= 0xbf:
+ b := uint64(data[pos]) - 0xb7
+
+ b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b]))
+
+ return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
+
+ case char <= 0xf7:
+ b := uint64(data[pos]) - 0xc0
+ prevPos := pos
+ pos++
+ for i := uint64(0); i < b; {
+ var obj interface{}
+
+ // Get the next item in the data list and append it
+ obj, prevPos = Decode(data, pos)
+ slice = append(slice, obj)
+
+ // Increment i by the amount bytes read in the previous
+ // read
+ i += (prevPos - pos)
+ pos = prevPos
+ }
+ return slice, pos
+
+ case char <= 0xff:
+ l := uint64(data[pos]) - 0xf7
+ b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
+
+ pos = pos + l + 1
+
+ prevPos := b
+ for i := uint64(0); i < uint64(b); {
+ var obj interface{}
+
+ obj, prevPos = Decode(data, pos)
+ slice = append(slice, obj)
+
+ i += (prevPos - pos)
+ pos = prevPos
+ }
+ return slice, pos
+
+ default:
+ panic(fmt.Sprintf("byte not supported: %q", char))
+ }
+
+ return slice, 0
+}
+
+var (
+ directRlp = big.NewInt(0x7f)
+ numberRlp = big.NewInt(0xb7)
+ zeroRlp = big.NewInt(0x0)
+)
+
+func Encode(object interface{}) []byte {
+ var buff bytes.Buffer
+
+ if object != nil {
+ switch t := object.(type) {
+ case *Value:
+ buff.Write(Encode(t.Raw()))
+ // Code dup :-/
+ case int:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case uint:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int8:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int16:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int32:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case int64:
+ buff.Write(Encode(big.NewInt(t)))
+ case uint16:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case uint32:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case uint64:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case byte:
+ buff.Write(Encode(big.NewInt(int64(t))))
+ case *big.Int:
+ // Not sure how this is possible while we check for
+ if t == nil {
+ buff.WriteByte(0xc0)
+ } else {
+ buff.Write(Encode(t.Bytes()))
+ }
+ case []byte:
+ if len(t) == 1 && t[0] <= 0x7f {
+ buff.Write(t)
+ } else if len(t) < 56 {
+ buff.WriteByte(byte(len(t) + 0x80))
+ buff.Write(t)
+ } else {
+ b := big.NewInt(int64(len(t)))
+ buff.WriteByte(byte(len(b.Bytes()) + 0xb7))
+ buff.Write(b.Bytes())
+ buff.Write(t)
+ }
+ case string:
+ buff.Write(Encode([]byte(t)))
+ case []interface{}:
+ // Inline function for writing the slice header
+ WriteSliceHeader := func(length int) {
+ if length < 56 {
+ buff.WriteByte(byte(length + 0xc0))
+ } else {
+ b := big.NewInt(int64(length))
+ buff.WriteByte(byte(len(b.Bytes()) + 0xf7))
+ buff.Write(b.Bytes())
+ }
+ }
+
+ var b bytes.Buffer
+ for _, val := range t {
+ b.Write(Encode(val))
+ }
+ WriteSliceHeader(len(b.Bytes()))
+ buff.Write(b.Bytes())
+ }
+ } else {
+ // Empty list for nil
+ buff.WriteByte(0xc0)
+ }
+
+ return buff.Bytes()
+}
diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go
new file mode 100644
index 000000000..095c01ecc
--- /dev/null
+++ b/ethutil/rlp_test.go
@@ -0,0 +1,135 @@
+package ethutil
+
+import (
+ "bytes"
+ "math/big"
+ "reflect"
+ "testing"
+)
+
+func TestRlpValueEncoding(t *testing.T) {
+ val := EmptyValue()
+ val.AppendList().Append(1).Append(2).Append(3)
+ val.Append("4").AppendList().Append(5)
+
+ res := val.Encode()
+ exp := Encode([]interface{}{[]interface{}{1, 2, 3}, "4", []interface{}{5}})
+ if bytes.Compare(res, exp) != 0 {
+ t.Errorf("expected %q, got %q", res, exp)
+ }
+}
+
+func TestValueSlice(t *testing.T) {
+ val := []interface{}{
+ "value1",
+ "valeu2",
+ "value3",
+ }
+
+ value := NewValue(val)
+ splitVal := value.SliceFrom(1)
+
+ if splitVal.Len() != 2 {
+ t.Error("SliceFrom: Expected len", 2, "got", splitVal.Len())
+ }
+
+ splitVal = value.SliceTo(2)
+ if splitVal.Len() != 2 {
+ t.Error("SliceTo: Expected len", 2, "got", splitVal.Len())
+ }
+
+ splitVal = value.SliceFromTo(1, 3)
+ if splitVal.Len() != 2 {
+ t.Error("SliceFromTo: Expected len", 2, "got", splitVal.Len())
+ }
+}
+
+func TestValue(t *testing.T) {
+ value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01"))
+ if value.Get(0).Str() != "dog" {
+ t.Errorf("expected '%v', got '%v'", value.Get(0).Str(), "dog")
+ }
+
+ if value.Get(3).Uint() != 1 {
+ t.Errorf("expected '%v', got '%v'", value.Get(3).Uint(), 1)
+ }
+}
+
+func TestEncode(t *testing.T) {
+ strRes := "\x83dog"
+ bytes := Encode("dog")
+
+ str := string(bytes)
+ if str != strRes {
+ t.Errorf("Expected %q, got %q", strRes, str)
+ }
+
+ sliceRes := "\xcc\x83dog\x83god\x83cat"
+ strs := []interface{}{"dog", "god", "cat"}
+ bytes = Encode(strs)
+ slice := string(bytes)
+ if slice != sliceRes {
+ t.Error("Expected %q, got %q", sliceRes, slice)
+ }
+
+ intRes := "\x82\x04\x00"
+ bytes = Encode(1024)
+ if string(bytes) != intRes {
+ t.Errorf("Expected %q, got %q", intRes, bytes)
+ }
+}
+
+func TestDecode(t *testing.T) {
+ single := []byte("\x01")
+ b, _ := Decode(single, 0)
+
+ if b.(uint8) != 1 {
+ t.Errorf("Expected 1, got %q", b)
+ }
+
+ str := []byte("\x83dog")
+ b, _ = Decode(str, 0)
+ if bytes.Compare(b.([]byte), []byte("dog")) != 0 {
+ t.Errorf("Expected dog, got %q", b)
+ }
+
+ slice := []byte("\xcc\x83dog\x83god\x83cat")
+ res := []interface{}{"dog", "god", "cat"}
+ b, _ = Decode(slice, 0)
+ if reflect.DeepEqual(b, res) {
+ t.Errorf("Expected %q, got %q", res, b)
+ }
+}
+
+func TestEncodeDecodeBigInt(t *testing.T) {
+ bigInt := big.NewInt(1391787038)
+ encoded := Encode(bigInt)
+
+ value := NewValueFromBytes(encoded)
+ if value.BigInt().Cmp(bigInt) != 0 {
+ t.Errorf("Expected %v, got %v", bigInt, value.BigInt())
+ }
+}
+
+func TestEncodeDecodeBytes(t *testing.T) {
+ b := NewValue([]interface{}{[]byte{1, 2, 3, 4, 5}, byte(6)})
+ val := NewValueFromBytes(b.Encode())
+ if !b.Cmp(val) {
+ t.Errorf("Expected %v, got %v", val, b)
+ }
+}
+
+func TestEncodeZero(t *testing.T) {
+ b := NewValue(0).Encode()
+ exp := []byte{0xc0}
+ if bytes.Compare(b, exp) == 0 {
+ t.Error("Expected", exp, "got", b)
+ }
+}
+
+func BenchmarkEncodeDecode(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ bytes := Encode([]interface{}{"dog", "god", "cat"})
+ Decode(bytes, 0)
+ }
+}
diff --git a/ethutil/script.go b/ethutil/script.go
new file mode 100644
index 000000000..af4ca6a38
--- /dev/null
+++ b/ethutil/script.go
@@ -0,0 +1,37 @@
+package ethutil
+
+import (
+ "fmt"
+ "github.com/obscuren/mutan"
+ "github.com/obscuren/mutan/backends"
+ "github.com/obscuren/serpent-go"
+ "strings"
+)
+
+// General compile function
+func Compile(script string) (ret []byte, err error) {
+ c := strings.Split(script, "\n")[0]
+
+ if c == "#!serpent" {
+ byteCode, err := serpent.Compile(script)
+ if err != nil {
+ return nil, err
+ }
+
+ return byteCode, nil
+ } else {
+ compiler := mutan.NewCompiler(backend.NewEthereumBackend())
+ byteCode, errors := compiler.Compile(strings.NewReader(script))
+ if len(errors) > 0 {
+ var errs string
+ for _, er := range errors {
+ if er != nil {
+ errs += er.Error()
+ }
+ }
+ return nil, fmt.Errorf("%v", errs)
+ }
+
+ return byteCode, nil
+ }
+}
diff --git a/ethutil/trie.go b/ethutil/trie.go
new file mode 100644
index 000000000..56f1648a6
--- /dev/null
+++ b/ethutil/trie.go
@@ -0,0 +1,578 @@
+package ethutil
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+)
+
+func (s *Cache) Len() int {
+ return len(s.nodes)
+}
+
+// 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
+ Dirty bool
+}
+
+func NewNode(key []byte, val *Value, dirty bool) *Node {
+ return &Node{Key: key, Value: val, Dirty: dirty}
+}
+
+func (n *Node) Copy() *Node {
+ return NewNode(n.Key, n.Value, n.Dirty)
+}
+
+type Cache struct {
+ nodes map[string]*Node
+ db Database
+ IsDirty bool
+}
+
+func NewCache(db Database) *Cache {
+ return &Cache{db: db, nodes: make(map[string]*Node)}
+}
+
+func (cache *Cache) PutValue(v interface{}, force bool) interface{} {
+ value := NewValue(v)
+
+ enc := value.Encode()
+ if len(enc) >= 32 || force {
+ sha := Sha3Bin(enc)
+
+ cache.nodes[string(sha)] = NewNode(sha, value, true)
+ cache.IsDirty = true
+
+ return sha
+ }
+
+ return v
+}
+
+func (cache *Cache) Put(v interface{}) interface{} {
+ return cache.PutValue(v, false)
+}
+
+func (cache *Cache) Get(key []byte) *Value {
+ // First check if the key is the cache
+ if cache.nodes[string(key)] != nil {
+ return cache.nodes[string(key)].Value
+ }
+
+ // Get the key of the database instead and cache it
+ data, _ := cache.db.Get(key)
+ // Create the cached value
+ value := NewValueFromBytes(data)
+ // Create caching node
+ cache.nodes[string(key)] = NewNode(key, value, false)
+
+ return value
+}
+
+func (cache *Cache) 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 {
+ 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
+ if len(cache.nodes) > 200 {
+ cache.nodes = make(map[string]*Node)
+ }
+}
+
+func (cache *Cache) Undo() {
+ for key, node := range cache.nodes {
+ if node.Dirty {
+ delete(cache.nodes, key)
+ }
+ }
+ cache.IsDirty = false
+}
+
+// A (modified) Radix Trie implementation. The Trie implements
+// a caching mechanism and will used cached values if they are
+// present. If a node is not present in the cache it will try to
+// fetch it from the database and store the cached value.
+// Please note that the data isn't persisted unless `Sync` is
+// explicitly called.
+type Trie struct {
+ mut sync.RWMutex
+ prevRoot interface{}
+ Root interface{}
+ //db Database
+ cache *Cache
+}
+
+func copyRoot(root interface{}) interface{} {
+ var prevRootCopy interface{}
+ if b, ok := root.([]byte); ok {
+ prevRootCopy = CopyBytes(b)
+ } else {
+ prevRootCopy = root
+ }
+
+ return prevRootCopy
+}
+
+func NewTrie(db Database, Root interface{}) *Trie {
+ // Make absolute sure the root is copied
+ r := copyRoot(Root)
+ p := copyRoot(Root)
+
+ return &Trie{cache: NewCache(db), Root: r, prevRoot: p}
+}
+
+// Save the cached value to the database.
+func (t *Trie) Sync() {
+ t.cache.Commit()
+ t.prevRoot = copyRoot(t.Root)
+}
+
+func (t *Trie) Undo() {
+ t.cache.Undo()
+ t.Root = t.prevRoot
+}
+
+func (t *Trie) Cache() *Cache {
+ return t.cache
+}
+
+/*
+ * Public (query) interface functions
+ */
+func (t *Trie) Update(key string, value string) {
+ t.mut.Lock()
+ defer t.mut.Unlock()
+
+ k := CompactHexDecode(key)
+
+ root := t.UpdateState(t.Root, k, value)
+ switch root.(type) {
+ case string:
+ t.Root = root
+ case []byte:
+ t.Root = root
+ default:
+ t.Root = t.cache.PutValue(root, true)
+ }
+}
+
+func (t *Trie) Get(key string) string {
+ t.mut.RLock()
+ defer t.mut.RUnlock()
+
+ k := CompactHexDecode(key)
+ c := NewValue(t.GetState(t.Root, k))
+
+ return c.Str()
+}
+
+func (t *Trie) Delete(key string) {
+ t.mut.Lock()
+ defer t.mut.Unlock()
+
+ k := CompactHexDecode(key)
+
+ root := t.DeleteState(t.Root, k)
+ switch root.(type) {
+ case string:
+ t.Root = root
+ case []byte:
+ t.Root = root
+ default:
+ t.Root = t.cache.PutValue(root, true)
+ }
+}
+
+func (t *Trie) GetState(node interface{}, key []int) interface{} {
+ n := NewValue(node)
+ // Return the node if key is empty (= found)
+ if len(key) == 0 || n.IsNil() || n.Len() == 0 {
+ return node
+ }
+
+ currentNode := t.GetNode(node)
+ length := currentNode.Len()
+
+ if length == 0 {
+ return ""
+ } else if length == 2 {
+ // Decode the key
+ k := CompactDecode(currentNode.Get(0).Str())
+ v := currentNode.Get(1).Raw()
+
+ if len(key) >= len(k) && CompareIntSlice(k, key[:len(k)]) {
+ return t.GetState(v, key[len(k):])
+ } else {
+ return ""
+ }
+ } else if length == 17 {
+ return t.GetState(currentNode.Get(key[0]).Raw(), key[1:])
+ }
+
+ // It shouldn't come this far
+ fmt.Println("GetState unexpected return")
+ return ""
+}
+
+func (t *Trie) GetNode(node interface{}) *Value {
+ n := NewValue(node)
+
+ if !n.Get(0).IsNil() {
+ return n
+ }
+
+ str := n.Str()
+ if len(str) == 0 {
+ return n
+ } else if len(str) < 32 {
+ return NewValueFromBytes([]byte(str))
+ }
+
+ return t.cache.Get(n.Bytes())
+}
+
+func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} {
+ return t.InsertState(node, key, value)
+}
+
+func (t *Trie) Put(node interface{}) interface{} {
+ /*
+ TODO?
+ c := Conv(t.Root)
+ fmt.Println(c.Type(), c.Length())
+ if c.Type() == reflect.String && c.AsString() == "" {
+ return enc
+ }
+ */
+
+ return t.cache.Put(node)
+
+}
+
+func EmptyStringSlice(l int) []interface{} {
+ slice := make([]interface{}, l)
+ for i := 0; i < l; i++ {
+ slice[i] = ""
+ }
+ return slice
+}
+
+func (t *Trie) InsertState(node interface{}, key []int, value interface{}) interface{} {
+ if len(key) == 0 {
+ return value
+ }
+
+ // New node
+ n := NewValue(node)
+ if node == nil || (n.Type() == reflect.String && (n.Str() == "" || n.Get(0).IsNil())) || n.Len() == 0 {
+ newNode := []interface{}{CompactEncode(key), value}
+
+ return t.Put(newNode)
+ }
+
+ currentNode := t.GetNode(node)
+ // Check for "special" 2 slice type node
+ if currentNode.Len() == 2 {
+ // Decode the key
+
+ k := CompactDecode(currentNode.Get(0).Str())
+ v := currentNode.Get(1).Raw()
+
+ // Matching key pair (ie. there's already an object with this key)
+ if CompareIntSlice(k, key) {
+ newNode := []interface{}{CompactEncode(key), value}
+ return t.Put(newNode)
+ }
+
+ var newHash interface{}
+ matchingLength := MatchingNibbleLength(key, k)
+ if matchingLength == len(k) {
+ // Insert the hash, creating a new node
+ newHash = t.InsertState(v, key[matchingLength:], value)
+ } else {
+ // Expand the 2 length slice to a 17 length slice
+ oldNode := t.InsertState("", k[matchingLength+1:], v)
+ newNode := t.InsertState("", key[matchingLength+1:], value)
+ // Create an expanded slice
+ scaledSlice := EmptyStringSlice(17)
+ // Set the copied and new node
+ scaledSlice[k[matchingLength]] = oldNode
+ scaledSlice[key[matchingLength]] = newNode
+
+ newHash = t.Put(scaledSlice)
+ }
+
+ if matchingLength == 0 {
+ // End of the chain, return
+ return newHash
+ } else {
+ newNode := []interface{}{CompactEncode(key[:matchingLength]), newHash}
+ return t.Put(newNode)
+ }
+ } else {
+
+ // Copy the current node over to the new node and replace the first nibble in the key
+ newNode := EmptyStringSlice(17)
+
+ for i := 0; i < 17; i++ {
+ cpy := currentNode.Get(i).Raw()
+ if cpy != nil {
+ newNode[i] = cpy
+ }
+ }
+
+ newNode[key[0]] = t.InsertState(currentNode.Get(key[0]).Raw(), key[1:], value)
+
+ return t.Put(newNode)
+ }
+
+ return ""
+}
+
+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))
+}
+
+// Returns a copy of this trie
+func (t *Trie) Copy() *Trie {
+ trie := NewTrie(t.cache.db, t.Root)
+ for key, node := range t.cache.nodes {
+ trie.cache.nodes[key] = node.Copy()
+ }
+
+ return trie
+}
+
+type TrieIterator struct {
+ trie *Trie
+ key string
+ value string
+
+ shas [][]byte
+ values []string
+
+ lastNode []byte
+}
+
+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).Str() == "" {
+ 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).Str() == "" {
+ 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 ""
+}
+
+type EachCallback func(key string, node *Value)
+
+func (it *TrieIterator) Each(cb EachCallback) {
+ it.fetchNode(nil, NewValue(it.trie.Root).Bytes(), cb)
+}
+
+func (it *TrieIterator) fetchNode(key []int, node []byte, cb EachCallback) {
+ it.iterateNode(key, it.trie.cache.Get(node), cb)
+}
+
+func (it *TrieIterator) iterateNode(key []int, currentNode *Value, cb EachCallback) {
+ if currentNode.Len() == 2 {
+ k := CompactDecode(currentNode.Get(0).Str())
+
+ pk := append(key, k...)
+ if currentNode.Get(1).Len() != 0 && currentNode.Get(1).Str() == "" {
+ it.iterateNode(pk, currentNode.Get(1), cb)
+ } else {
+
+ if k[len(k)-1] == 16 {
+ cb(DecodeCompact(pk), currentNode.Get(1))
+ } else {
+ it.fetchNode(pk, currentNode.Get(1).Bytes(), cb)
+ }
+ }
+ } else {
+ for i := 0; i < currentNode.Len(); i++ {
+ pk := append(key, i)
+ if i == 16 && currentNode.Get(i).Len() != 0 {
+ cb(DecodeCompact(pk), currentNode.Get(i))
+ } else {
+ if currentNode.Get(i).Len() != 0 && currentNode.Get(i).Str() == "" {
+ it.iterateNode(pk, currentNode.Get(i), cb)
+ } else {
+ val := currentNode.Get(i).Str()
+ if val != "" {
+ it.fetchNode(pk, []byte(val), cb)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go
new file mode 100644
index 000000000..01207dbc3
--- /dev/null
+++ b/ethutil/trie_test.go
@@ -0,0 +1,331 @@
+package ethutil
+
+import (
+ "bytes"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "net/http"
+ "reflect"
+ "testing"
+ "time"
+)
+
+const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ"
+
+type MemDatabase struct {
+ db map[string][]byte
+}
+
+func NewMemDatabase() (*MemDatabase, error) {
+ db := &MemDatabase{db: make(map[string][]byte)}
+ return db, nil
+}
+func (db *MemDatabase) Put(key []byte, value []byte) {
+ db.db[string(key)] = value
+}
+func (db *MemDatabase) Get(key []byte) ([]byte, error) {
+ return db.db[string(key)], nil
+}
+func (db *MemDatabase) 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 }
+
+func New() (*MemDatabase, *Trie) {
+ db, _ := NewMemDatabase()
+ return db, NewTrie(db, "")
+}
+
+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")
+ }
+
+ trie.Sync()
+ if len(db.db) == 0 {
+ t.Error("Expected data to be persisted")
+ }
+}
+
+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()
+
+ 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)
+ }
+}
+
+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)
+ }
+
+}
+
+func TestTriePurge(t *testing.T) {
+ _, trie := New()
+ trie.Update("c", LONG_WORD)
+ trie.Update("ca", LONG_WORD)
+ trie.Update("cat", LONG_WORD)
+
+ lenBefore := len(trie.cache.nodes)
+ it := trie.NewIterator()
+ if num := it.Purge(); num != 3 {
+ t.Errorf("Expected purge to return 3, got %d", num)
+ }
+
+ if lenBefore == len(trie.cache.nodes) {
+ t.Errorf("Expected cached nodes to be deleted")
+ }
+}
+
+func h(str string) string {
+ d, err := hex.DecodeString(str)
+ if err != nil {
+ panic(err)
+ }
+
+ return string(d)
+}
+
+func get(in string) (out string) {
+ if len(in) > 2 && in[:2] == "0x" {
+ out = h(in[2:])
+ } else {
+ out = in
+ }
+
+ return
+}
+
+type Test struct {
+ Name string
+ In map[string]string
+ Root string
+}
+
+func CreateTest(name string, data []byte) (Test, error) {
+ t := Test{Name: name}
+ err := json.Unmarshal(data, &t)
+ if err != nil {
+ return Test{}, fmt.Errorf("%v", err)
+ }
+
+ return t, nil
+}
+
+func CreateTests(uri string, cb func(Test)) map[string]Test {
+ resp, err := http.Get(uri)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+
+ data, err := ioutil.ReadAll(resp.Body)
+
+ var objmap map[string]*json.RawMessage
+ err = json.Unmarshal(data, &objmap)
+ if err != nil {
+ panic(err)
+ }
+
+ tests := make(map[string]Test)
+ for name, testData := range objmap {
+ test, err := CreateTest(name, *testData)
+ if err != nil {
+ panic(err)
+ }
+
+ if cb != nil {
+ cb(test)
+ }
+ tests[name] = test
+ }
+
+ return tests
+}
+
+func TestRemote(t *testing.T) {
+ CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) {
+ _, trie := New()
+ for key, value := range test.In {
+ trie.Update(get(key), get(value))
+ }
+
+ a := NewValue(h(test.Root)).Bytes()
+ b := NewValue(trie.Root).Bytes()
+ if bytes.Compare(a, b) != 0 {
+ t.Errorf("%-10s: %x %x", test.Name, a, b)
+ }
+ })
+}
+
+func TestTrieReplay(t *testing.T) {
+ CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) {
+ _, trie := New()
+ for key, value := range test.In {
+ trie.Update(get(key), get(value))
+ }
+
+ _, trie2 := New()
+ trie.NewIterator().Each(func(key string, v *Value) {
+ trie2.Update(key, v.Str())
+ })
+
+ a := NewValue(trie.Root).Bytes()
+ b := NewValue(trie2.Root).Bytes()
+ if bytes.Compare(a, b) != 0 {
+ t.Errorf("%s %x %x\n", test.Name, trie.Root, trie2.Root)
+ }
+ })
+}
+
+func RandomData() [][]string {
+ data := [][]string{
+ {"0x000000000000000000000000ec4f34c97e43fbb2816cfd95e388353c7181dab1", "0x4e616d6552656700000000000000000000000000000000000000000000000000"},
+ {"0x0000000000000000000000000000000000000000000000000000000000000045", "0x22b224a1420a802ab51d326e29fa98e34c4f24ea"},
+ {"0x0000000000000000000000000000000000000000000000000000000000000046", "0x67706c2076330000000000000000000000000000000000000000000000000000"},
+ {"0x000000000000000000000000697c7b8c961b56f675d570498424ac8de1a918f6", "0x6f6f6f6820736f2067726561742c207265616c6c6c793f000000000000000000"},
+ {"0x0000000000000000000000007ef9e639e2733cb34e4dfc576d4b23f72db776b2", "0x4655474156000000000000000000000000000000000000000000000000000000"},
+ {"0x6f6f6f6820736f2067726561742c207265616c6c6c793f000000000000000000", "0x697c7b8c961b56f675d570498424ac8de1a918f6"},
+ {"0x4655474156000000000000000000000000000000000000000000000000000000", "0x7ef9e639e2733cb34e4dfc576d4b23f72db776b2"},
+ {"0x4e616d6552656700000000000000000000000000000000000000000000000000", "0xec4f34c97e43fbb2816cfd95e388353c7181dab1"},
+ }
+
+ var c [][]string
+ for len(data) != 0 {
+ e := rand.Intn(len(data))
+ c = append(c, data[e])
+
+ copy(data[e:], data[e+1:])
+ data[len(data)-1] = nil
+ data = data[:len(data)-1]
+ }
+
+ return c
+}
+
+const MaxTest = 1000
+
+// This test insert data in random order and seeks to find indifferences between the different tries
+func TestRegression(t *testing.T) {
+ rand.Seed(time.Now().Unix())
+
+ roots := make(map[string]int)
+ for i := 0; i < MaxTest; i++ {
+ _, trie := New()
+ data := RandomData()
+
+ for _, test := range data {
+ trie.Update(test[0], test[1])
+ }
+ trie.Delete("0x4e616d6552656700000000000000000000000000000000000000000000000000")
+
+ roots[string(trie.Root.([]byte))] += 1
+ }
+
+ if len(roots) > 1 {
+ for root, num := range roots {
+ t.Errorf("%x => %d\n", root, num)
+ }
+ }
+}
diff --git a/ethutil/value.go b/ethutil/value.go
new file mode 100644
index 000000000..b37b33c28
--- /dev/null
+++ b/ethutil/value.go
@@ -0,0 +1,279 @@
+package ethutil
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "reflect"
+)
+
+// Data values are returned by the rlp decoder. The data values represents
+// one item within the rlp data structure. It's responsible for all the casting
+// It always returns something valid
+type Value struct {
+ Val interface{}
+ kind reflect.Value
+}
+
+func (val *Value) String() string {
+ return fmt.Sprintf("%x", val.Val)
+}
+
+func NewValue(val interface{}) *Value {
+ t := val
+ if v, ok := val.(*Value); ok {
+ t = v.Val
+ }
+
+ return &Value{Val: t}
+}
+
+func (val *Value) Type() reflect.Kind {
+ return reflect.TypeOf(val.Val).Kind()
+}
+
+func (val *Value) IsNil() bool {
+ return val.Val == nil
+}
+
+func (val *Value) Len() int {
+ //return val.kind.Len()
+ if data, ok := val.Val.([]interface{}); ok {
+ return len(data)
+ } else if data, ok := val.Val.([]byte); ok {
+ return len(data)
+ } else if data, ok := val.Val.(string); ok {
+ return len(data)
+ }
+
+ return 0
+}
+
+func (val *Value) Raw() interface{} {
+ return val.Val
+}
+
+func (val *Value) Interface() interface{} {
+ return val.Val
+}
+
+func (val *Value) Uint() uint64 {
+ if Val, ok := val.Val.(uint8); ok {
+ return uint64(Val)
+ } else if Val, ok := val.Val.(uint16); ok {
+ return uint64(Val)
+ } else if Val, ok := val.Val.(uint32); ok {
+ return uint64(Val)
+ } else if Val, ok := val.Val.(uint64); ok {
+ return Val
+ } else if Val, ok := val.Val.(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))
+ }
+
+ return 0
+}
+
+func (val *Value) Byte() byte {
+ if Val, ok := val.Val.(byte); ok {
+ return Val
+ }
+
+ return 0x0
+}
+
+func (val *Value) BigInt() *big.Int {
+ if a, ok := val.Val.([]byte); ok {
+ b := new(big.Int).SetBytes(a)
+
+ return b
+ } else if a, ok := val.Val.(*big.Int); ok {
+ return a
+ } else {
+ return big.NewInt(int64(val.Uint()))
+ }
+
+ return big.NewInt(0)
+}
+
+func (val *Value) Str() string {
+ if a, ok := val.Val.([]byte); ok {
+ return string(a)
+ } else if a, ok := val.Val.(string); ok {
+ return a
+ } else if a, ok := val.Val.(byte); ok {
+ return string(a)
+ }
+
+ return ""
+}
+
+func (val *Value) Bytes() []byte {
+ if a, ok := val.Val.([]byte); ok {
+ return a
+ } else if s, ok := val.Val.(byte); ok {
+ return []byte{s}
+ } else if s, ok := val.Val.(string); ok {
+ return []byte(s)
+ }
+
+ return []byte{}
+}
+
+func (val *Value) Slice() []interface{} {
+ if d, ok := val.Val.([]interface{}); ok {
+ return d
+ }
+
+ return []interface{}{}
+}
+
+func (val *Value) SliceFrom(from int) *Value {
+ slice := val.Slice()
+
+ return NewValue(slice[from:])
+}
+
+func (val *Value) SliceTo(to int) *Value {
+ slice := val.Slice()
+
+ return NewValue(slice[:to])
+}
+
+func (val *Value) SliceFromTo(from, to int) *Value {
+ slice := val.Slice()
+
+ return NewValue(slice[from:to])
+}
+
+// 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
+}
+
+// Special list checking function. Something is considered
+// a list if it's of type []interface{}. The list is usually
+// used in conjunction with rlp decoded streams.
+func (val *Value) IsList() bool {
+ _, ok := val.Val.([]interface{})
+
+ return ok
+}
+
+func (val *Value) IsEmpty() bool {
+ return val.Val == nil || ((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 {
+ // Guard for oob
+ if len(d) <= idx {
+ return NewValue(nil)
+ }
+
+ if idx < 0 {
+ return NewValue(nil)
+ }
+
+ return NewValue(d[idx])
+ }
+
+ // If this wasn't a slice you probably shouldn't be using this function
+ return NewValue(nil)
+}
+
+func (val *Value) Cmp(o *Value) bool {
+ return reflect.DeepEqual(val.Val, o.Val)
+}
+
+func (val *Value) Encode() []byte {
+ return Encode(val.Val)
+}
+
+// Assume that the data we have is encoded
+func (self *Value) Decode() {
+ v, _ := Decode(self.Bytes(), 0)
+ self.Val = v
+}
+
+func NewValueFromBytes(data []byte) *Value {
+ if len(data) != 0 {
+ data, _ := Decode(data, 0)
+ return NewValue(data)
+ }
+
+ return NewValue(nil)
+}
+
+// Value setters
+func NewSliceValue(s interface{}) *Value {
+ list := EmptyValue()
+
+ if s != nil {
+ if slice, ok := s.([]interface{}); ok {
+ for _, val := range slice {
+ list.Append(val)
+ }
+ } else if slice, ok := s.([]string); ok {
+ for _, val := range slice {
+ list.Append(val)
+ }
+ }
+ }
+
+ return list
+}
+
+func EmptyValue() *Value {
+ return NewValue([]interface{}{})
+}
+
+func (val *Value) AppendList() *Value {
+ list := EmptyValue()
+ val.Val = append(val.Slice(), list)
+
+ return list
+}
+
+func (val *Value) Append(v interface{}) *Value {
+ val.Val = append(val.Slice(), v)
+
+ return val
+}
+
+type ValueIterator struct {
+ value *Value
+ currentValue *Value
+ idx int
+}
+
+func (val *Value) NewIterator() *ValueIterator {
+ return &ValueIterator{value: val}
+}
+
+func (it *ValueIterator) Next() bool {
+ if it.idx >= it.value.Len() {
+ return false
+ }
+
+ it.currentValue = it.value.Get(it.idx)
+ it.idx++
+
+ return true
+}
+
+func (it *ValueIterator) Value() *Value {
+ return it.currentValue
+}
+
+func (it *ValueIterator) Idx() int {
+ return it.idx
+}
diff --git a/ethutil/value_test.go b/ethutil/value_test.go
new file mode 100644
index 000000000..a100f44bc
--- /dev/null
+++ b/ethutil/value_test.go
@@ -0,0 +1,65 @@
+package ethutil
+
+import (
+ "bytes"
+ "math/big"
+ "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})
+ 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())
+ }
+
+ if num.Uint() != 1 {
+ t.Errorf("expected Uint to return '1', got %d", num.Uint())
+ }
+
+ 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())
+ }
+}
+
+func TestIterator(t *testing.T) {
+ value := NewValue([]interface{}{1, 2, 3})
+ it := value.NewIterator()
+ values := []uint64{1, 2, 3}
+ i := 0
+ for it.Next() {
+ if values[i] != it.Value().Uint() {
+ t.Errorf("Expected %d, got %d", values[i], it.Value().Uint())
+ }
+ i++
+ }
+}
diff --git a/ethwire/.gitignore b/ethwire/.gitignore
new file mode 100644
index 000000000..f725d58d1
--- /dev/null
+++ b/ethwire/.gitignore
@@ -0,0 +1,12 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile ~/.gitignore_global
+
+/tmp
+*/**/*un~
+*un~
+.DS_Store
+*/**/.DS_Store
+
diff --git a/ethwire/README.md b/ethwire/README.md
new file mode 100644
index 000000000..7f63688b3
--- /dev/null
+++ b/ethwire/README.md
@@ -0,0 +1,36 @@
+# ethwire
+
+The ethwire package contains the ethereum wire protocol. The ethwire
+package is required to write and read from the ethereum network.
+
+# Installation
+
+`go get github.com/ethereum/ethwire-go`
+
+# Messaging overview
+
+The Ethereum Wire protocol defines the communication between the nodes
+running Ethereum. Further reader reading can be done on the
+[Wiki](http://wiki.ethereum.org/index.php/Wire_Protocol).
+
+# Reading Messages
+
+```go
+// Read and validate the next eth message from the provided connection.
+// returns a error message with the details.
+msg, err := ethwire.ReadMessage(conn)
+if err != nil {
+ // Handle error
+}
+```
+
+# Writing Messages
+
+```go
+// Constructs a message which can be interpreted by the eth network.
+// Write the inventory to network
+err := ethwire.WriteMessage(conn, &Msg{
+ Type: ethwire.MsgInvTy,
+ Data : []interface{}{...},
+})
+```
diff --git a/ethwire/messaging.go b/ethwire/messaging.go
new file mode 100644
index 000000000..f13b72353
--- /dev/null
+++ b/ethwire/messaging.go
@@ -0,0 +1,339 @@
+// Package ethwire provides low level access to the Ethereum network and allows
+// you to broadcast data over the network.
+package ethwire
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "net"
+ "time"
+)
+
+// Connection interface describing the methods required to implement the wire protocol.
+type Conn interface {
+ Write(typ MsgType, v ...interface{}) error
+ Read() *Msg
+}
+
+// The magic token which should be the first 4 bytes of every message and can be used as separator between messages.
+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
+ MsgPongTy = 0x03
+ MsgGetPeersTy = 0x10
+ MsgPeersTy = 0x11
+ MsgTxTy = 0x12
+ MsgBlockTy = 0x13
+ MsgGetChainTy = 0x14
+ MsgNotInChainTy = 0x15
+ MsgGetTxsTy = 0x16
+
+ MsgTalkTy = 0xff
+)
+
+var msgTypeToString = map[MsgType]string{
+ MsgHandshakeTy: "Handshake",
+ MsgDiscTy: "Disconnect",
+ MsgPingTy: "Ping",
+ MsgPongTy: "Pong",
+ MsgGetPeersTy: "Get peers",
+ MsgPeersTy: "Peers",
+ MsgTxTy: "Transactions",
+ MsgBlockTy: "Blocks",
+ MsgGetChainTy: "Get chain",
+ MsgGetTxsTy: "Get Txs",
+ MsgNotInChainTy: "Not in chain",
+}
+
+func (mt MsgType) String() string {
+ return msgTypeToString[mt]
+}
+
+type Msg struct {
+ Type MsgType // Specifies how the encoded data should be interpreted
+ //Data []byte
+ Data *ethutil.Value
+}
+
+func NewMessage(msgType MsgType, data interface{}) *Msg {
+ return &Msg{
+ Type: msgType,
+ Data: ethutil.NewValue(data),
+ }
+}
+
+type Messages []*Msg
+
+// The connection object allows you to set up a connection to the Ethereum network.
+// The Connection object takes care of all encoding and sending objects properly over
+// the network.
+type Connection struct {
+ conn net.Conn
+ nTimeout time.Duration
+ pendingMessages Messages
+}
+
+// Create a new connection to the Ethereum network
+func New(conn net.Conn) *Connection {
+ return &Connection{conn: conn, nTimeout: 500}
+}
+
+// Read, reads from the network. It will block until the next message is received.
+func (self *Connection) Read() *Msg {
+ if len(self.pendingMessages) == 0 {
+ self.readMessages()
+ }
+
+ ret := self.pendingMessages[0]
+ self.pendingMessages = self.pendingMessages[1:]
+
+ return ret
+
+}
+
+// Write to the Ethereum network specifying the type of the message and
+// the data. Data can be of type RlpEncodable or []interface{}. Returns
+// nil or if something went wrong an error.
+func (self *Connection) Write(typ MsgType, v ...interface{}) error {
+ var pack []byte
+
+ slice := [][]interface{}{[]interface{}{byte(typ)}}
+ for _, value := range v {
+ if encodable, ok := value.(ethutil.RlpEncodable); ok {
+ slice = append(slice, encodable.RlpValue())
+ } else if raw, ok := value.([]interface{}); ok {
+ slice = append(slice, raw)
+ } else {
+ panic(fmt.Sprintf("Unable to 'write' object of type %T", value))
+ }
+ }
+
+ // Encode the type and the (RLP encoded) data for sending over the wire
+ encoded := ethutil.NewValue(slice).Encode()
+ payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
+
+ // Write magic token and payload length (first 8 bytes)
+ pack = append(MagicToken, payloadLength...)
+ pack = append(pack, encoded...)
+
+ // Write to the connection
+ _, err := self.conn.Write(pack)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (self *Connection) readMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
+ if len(data) == 0 {
+ return nil, nil, true, nil
+ }
+
+ if len(data) <= 8 {
+ return nil, remaining, false, errors.New("Invalid message")
+ }
+
+ // Check if the received 4 first bytes are the magic token
+ if bytes.Compare(MagicToken, data[:4]) != 0 {
+ return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
+ }
+
+ messageLength := ethutil.BytesToNumber(data[4:8])
+ remaining = data[8+messageLength:]
+ if int(messageLength) > len(data[8:]) {
+ return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
+ }
+
+ message := data[8 : 8+messageLength]
+ decoder := ethutil.NewValueFromBytes(message)
+ // Type of message
+ t := decoder.Get(0).Uint()
+ // Actual data
+ d := decoder.SliceFrom(1)
+
+ msg = &Msg{
+ Type: MsgType(t),
+ Data: d,
+ }
+
+ return
+}
+
+// The basic message reader waits for data on the given connection, decoding
+// and doing a few sanity checks such as if there's a data type and
+// unmarhals the given data
+func (self *Connection) readMessages() (err error) {
+ // The recovering function in case anything goes horribly wrong
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("ethwire.ReadMessage error: %v", r)
+ }
+ }()
+
+ // Buff for writing network message to
+ //buff := make([]byte, 1440)
+ var buff []byte
+ var totalBytes int
+ for {
+ // Give buffering some time
+ self.conn.SetReadDeadline(time.Now().Add(self.nTimeout * time.Millisecond))
+ // Create a new temporarily buffer
+ b := make([]byte, 1440)
+ // Wait for a message from this peer
+ n, _ := self.conn.Read(b)
+ if err != nil && n == 0 {
+ if err.Error() != "EOF" {
+ fmt.Println("err now", err)
+ return err
+ } else {
+ break
+ }
+
+ // Messages can't be empty
+ } else if n == 0 {
+ break
+ }
+
+ buff = append(buff, b[:n]...)
+ totalBytes += n
+ }
+
+ // Reslice buffer
+ buff = buff[:totalBytes]
+ msg, remaining, done, err := self.readMessage(buff)
+ for ; done != true; msg, remaining, done, err = self.readMessage(remaining) {
+ //log.Println("rx", msg)
+
+ if msg != nil {
+ self.pendingMessages = append(self.pendingMessages, msg)
+ }
+ }
+
+ return
+}
+
+func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
+ if len(data) == 0 {
+ return nil, nil, true, nil
+ }
+
+ if len(data) <= 8 {
+ return nil, remaining, false, errors.New("Invalid message")
+ }
+
+ // Check if the received 4 first bytes are the magic token
+ if bytes.Compare(MagicToken, data[:4]) != 0 {
+ return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
+ }
+
+ messageLength := ethutil.BytesToNumber(data[4:8])
+ remaining = data[8+messageLength:]
+ if int(messageLength) > len(data[8:]) {
+ return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
+ }
+
+ message := data[8 : 8+messageLength]
+ decoder := ethutil.NewValueFromBytes(message)
+ // Type of message
+ t := decoder.Get(0).Uint()
+ // Actual data
+ d := decoder.SliceFrom(1)
+
+ msg = &Msg{
+ Type: MsgType(t),
+ Data: d,
+ }
+
+ return
+}
+
+func bufferedRead(conn net.Conn) ([]byte, error) {
+ return nil, nil
+}
+
+// The basic message reader waits for data on the given connection, decoding
+// and doing a few sanity checks such as if there's a data type and
+// unmarhals the given data
+func ReadMessages(conn net.Conn) (msgs []*Msg, err error) {
+ // The recovering function in case anything goes horribly wrong
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("ethwire.ReadMessage error: %v", r)
+ }
+ }()
+
+ // Buff for writing network message to
+ //buff := make([]byte, 1440)
+ var buff []byte
+ var totalBytes int
+ for {
+ // Give buffering some time
+ conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
+ // Create a new temporarily buffer
+ b := make([]byte, 1440)
+ // Wait for a message from this peer
+ n, _ := conn.Read(b)
+ if err != nil && n == 0 {
+ if err.Error() != "EOF" {
+ fmt.Println("err now", err)
+ return nil, err
+ } else {
+ break
+ }
+
+ // Messages can't be empty
+ } else if n == 0 {
+ break
+ }
+
+ buff = append(buff, b[:n]...)
+ totalBytes += n
+ }
+
+ // Reslice buffer
+ buff = buff[:totalBytes]
+ msg, remaining, done, err := ReadMessage(buff)
+ for ; done != true; msg, remaining, done, err = ReadMessage(remaining) {
+ //log.Println("rx", msg)
+
+ if msg != nil {
+ msgs = append(msgs, msg)
+ }
+ }
+
+ return
+}
+
+// The basic message writer takes care of writing data over the given
+// connection and does some basic error checking
+func WriteMessage(conn net.Conn, msg *Msg) error {
+ var pack []byte
+
+ // Encode the type and the (RLP encoded) data for sending over the wire
+ encoded := ethutil.NewValue(append([]interface{}{byte(msg.Type)}, msg.Data.Slice()...)).Encode()
+ payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
+
+ // Write magic token and payload length (first 8 bytes)
+ pack = append(MagicToken, payloadLength...)
+ pack = append(pack, encoded...)
+ //fmt.Printf("payload %v (%v) %q\n", msg.Type, conn.RemoteAddr(), encoded)
+
+ // Write to the connection
+ _, err := conn.Write(pack)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/nat.go b/nat.go
new file mode 100644
index 000000000..999308eb2
--- /dev/null
+++ b/nat.go
@@ -0,0 +1,12 @@
+package eth
+
+import (
+ "net"
+)
+
+// protocol is either "udp" or "tcp"
+type NAT interface {
+ GetExternalAddress() (addr net.IP, err error)
+ AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
+ DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
+}
diff --git a/natpmp.go b/natpmp.go
new file mode 100644
index 000000000..9a1fb652b
--- /dev/null
+++ b/natpmp.go
@@ -0,0 +1,54 @@
+package eth
+
+import (
+ natpmp "code.google.com/p/go-nat-pmp"
+ "fmt"
+ "net"
+)
+
+// Adapt the NAT-PMP protocol to the NAT interface
+
+// TODO:
+// + Register for changes to the external address.
+// + Re-register port mapping when router reboots.
+// + A mechanism for keeping a port mapping registered.
+
+type natPMPClient struct {
+ client *natpmp.Client
+}
+
+func NewNatPMP(gateway net.IP) (nat NAT) {
+ return &natPMPClient{natpmp.NewClient(gateway)}
+}
+
+func (n *natPMPClient) GetExternalAddress() (addr net.IP, err error) {
+ response, err := n.client.GetExternalAddress()
+ if err != nil {
+ return
+ }
+ ip := response.ExternalIPAddress
+ addr = net.IPv4(ip[0], ip[1], ip[2], ip[3])
+ return
+}
+
+func (n *natPMPClient) AddPortMapping(protocol string, externalPort, internalPort int,
+ description string, timeout int) (mappedExternalPort int, err error) {
+ if timeout <= 0 {
+ err = fmt.Errorf("timeout must not be <= 0")
+ return
+ }
+ // Note order of port arguments is switched between our AddPortMapping and the client's AddPortMapping.
+ response, err := n.client.AddPortMapping(protocol, internalPort, externalPort, timeout)
+ if err != nil {
+ return
+ }
+ mappedExternalPort = int(response.MappedExternalPort)
+ return
+}
+
+func (n *natPMPClient) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
+ // To destroy a mapping, send an add-port with
+ // an internalPort of the internal port to destroy, an external port of zero and a time of zero.
+ _, err = n.client.AddPortMapping(protocol, internalPort, 0, 0)
+ return
+}
diff --git a/natupnp.go b/natupnp.go
new file mode 100644
index 000000000..c7f9eeb62
--- /dev/null
+++ b/natupnp.go
@@ -0,0 +1,338 @@
+package eth
+
+// Just enough UPnP to be able to forward ports
+//
+
+import (
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "net"
+ "net/http"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type upnpNAT struct {
+ serviceURL string
+ ourIP string
+}
+
+func Discover() (nat NAT, err error) {
+ ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
+ if err != nil {
+ return
+ }
+ conn, err := net.ListenPacket("udp4", ":0")
+ if err != nil {
+ return
+ }
+ socket := conn.(*net.UDPConn)
+ defer socket.Close()
+
+ err = socket.SetDeadline(time.Now().Add(10 * time.Second))
+ if err != nil {
+ return
+ }
+
+ st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
+ buf := bytes.NewBufferString(
+ "M-SEARCH * HTTP/1.1\r\n" +
+ "HOST: 239.255.255.250:1900\r\n" +
+ st +
+ "MAN: \"ssdp:discover\"\r\n" +
+ "MX: 2\r\n\r\n")
+ message := buf.Bytes()
+ answerBytes := make([]byte, 1024)
+ for i := 0; i < 3; i++ {
+ _, err = socket.WriteToUDP(message, ssdp)
+ if err != nil {
+ return
+ }
+ var n int
+ n, _, err = socket.ReadFromUDP(answerBytes)
+ if err != nil {
+ continue
+ // socket.Close()
+ // return
+ }
+ answer := string(answerBytes[0:n])
+ if strings.Index(answer, "\r\n"+st) < 0 {
+ continue
+ }
+ // HTTP header field names are case-insensitive.
+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
+ locString := "\r\nlocation: "
+ answer = strings.ToLower(answer)
+ locIndex := strings.Index(answer, locString)
+ if locIndex < 0 {
+ continue
+ }
+ loc := answer[locIndex+len(locString):]
+ endIndex := strings.Index(loc, "\r\n")
+ if endIndex < 0 {
+ continue
+ }
+ locURL := loc[0:endIndex]
+ var serviceURL string
+ serviceURL, err = getServiceURL(locURL)
+ if err != nil {
+ return
+ }
+ var ourIP string
+ ourIP, err = getOurIP()
+ if err != nil {
+ return
+ }
+ nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
+ return
+ }
+ err = errors.New("UPnP port discovery failed.")
+ return
+}
+
+// service represents the Service type in an UPnP xml description.
+// Only the parts we care about are present and thus the xml may have more
+// fields than present in the structure.
+type service struct {
+ ServiceType string `xml:"serviceType"`
+ ControlURL string `xml:"controlURL"`
+}
+
+// deviceList represents the deviceList type in an UPnP xml description.
+// Only the parts we care about are present and thus the xml may have more
+// fields than present in the structure.
+type deviceList struct {
+ XMLName xml.Name `xml:"deviceList"`
+ Device []device `xml:"device"`
+}
+
+// serviceList represents the serviceList type in an UPnP xml description.
+// Only the parts we care about are present and thus the xml may have more
+// fields than present in the structure.
+type serviceList struct {
+ XMLName xml.Name `xml:"serviceList"`
+ Service []service `xml:"service"`
+}
+
+// device represents the device type in an UPnP xml description.
+// Only the parts we care about are present and thus the xml may have more
+// fields than present in the structure.
+type device struct {
+ XMLName xml.Name `xml:"device"`
+ DeviceType string `xml:"deviceType"`
+ DeviceList deviceList `xml:"deviceList"`
+ ServiceList serviceList `xml:"serviceList"`
+}
+
+// specVersion represents the specVersion in a UPnP xml description.
+// Only the parts we care about are present and thus the xml may have more
+// fields than present in the structure.
+type specVersion struct {
+ XMLName xml.Name `xml:"specVersion"`
+ Major int `xml:"major"`
+ Minor int `xml:"minor"`
+}
+
+// root represents the Root document for a UPnP xml description.
+// Only the parts we care about are present and thus the xml may have more
+// fields than present in the structure.
+type root struct {
+ XMLName xml.Name `xml:"root"`
+ SpecVersion specVersion
+ Device device
+}
+
+func getChildDevice(d *device, deviceType string) *device {
+ dl := d.DeviceList.Device
+ for i := 0; i < len(dl); i++ {
+ if dl[i].DeviceType == deviceType {
+ return &dl[i]
+ }
+ }
+ return nil
+}
+
+func getChildService(d *device, serviceType string) *service {
+ sl := d.ServiceList.Service
+ for i := 0; i < len(sl); i++ {
+ if sl[i].ServiceType == serviceType {
+ return &sl[i]
+ }
+ }
+ return nil
+}
+
+func getOurIP() (ip string, err error) {
+ hostname, err := os.Hostname()
+ if err != nil {
+ return
+ }
+ p, err := net.LookupIP(hostname)
+ if err != nil && len(p) > 0 {
+ return
+ }
+ return p[0].String(), nil
+}
+
+func getServiceURL(rootURL string) (url string, err error) {
+ r, err := http.Get(rootURL)
+ if err != nil {
+ return
+ }
+ defer r.Body.Close()
+ if r.StatusCode >= 400 {
+ err = errors.New(string(r.StatusCode))
+ return
+ }
+ var root root
+ err = xml.NewDecoder(r.Body).Decode(&root)
+
+ if err != nil {
+ return
+ }
+ a := &root.Device
+ if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
+ err = errors.New("No InternetGatewayDevice")
+ return
+ }
+ b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
+ if b == nil {
+ err = errors.New("No WANDevice")
+ return
+ }
+ c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
+ if c == nil {
+ err = errors.New("No WANConnectionDevice")
+ return
+ }
+ d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
+ if d == nil {
+ err = errors.New("No WANIPConnection")
+ return
+ }
+ url = combineURL(rootURL, d.ControlURL)
+ return
+}
+
+func combineURL(rootURL, subURL string) string {
+ protocolEnd := "://"
+ protoEndIndex := strings.Index(rootURL, protocolEnd)
+ a := rootURL[protoEndIndex+len(protocolEnd):]
+ rootIndex := strings.Index(a, "/")
+ return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
+}
+
+func soapRequest(url, function, message string) (r *http.Response, err error) {
+ fullMessage := "<?xml version=\"1.0\" ?>" +
+ "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
+ "<s:Body>" + message + "</s:Body></s:Envelope>"
+
+ req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
+ req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
+ //req.Header.Set("Transfer-Encoding", "chunked")
+ req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
+ req.Header.Set("Connection", "Close")
+ req.Header.Set("Cache-Control", "no-cache")
+ req.Header.Set("Pragma", "no-cache")
+
+ // log.Stderr("soapRequest ", req)
+ //fmt.Println(fullMessage)
+
+ r, err = http.DefaultClient.Do(req)
+ if err != nil {
+ return
+ }
+
+ if r.Body != nil {
+ defer r.Body.Close()
+ }
+
+ if r.StatusCode >= 400 {
+ // log.Stderr(function, r.StatusCode)
+ err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
+ r = nil
+ return
+ }
+ return
+}
+
+type statusInfo struct {
+ externalIpAddress string
+}
+
+func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
+
+ message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
+ "</u:GetStatusInfo>"
+
+ var response *http.Response
+ response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
+ if err != nil {
+ return
+ }
+
+ // TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
+
+ response.Body.Close()
+ return
+}
+
+func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
+ info, err := n.getStatusInfo()
+ if err != nil {
+ return
+ }
+ addr = net.ParseIP(info.externalIpAddress)
+ return
+}
+
+func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
+ // A single concatenation would break ARM compilation.
+ message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
+ "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
+ message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
+ message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
+ "<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
+ "<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
+ message += description +
+ "</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
+ "</NewLeaseDuration></u:AddPortMapping>"
+
+ var response *http.Response
+ response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
+ if err != nil {
+ return
+ }
+
+ // TODO: check response to see if the port was forwarded
+ // log.Println(message, response)
+ mappedExternalPort = externalPort
+ _ = response
+ return
+}
+
+func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
+
+ message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
+ "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
+ "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
+ "</u:DeletePortMapping>"
+
+ var response *http.Response
+ response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
+ if err != nil {
+ return
+ }
+
+ // TODO: check response to see if the port was deleted
+ // log.Println(message, response)
+ _ = response
+ return
+}
diff --git a/peer.go b/peer.go
new file mode 100644
index 000000000..e50fd43f9
--- /dev/null
+++ b/peer.go
@@ -0,0 +1,785 @@
+package eth
+
+import (
+ "bytes"
+ "container/list"
+ "fmt"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "net"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "time"
+)
+
+var peerlogger = ethlog.NewLogger("PEER")
+
+const (
+ // The size of the output buffer for writing messages
+ outputBufferSize = 50
+ // Current protocol version
+ ProtocolVersion = 20
+ // Interval for ping/pong message
+ pingPongTimer = 2 * time.Second
+)
+
+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
+ DiscBadPeer = 0x03
+ DiscTooManyPeers = 0x04
+ DiscConnDup = 0x05
+ DiscGenesisErr = 0x06
+ DiscProtoErr = 0x07
+)
+
+var discReasonToString = []string{
+ "Disconnect requested",
+ "Disconnect TCP sys error",
+ "Disconnect bad protocol",
+ "Disconnect useless peer",
+ "Disconnect too many peers",
+ "Disconnect already connected",
+ "Disconnect wrong genesis block",
+ "Disconnect incompatible network",
+}
+
+func (d DiscReason) String() string {
+ if len(discReasonToString) < int(d) {
+ return "Unknown"
+ }
+
+ return discReasonToString[d]
+}
+
+// Peer capabilities
+type Caps byte
+
+const (
+ CapPeerDiscTy = 1 << iota
+ CapTxTy
+ CapChainTy
+
+ CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
+)
+
+var capsToString = map[Caps]string{
+ CapPeerDiscTy: "Peer discovery",
+ CapTxTy: "Transaction relaying",
+ CapChainTy: "Block chain relaying",
+}
+
+func (c Caps) IsCap(cap Caps) bool {
+ return c&cap > 0
+}
+
+func (c Caps) String() string {
+ var caps []string
+ if c.IsCap(CapPeerDiscTy) {
+ caps = append(caps, capsToString[CapPeerDiscTy])
+ }
+ if c.IsCap(CapChainTy) {
+ caps = append(caps, capsToString[CapChainTy])
+ }
+ if c.IsCap(CapTxTy) {
+ caps = append(caps, capsToString[CapTxTy])
+ }
+
+ return strings.Join(caps, " | ")
+}
+
+type Peer struct {
+ // Ethereum interface
+ ethereum *Ethereum
+ // Net connection
+ conn net.Conn
+ // Output queue which is used to communicate and handle messages
+ outputQueue chan *ethwire.Msg
+ // Quit channel
+ quit chan bool
+ // Determines whether it's an inbound or outbound peer
+ inbound bool
+ // Flag for checking the peer's connectivity state
+ connected int32
+ disconnect int32
+ // Last known message send
+ lastSend time.Time
+ // Indicated whether a verack has been send or not
+ // This flag is used by writeMessage to check if messages are allowed
+ // to be send or not. If no version is known all messages are ignored.
+ versionKnown bool
+
+ // Last received pong message
+ lastPong int64
+ // Indicates whether a MsgGetPeersTy was requested of the peer
+ // this to prevent receiving false peers.
+ requestedPeerList bool
+
+ host []byte
+ port uint16
+ caps Caps
+
+ // This peer's public key
+ pubkey []byte
+
+ // Indicated whether the node is catching up or not
+ catchingUp bool
+ diverted bool
+ blocksRequested int
+
+ version string
+
+ // We use this to give some kind of pingtime to a node, not very accurate, could be improved.
+ pingTime time.Duration
+ pingStartTime time.Time
+
+ lastRequestedBlock *ethchain.Block
+}
+
+func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
+ pubkey := ethutil.GetKeyRing().Get(0).PublicKey[1:]
+
+ return &Peer{
+ outputQueue: make(chan *ethwire.Msg, outputBufferSize),
+ quit: make(chan bool),
+ ethereum: ethereum,
+ conn: conn,
+ inbound: inbound,
+ disconnect: 0,
+ connected: 1,
+ port: 30303,
+ pubkey: pubkey,
+ blocksRequested: 10,
+ caps: ethereum.ServerCaps(),
+ version: ethutil.Config.ClientString,
+ }
+}
+
+func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
+ p := &Peer{
+ outputQueue: make(chan *ethwire.Msg, outputBufferSize),
+ quit: make(chan bool),
+ ethereum: ethereum,
+ inbound: false,
+ connected: 0,
+ disconnect: 0,
+ caps: caps,
+ version: ethutil.Config.ClientString,
+ }
+
+ // Set up the connection in another goroutine so we don't block the main thread
+ go func() {
+ conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
+
+ if err != nil {
+ peerlogger.Debugln("Connection to peer failed", err)
+ p.Stop()
+ return
+ }
+ p.conn = conn
+
+ // Atomically set the connection state
+ atomic.StoreInt32(&p.connected, 1)
+ atomic.StoreInt32(&p.disconnect, 0)
+
+ p.Start()
+ }()
+
+ return p
+}
+
+// Getters
+func (p *Peer) PingTime() string {
+ return p.pingTime.String()
+}
+func (p *Peer) Inbound() bool {
+ return p.inbound
+}
+func (p *Peer) LastSend() time.Time {
+ return p.lastSend
+}
+func (p *Peer) LastPong() int64 {
+ return p.lastPong
+}
+func (p *Peer) Host() []byte {
+ return p.host
+}
+func (p *Peer) Port() uint16 {
+ return p.port
+}
+func (p *Peer) Version() string {
+ return p.version
+}
+func (p *Peer) Connected() *int32 {
+ return &p.connected
+}
+
+// Setters
+func (p *Peer) SetVersion(version string) {
+ p.version = version
+}
+
+// Outputs any RLP encoded data to the peer
+func (p *Peer) QueueMessage(msg *ethwire.Msg) {
+ if atomic.LoadInt32(&p.connected) != 1 {
+ return
+ }
+ p.outputQueue <- msg
+}
+
+func (p *Peer) writeMessage(msg *ethwire.Msg) {
+ // Ignore the write if we're not connected
+ if atomic.LoadInt32(&p.connected) != 1 {
+ return
+ }
+
+ if !p.versionKnown {
+ switch msg.Type {
+ case ethwire.MsgHandshakeTy: // Ok
+ default: // Anything but ack is allowed
+ return
+ }
+ }
+
+ peerlogger.DebugDetailln("<=", msg.Type, msg.Data)
+
+ err := ethwire.WriteMessage(p.conn, msg)
+ if err != nil {
+ peerlogger.Debugln(" Can't send message:", err)
+ // Stop the client if there was an error writing to it
+ p.Stop()
+ return
+ }
+}
+
+// Outbound message handler. Outbound messages are handled here
+func (p *Peer) HandleOutbound() {
+ // The ping timer. Makes sure that every 2 minutes a ping is send to the peer
+ pingTimer := time.NewTicker(pingPongTimer)
+ serviceTimer := time.NewTicker(5 * time.Minute)
+
+out:
+ for {
+ select {
+ // Main message queue. All outbound messages are processed through here
+ case msg := <-p.outputQueue:
+ p.writeMessage(msg)
+ p.lastSend = time.Now()
+
+ // Ping timer
+ case <-pingTimer.C:
+ timeSince := time.Since(time.Unix(p.lastPong, 0))
+ if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
+ peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
+ p.Stop()
+ return
+ }
+ p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
+ p.pingStartTime = time.Now()
+
+ // Service timer takes care of peer broadcasting, transaction
+ // posting or block posting
+ case <-serviceTimer.C:
+ if p.caps&CapPeerDiscTy > 0 {
+ msg := p.peersMessage()
+ p.ethereum.BroadcastMsg(msg)
+ }
+
+ case <-p.quit:
+ // Break out of the for loop if a quit message is posted
+ break out
+ }
+ }
+
+clean:
+ // This loop is for draining the output queue and anybody waiting for us
+ for {
+ select {
+ case <-p.outputQueue:
+ // TODO
+ default:
+ break clean
+ }
+ }
+}
+
+// Inbound handler. Inbound messages are received here and passed to the appropriate methods
+func (p *Peer) HandleInbound() {
+ for atomic.LoadInt32(&p.disconnect) == 0 {
+
+ // HMM?
+ time.Sleep(500 * time.Millisecond)
+ // Wait for a message from the peer
+ msgs, err := ethwire.ReadMessages(p.conn)
+ if err != nil {
+ peerlogger.Debugln(err)
+ }
+ for _, msg := range msgs {
+ peerlogger.DebugDetailln("=>", msg.Type, msg.Data)
+
+ switch msg.Type {
+ case ethwire.MsgHandshakeTy:
+ // Version message
+ p.handleHandshake(msg)
+
+ if p.caps.IsCap(CapPeerDiscTy) {
+ p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, ""))
+ }
+ case ethwire.MsgDiscTy:
+ p.Stop()
+ peerlogger.Infoln("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint()))
+ case ethwire.MsgPingTy:
+ // Respond back with pong
+ p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
+ case ethwire.MsgPongTy:
+ // If we received a pong back from a peer we set the
+ // last pong so the peer handler knows this peer is still
+ // active.
+ p.lastPong = time.Now().Unix()
+ p.pingTime = time.Now().Sub(p.pingStartTime)
+ case ethwire.MsgBlockTy:
+ // Get all blocks and process them
+ var block, lastBlock *ethchain.Block
+ var err error
+
+ // Make sure we are actually receiving anything
+ if msg.Data.Len()-1 > 1 && p.diverted {
+ // We requested blocks and now we need to make sure we have a common ancestor somewhere in these blocks so we can find
+ // common ground to start syncing from
+ lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1))
+ if p.lastRequestedBlock != nil && bytes.Compare(lastBlock.Hash(), p.lastRequestedBlock.Hash()) == 0 {
+ p.catchingUp = false
+ continue
+ }
+ p.lastRequestedBlock = lastBlock
+ peerlogger.Infof("Last block: %x. Checking if we have it locally.\n", lastBlock.Hash())
+ for i := msg.Data.Len() - 1; i >= 0; i-- {
+ block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
+ // Do we have this block on our chain? If so we can continue
+ if !p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) {
+ // We don't have this block, but we do have a block with the same prevHash, diversion time!
+ if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) {
+ p.diverted = false
+ if !p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) {
+ p.SyncWithPeerToLastKnown()
+ }
+ break
+ }
+ }
+ }
+ if !p.ethereum.StateManager().BlockChain().HasBlock(lastBlock.Hash()) {
+ // If we can't find a common ancenstor we need to request more blocks.
+ // FIXME: At one point this won't scale anymore since we are not asking for an offset
+ // we just keep increasing the amount of blocks.
+ p.blocksRequested = p.blocksRequested * 2
+
+ peerlogger.Infof("No common ancestor found, requesting %d more blocks.\n", p.blocksRequested)
+ p.catchingUp = false
+ p.FindCommonParentBlock()
+ break
+ }
+ }
+
+ for i := msg.Data.Len() - 1; i >= 0; i-- {
+ block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
+
+ //p.ethereum.StateManager().PrepareDefault(block)
+ //state := p.ethereum.StateManager().CurrentState()
+ err = p.ethereum.StateManager().Process(block, false)
+
+ if err != nil {
+ if ethutil.Config.Debug {
+ peerlogger.Infof("Block %x failed\n", block.Hash())
+ peerlogger.Infof("%v\n", err)
+ peerlogger.Debugln(block)
+ }
+ break
+ } else {
+ lastBlock = block
+ }
+ }
+
+ if msg.Data.Len() == 0 {
+ // Set catching up to false if
+ // the peer has nothing left to give
+ p.catchingUp = false
+ }
+
+ if err != nil {
+ // If the parent is unknown try to catch up with this peer
+ if ethchain.IsParentErr(err) {
+ peerlogger.Infoln("Attempting to catch. Parent known")
+ p.catchingUp = false
+ p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
+ } else if ethchain.IsValidationErr(err) {
+ fmt.Println("Err:", err)
+ p.catchingUp = false
+ }
+ } else {
+ // If we're catching up, try to catch up further.
+ if p.catchingUp && msg.Data.Len() > 1 {
+ if lastBlock != nil {
+ blockInfo := lastBlock.BlockInfo()
+ peerlogger.DebugDetailf("Synced chain to #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
+ }
+
+ p.catchingUp = false
+
+ hash := p.ethereum.BlockChain().CurrentBlock.Hash()
+ p.CatchupWithPeer(hash)
+ }
+ }
+
+ case ethwire.MsgTxTy:
+ // If the message was a transaction queue the transaction
+ // in the TxPool where it will undergo validation and
+ // processing when a new block is found
+ for i := 0; i < msg.Data.Len(); i++ {
+ tx := ethchain.NewTransactionFromValue(msg.Data.Get(i))
+ p.ethereum.TxPool().QueueTransaction(tx)
+ }
+ case ethwire.MsgGetPeersTy:
+ // Flag this peer as a 'requested of new peers' this to
+ // prevent malicious peers being forced.
+ p.requestedPeerList = true
+ // Peer asked for list of connected peers
+ p.pushPeers()
+ case ethwire.MsgPeersTy:
+ // Received a list of peers (probably because MsgGetPeersTy was send)
+ // Only act on message if we actually requested for a peers list
+ if p.requestedPeerList {
+ data := msg.Data
+ // Create new list of possible peers for the ethereum to process
+ peers := make([]string, data.Len())
+ // Parse each possible peer
+ for i := 0; i < data.Len(); i++ {
+ value := data.Get(i)
+ peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
+ }
+
+ // Connect to the list of peers
+ p.ethereum.ProcessPeerList(peers)
+ // Mark unrequested again
+ p.requestedPeerList = false
+
+ }
+ case ethwire.MsgGetChainTy:
+ var parent *ethchain.Block
+ // Length minus one since the very last element in the array is a count
+ l := msg.Data.Len() - 1
+ // Ignore empty get chains
+ if l == 0 {
+ break
+ }
+
+ // Amount of parents in the canonical chain
+ //amountOfBlocks := msg.Data.Get(l).AsUint()
+ amountOfBlocks := uint64(100)
+
+ // Check each SHA block hash from the message and determine whether
+ // the SHA is in the database
+ for i := 0; i < l; i++ {
+ if data := msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) {
+ parent = p.ethereum.BlockChain().GetBlock(data)
+ break
+ }
+ }
+
+ // If a parent is found send back a reply
+ if parent != nil {
+ peerlogger.DebugDetailf("Found canonical block, returning chain from: %x ", parent.Hash())
+ chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks)
+ if len(chain) > 0 {
+ //peerlogger.Debugf("Returning %d blocks: %x ", len(chain), parent.Hash())
+ p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
+ } else {
+ p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, []interface{}{}))
+ }
+
+ } else {
+ //peerlogger.Debugf("Could not find a similar block")
+ // If no blocks are found we send back a reply with msg not in chain
+ // and the last hash from get chain
+ if l > 0 {
+ lastHash := msg.Data.Get(l - 1)
+ //log.Printf("Sending not in chain with hash %x\n", lastHash.AsRaw())
+ p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
+ }
+ }
+ case ethwire.MsgNotInChainTy:
+ peerlogger.DebugDetailf("Not in chain: %x\n", msg.Data.Get(0).Bytes())
+ if p.diverted == true {
+ // If were already looking for a common parent and we get here again we need to go deeper
+ p.blocksRequested = p.blocksRequested * 2
+ }
+ p.diverted = true
+ p.catchingUp = false
+ p.FindCommonParentBlock()
+ case ethwire.MsgGetTxsTy:
+ // Get the current transactions of the pool
+ txs := p.ethereum.TxPool().CurrentTransactions()
+ // Get the RlpData values from the txs
+ txsInterface := make([]interface{}, len(txs))
+ for i, tx := range txs {
+ txsInterface[i] = tx.RlpData()
+ }
+ // Broadcast it back to the peer
+ p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface))
+
+ // Unofficial but fun nonetheless
+ case ethwire.MsgTalkTy:
+ peerlogger.Infoln("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str())
+ }
+ }
+ }
+ p.Stop()
+}
+
+func (p *Peer) Start() {
+ peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
+ servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
+
+ if p.inbound {
+ p.host, p.port = packAddr(peerHost, peerPort)
+ } else {
+ p.host, p.port = packAddr(servHost, servPort)
+ }
+
+ err := p.pushHandshake()
+ if err != nil {
+ peerlogger.Debugln("Peer can't send outbound version ack", err)
+
+ p.Stop()
+
+ return
+ }
+
+ go p.HandleOutbound()
+ // Run the inbound handler in a new goroutine
+ go p.HandleInbound()
+
+ // Wait a few seconds for startup and then ask for an initial ping
+ time.Sleep(2 * time.Second)
+ p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
+ p.pingStartTime = time.Now()
+
+}
+
+func (p *Peer) Stop() {
+ if atomic.AddInt32(&p.disconnect, 1) != 1 {
+ return
+ }
+
+ close(p.quit)
+ if atomic.LoadInt32(&p.connected) != 0 {
+ p.writeMessage(ethwire.NewMessage(ethwire.MsgDiscTy, ""))
+ p.conn.Close()
+ }
+
+ // Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
+ p.ethereum.RemovePeer(p)
+}
+
+func (p *Peer) pushHandshake() error {
+ keyRing := ethutil.GetKeyRing().Get(0)
+ if keyRing != nil {
+ pubkey := keyRing.PublicKey
+
+ msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
+ uint32(ProtocolVersion), uint32(0), []byte(p.version), byte(p.caps), p.port, pubkey[1:],
+ })
+
+ p.QueueMessage(msg)
+ }
+
+ return nil
+}
+
+func (p *Peer) peersMessage() *ethwire.Msg {
+ outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
+ // Serialise each peer
+ for i, peer := range p.ethereum.InOutPeers() {
+ // Don't return localhost as valid peer
+ if !net.ParseIP(peer.conn.RemoteAddr().String()).IsLoopback() {
+ outPeers[i] = peer.RlpData()
+ }
+ }
+
+ // Return the message to the peer with the known list of connected clients
+ return ethwire.NewMessage(ethwire.MsgPeersTy, outPeers)
+}
+
+// Pushes the list of outbound peers to the client when requested
+func (p *Peer) pushPeers() {
+ p.QueueMessage(p.peersMessage())
+}
+
+func (p *Peer) handleHandshake(msg *ethwire.Msg) {
+ c := msg.Data
+
+ // Set pubkey
+ p.pubkey = c.Get(5).Bytes()
+
+ if p.pubkey == nil {
+ peerlogger.Warnln("Pubkey required, not supplied in handshake.")
+ p.Stop()
+ return
+ }
+
+ usedPub := 0
+ // This peer is already added to the peerlist so we expect to find a double pubkey at least once
+
+ eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
+ if bytes.Compare(p.pubkey, peer.pubkey) == 0 {
+ usedPub++
+ }
+ })
+
+ if usedPub > 0 {
+ peerlogger.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey)
+ p.Stop()
+ return
+ }
+
+ if c.Get(0).Uint() != ProtocolVersion {
+ peerlogger.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint())
+ p.Stop()
+ return
+ }
+
+ // [PROTOCOL_VERSION, NETWORK_ID, CLIENT_ID, CAPS, PORT, PUBKEY]
+ p.versionKnown = true
+
+ // If this is an inbound connection send an ack back
+ if p.inbound {
+ p.port = uint16(c.Get(4).Uint())
+
+ // Self connect detection
+ keyPair := ethutil.GetKeyRing().Get(0)
+ if bytes.Compare(keyPair.PublicKey, p.pubkey) == 0 {
+ p.Stop()
+
+ return
+ }
+
+ }
+
+ // Set the peer's caps
+ p.caps = Caps(c.Get(3).Byte())
+
+ // Get a reference to the peers version
+ versionString := c.Get(2).Str()
+ if len(versionString) > 0 {
+ p.SetVersion(c.Get(2).Str())
+ }
+
+ p.ethereum.PushPeer(p)
+ p.ethereum.reactor.Post("peerList", p.ethereum.Peers())
+
+ ethlogger.Infof("Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers)
+
+ // Catch up with the connected peer
+ if !p.ethereum.IsUpToDate() {
+ peerlogger.Debugln("Already syncing up with a peer; sleeping")
+ time.Sleep(10 * time.Second)
+ }
+ p.SyncWithPeerToLastKnown()
+
+ peerlogger.Debugln(p)
+}
+
+func (p *Peer) String() string {
+ var strBoundType string
+ if p.inbound {
+ strBoundType = "inbound"
+ } else {
+ strBoundType = "outbound"
+ }
+ var strConnectType string
+ if atomic.LoadInt32(&p.disconnect) == 0 {
+ strConnectType = "connected"
+ } else {
+ strConnectType = "disconnected"
+ }
+
+ return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version, p.caps)
+
+}
+func (p *Peer) SyncWithPeerToLastKnown() {
+ p.catchingUp = false
+ p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
+}
+
+func (p *Peer) FindCommonParentBlock() {
+ if p.catchingUp {
+ return
+ }
+
+ p.catchingUp = true
+ if p.blocksRequested == 0 {
+ p.blocksRequested = 20
+ }
+ blocks := p.ethereum.BlockChain().GetChain(p.ethereum.BlockChain().CurrentBlock.Hash(), p.blocksRequested)
+
+ var hashes []interface{}
+ for _, block := range blocks {
+ hashes = append(hashes, block.Hash())
+ }
+
+ msgInfo := append(hashes, uint64(len(hashes)))
+
+ peerlogger.DebugDetailf("Asking for block from %x (%d total) from %s\n", p.ethereum.BlockChain().CurrentBlock.Hash(), len(hashes), p.conn.RemoteAddr().String())
+
+ msg := ethwire.NewMessage(ethwire.MsgGetChainTy, msgInfo)
+ p.QueueMessage(msg)
+}
+func (p *Peer) CatchupWithPeer(blockHash []byte) {
+ if !p.catchingUp {
+ // Make sure nobody else is catching up when you want to do this
+ p.catchingUp = true
+ msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(50)})
+ p.QueueMessage(msg)
+
+ peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())
+
+ msg = ethwire.NewMessage(ethwire.MsgGetTxsTy, []interface{}{})
+ p.QueueMessage(msg)
+ }
+}
+
+func (p *Peer) RlpData() []interface{} {
+ return []interface{}{p.host, p.port, p.pubkey}
+}
+
+func packAddr(address, port string) ([]byte, uint16) {
+ addr := strings.Split(address, ".")
+ a, _ := strconv.Atoi(addr[0])
+ b, _ := strconv.Atoi(addr[1])
+ c, _ := strconv.Atoi(addr[2])
+ d, _ := strconv.Atoi(addr[3])
+ host := []byte{byte(a), byte(b), byte(c), byte(d)}
+ prt, _ := strconv.Atoi(port)
+
+ return host, uint16(prt)
+}
+
+func unpackAddr(value *ethutil.Value, p uint64) string {
+ byts := value.Bytes()
+ a := strconv.Itoa(int(byts[0]))
+ b := strconv.Itoa(int(byts[1]))
+ c := strconv.Itoa(int(byts[2]))
+ d := strconv.Itoa(int(byts[3]))
+ host := strings.Join([]string{a, b, c, d}, ".")
+ port := strconv.Itoa(int(p))
+
+ return net.JoinHostPort(host, port)
+}