aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--ethchain/address.go76
-rw-r--r--ethchain/address_test.go8
-rw-r--r--ethchain/asm.go59
-rw-r--r--ethchain/block.go35
-rw-r--r--ethchain/block_chain.go137
-rw-r--r--ethchain/block_chain_test.go115
-rw-r--r--ethchain/closure.go64
-rw-r--r--ethchain/contract.go94
-rw-r--r--ethchain/dagger.go31
-rw-r--r--ethchain/error.go23
-rw-r--r--ethchain/keypair.go17
-rw-r--r--ethchain/stack.go194
-rw-r--r--ethchain/state.go92
-rw-r--r--ethchain/state_manager.go214
-rw-r--r--ethchain/state_object.go194
-rw-r--r--ethchain/transaction.go97
-rw-r--r--ethchain/transaction_pool.go47
-rw-r--r--ethchain/transaction_test.go53
-rw-r--r--ethchain/types.go230
-rw-r--r--ethchain/vm.go426
-rw-r--r--ethchain/vm_test.go194
-rw-r--r--ethereum.go14
-rw-r--r--ethminer/miner.go156
-rw-r--r--ethpub/pub.go147
-rw-r--r--ethpub/types.go123
-rw-r--r--ethrpc/packages.go215
-rw-r--r--ethrpc/server.go62
-rw-r--r--ethutil/big.go37
-rw-r--r--ethutil/bytes.go28
-rw-r--r--ethutil/common.go20
-rw-r--r--ethutil/config.go13
-rw-r--r--ethutil/mnemonic.go1690
-rw-r--r--ethutil/mnemonic_test.go74
-rw-r--r--ethutil/package.go123
-rw-r--r--ethutil/parsing.go144
-rw-r--r--ethutil/parsing_test.go32
-rw-r--r--ethutil/rlp.go19
-rw-r--r--ethutil/rlp_test.go15
-rw-r--r--ethutil/script.go41
-rw-r--r--ethutil/trie.go19
-rw-r--r--ethutil/trie_test.go2
-rw-r--r--ethutil/value.go16
-rw-r--r--ethwire/messaging.go2
-rw-r--r--natupnp.go4
-rw-r--r--peer.go185
46 files changed, 4456 insertions, 1127 deletions
diff --git a/README.md b/README.md
index 0f0a33edb..0a622fdcb 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Ethereum
Ethereum Go Development package (C) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof
-of Concept 3.5". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
+of Concept 5.0 RC1". 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.
diff --git a/ethchain/address.go b/ethchain/address.go
deleted file mode 100644
index 0b3ef7c05..000000000
--- a/ethchain/address.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package ethchain
-
-import (
- "github.com/ethereum/eth-go/ethutil"
- "math/big"
-)
-
-type Account struct {
- address []byte
- Amount *big.Int
- Nonce uint64
-}
-
-func NewAccount(address []byte, amount *big.Int) *Account {
- return &Account{address, amount, 0}
-}
-
-func NewAccountFromData(address, data []byte) *Account {
- account := &Account{address: address}
- account.RlpDecode(data)
-
- return account
-}
-
-func (a *Account) AddFee(fee *big.Int) {
- a.AddFunds(fee)
-}
-
-func (a *Account) AddFunds(funds *big.Int) {
- a.Amount.Add(a.Amount, funds)
-}
-
-func (a *Account) Address() []byte {
- return a.address
-}
-
-// Implements Callee
-func (a *Account) ReturnGas(value *big.Int, state *State) {
- // Return the value back to the sender
- a.AddFunds(value)
- state.UpdateAccount(a.address, a)
-}
-
-func (a *Account) RlpEncode() []byte {
- return ethutil.Encode([]interface{}{a.Amount, a.Nonce})
-}
-
-func (a *Account) RlpDecode(data []byte) {
- decoder := ethutil.NewValueFromBytes(data)
-
- a.Amount = decoder.Get(0).BigInt()
- a.Nonce = decoder.Get(1).Uint()
-}
-
-type AddrStateStore struct {
- states map[string]*AccountState
-}
-
-func NewAddrStateStore() *AddrStateStore {
- return &AddrStateStore{states: make(map[string]*AccountState)}
-}
-
-func (s *AddrStateStore) Add(addr []byte, account *Account) *AccountState {
- state := &AccountState{Nonce: account.Nonce, Account: account}
- s.states[string(addr)] = state
- return state
-}
-
-func (s *AddrStateStore) Get(addr []byte) *AccountState {
- return s.states[string(addr)]
-}
-
-type AccountState struct {
- Nonce uint64
- Account *Account
-}
diff --git a/ethchain/address_test.go b/ethchain/address_test.go
deleted file mode 100644
index 161e1b251..000000000
--- a/ethchain/address_test.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package ethchain
-
-import (
- "testing"
-)
-
-func TestAddressState(t *testing.T) {
-}
diff --git a/ethchain/asm.go b/ethchain/asm.go
new file mode 100644
index 000000000..3194549ba
--- /dev/null
+++ b/ethchain/asm.go
@@ -0,0 +1,59 @@
+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("%v", op))
+
+ switch op {
+ case oPUSH: // Push PC+1 on to the stack
+ pc.Add(pc, ethutil.Big1)
+ data := script[pc.Int64() : pc.Int64()+32]
+ val := ethutil.BigD(data)
+
+ var b []byte
+ if val.Int64() == 0 {
+ b = []byte{0}
+ } else {
+ b = val.Bytes()
+ }
+
+ asm = append(asm, fmt.Sprintf("0x%x", b))
+
+ pc.Add(pc, big.NewInt(31))
+ case oPUSH20:
+ pc.Add(pc, ethutil.Big1)
+ data := script[pc.Int64() : pc.Int64()+20]
+ val := ethutil.BigD(data)
+ var b []byte
+ if val.Int64() == 0 {
+ b = []byte{0}
+ } else {
+ b = val.Bytes()
+ }
+
+ asm = append(asm, fmt.Sprintf("0x%x", b))
+
+ pc.Add(pc, big.NewInt(19))
+ }
+
+ pc.Add(pc, ethutil.Big1)
+ }
+
+ return
+}
diff --git a/ethchain/block.go b/ethchain/block.go
index 1f63c2c9e..aac50ccb1 100644
--- a/ethchain/block.go
+++ b/ethchain/block.go
@@ -113,11 +113,6 @@ 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.Time, block.Extra}))
}
-func (block *Block) PrintHash() {
- fmt.Println(block)
- fmt.Println(ethutil.NewValue(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Extra, block.Nonce})))
-}
-
func (block *Block) State() *State {
return block.state
}
@@ -142,12 +137,13 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
data := block.state.trie.Get(string(block.Coinbase))
// Get the ether (Coinbase) and add the fee (gief fee to miner)
- ether := NewAccountFromData(block.Coinbase, []byte(data))
+ account := NewStateObjectFromBytes(block.Coinbase, []byte(data))
base = new(big.Int)
- ether.Amount = base.Add(ether.Amount, fee)
+ account.Amount = base.Add(account.Amount, fee)
- block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
+ //block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
+ block.state.UpdateStateObject(account)
return true
}
@@ -178,26 +174,6 @@ func (block *Block) MakeContract(tx *Transaction) {
}
/////// Block Encoding
-func (block *Block) encodedUncles() interface{} {
- uncles := make([]interface{}, len(block.Uncles))
- for i, uncle := range block.Uncles {
- uncles[i] = uncle.RlpEncode()
- }
-
- return uncles
-}
-
-func (block *Block) encodedTxs() interface{} {
- // Marshal the transactions of this block
- encTx := make([]interface{}, len(block.transactions))
- for i, tx := range block.transactions {
- // Cast it to a string (safe)
- encTx[i] = tx.RlpData()
- }
-
- return encTx
-}
-
func (block *Block) rlpTxs() interface{} {
// Marshal the transactions of this block
encTx := make([]interface{}, len(block.transactions))
@@ -304,6 +280,9 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
func (block *Block) String() string {
return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x\nTxs:%d\n", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce, len(block.transactions))
}
+func (block *Block) GetRoot() interface{} {
+ return block.state.trie.Root
+}
//////////// UNEXPORTED /////////////////
func (block *Block) header() []interface{} {
diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go
index 2865e0a21..2be4cd92b 100644
--- a/ethchain/block_chain.go
+++ b/ethchain/block_chain.go
@@ -3,6 +3,7 @@ package ethchain
import (
"bytes"
"github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
"log"
"math"
"math/big"
@@ -23,7 +24,8 @@ type BlockChain struct {
func NewBlockChain(ethereum EthManager) *BlockChain {
bc := &BlockChain{}
- bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis))
+ bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
+ bc.Ethereum = ethereum
bc.setLastBlock()
@@ -78,6 +80,128 @@ func (bc *BlockChain) HasBlock(hash []byte) bool {
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 {
+ log.Println("[CHAIN] We have found the common parent block, breaking")
+ break
+ }
+ log.Println("Checking incoming blocks:")
+ chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
+ }
+
+ log.Println("[CHAIN] 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 {
+ log.Println("[CHAIN] 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
+ log.Println("[CHAIN] At genesis block, breaking")
+ break
+ }
+ curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
+ }
+
+ log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty)
+ if chainDifficulty.Cmp(curChainDifficulty) == 1 {
+ log.Printf("[CHAIN] The incoming Chain beat our asses, resetting to block: %x", commonBlockHash)
+ bc.ResetTillBlockHash(commonBlockHash)
+ return false
+ } else {
+ log.Println("[CHAIN] 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 {
+ // TODO: Somehow this doesn't really give the right numbers, double check.
+ // TODO: Change logs into debug lines
+ returnTo = bc.GetBlock(hash)
+ bc.CurrentBlock = returnTo
+ bc.LastBlockHash = returnTo.Hash()
+ info := bc.BlockInfo(returnTo)
+ bc.LastBlockNumber = info.Number
+ }
+
+ // XXX Why are we resetting? This is the block chain, it has nothing to do with states
+ //bc.Ethereum.StateManager().PrepareDefault(returnTo)
+
+ 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 {
+ log.Println("[CHAIN] We have arrived at the the common parent block, breaking")
+ break
+ }
+ err = ethutil.Config.Db.Delete(block.Hash())
+ if err != nil {
+ return err
+ }
+ }
+ log.Println("[CHAIN] Split chain deleted and reverted to common parent block.")
+ return nil
+}
+
func (bc *BlockChain) GenesisBlock() *Block {
return bc.genesisBlock
}
@@ -136,12 +260,13 @@ func AddTestNetFunds(block *Block) {
"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
+ "2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran
} {
//log.Println("2^200 Wei to", addr)
codedAddr := ethutil.FromHex(addr)
- addr := block.state.GetAccount(codedAddr)
- addr.Amount = ethutil.BigPow(2, 200)
- block.state.UpdateAccount(codedAddr, addr)
+ account := block.state.GetAccount(codedAddr)
+ account.Amount = ethutil.BigPow(2, 200)
+ block.state.UpdateStateObject(account)
}
}
@@ -180,8 +305,8 @@ func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
// 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()
@@ -196,7 +321,7 @@ func (bc *BlockChain) GetBlock(hash []byte) *Block {
return nil
}
- return NewBlockFromData(data)
+ return NewBlockFromBytes(data)
}
func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
diff --git a/ethchain/block_chain_test.go b/ethchain/block_chain_test.go
new file mode 100644
index 000000000..30eb62266
--- /dev/null
+++ b/ethchain/block_chain_test.go
@@ -0,0 +1,115 @@
+package ethchain
+
+import (
+ "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) 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")
+
+ 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)
+ tm.StateManager().PrepareDefault(block)
+ err := tm.StateManager().ProcessBlock(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/closure.go b/ethchain/closure.go
index 2e809aa9d..7e911ad99 100644
--- a/ethchain/closure.go
+++ b/ethchain/closure.go
@@ -7,33 +7,39 @@ import (
"math/big"
)
-type Callee interface {
- ReturnGas(*big.Int, *State)
+type ClosureRef interface {
+ ReturnGas(*big.Int, *big.Int, *State)
Address() []byte
-}
-
-type ClosureBody interface {
- Callee
- ethutil.RlpEncodable
GetMem(*big.Int) *ethutil.Value
SetMem(*big.Int, *ethutil.Value)
+ N() *big.Int
}
// Basic inline closure object which implement the 'closure' interface
type Closure struct {
- callee Callee
- object ClosureBody
+ callee *StateObject
+ object *StateObject
+ Script []byte
State *State
Gas *big.Int
+ Price *big.Int
Value *big.Int
Args []byte
}
// Create a new closure for the given data items
-func NewClosure(callee Callee, object ClosureBody, state *State, gas, val *big.Int) *Closure {
- return &Closure{callee, object, state, gas, val, nil}
+func NewClosure(callee, object *StateObject, script []byte, state *State, gas, price, val *big.Int) *Closure {
+ c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil}
+
+ // In most cases gas, price and value are pointers to transaction objects
+ // and we don't want the transaction's values to change.
+ c.Gas = new(big.Int).Set(gas)
+ c.Price = new(big.Int).Set(price)
+ c.Value = new(big.Int).Set(val)
+
+ return c
}
// Retuns the x element in data slice
@@ -46,6 +52,20 @@ func (c *Closure) GetMem(x *big.Int) *ethutil.Value {
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) SetMem(x *big.Int, val *ethutil.Value) {
c.object.SetMem(x, val)
}
@@ -54,10 +74,12 @@ func (c *Closure) Address() []byte {
return c.object.Address()
}
-func (c *Closure) Call(vm *Vm, args []byte) []byte {
+type DebugHook func(step int, op OpCode, mem *Memory, stack *Stack)
+
+func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) ([]byte, error) {
c.Args = args
- return vm.RunClosure(c)
+ return vm.RunClosure(c, hook)
}
func (c *Closure) Return(ret []byte) []byte {
@@ -65,26 +87,28 @@ func (c *Closure) Return(ret []byte) []byte {
// If no callee is present return it to
// the origin (i.e. contract or tx)
if c.callee != nil {
- c.callee.ReturnGas(c.Gas, c.State)
+ c.callee.ReturnGas(c.Gas, c.Price, c.State)
} else {
- c.object.ReturnGas(c.Gas, c.State)
- // TODO incase it's a POST contract we gotta serialise the contract again.
- // But it's not yet defined
+ c.object.ReturnGas(c.Gas, c.Price, c.State)
}
return ret
}
// Implement the Callee interface
-func (c *Closure) ReturnGas(gas *big.Int, state *State) {
+func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
// Return the gas to the closure
c.Gas.Add(c.Gas, gas)
}
-func (c *Closure) Object() ClosureBody {
+func (c *Closure) Object() *StateObject {
return c.object
}
-func (c *Closure) Callee() Callee {
+func (c *Closure) Callee() *StateObject {
return c.callee
}
+
+func (c *Closure) N() *big.Int {
+ return c.object.N()
+}
diff --git a/ethchain/contract.go b/ethchain/contract.go
deleted file mode 100644
index f7ae01753..000000000
--- a/ethchain/contract.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package ethchain
-
-import (
- "github.com/ethereum/eth-go/ethutil"
- "math/big"
-)
-
-type Contract struct {
- Amount *big.Int
- Nonce uint64
- //state *ethutil.Trie
- state *State
- address []byte
-}
-
-func NewContract(address []byte, Amount *big.Int, root []byte) *Contract {
- contract := &Contract{address: address, Amount: Amount, Nonce: 0}
- contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root)))
-
- return contract
-}
-
-func NewContractFromBytes(address, data []byte) *Contract {
- contract := &Contract{address: address}
- contract.RlpDecode(data)
-
- return contract
-}
-
-func (c *Contract) Addr(addr []byte) *ethutil.Value {
- return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr))))
-}
-
-func (c *Contract) SetAddr(addr []byte, value interface{}) {
- c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
-}
-
-func (c *Contract) State() *State {
- return c.state
-}
-
-func (c *Contract) GetMem(num *big.Int) *ethutil.Value {
- nb := ethutil.BigToBytes(num, 256)
-
- return c.Addr(nb)
-}
-
-func (c *Contract) SetMem(num *big.Int, val *ethutil.Value) {
- addr := ethutil.BigToBytes(num, 256)
- c.state.trie.Update(string(addr), string(val.Encode()))
-}
-
-// Return the gas back to the origin. Used by the Virtual machine or Closures
-func (c *Contract) ReturnGas(val *big.Int, state *State) {
- c.Amount.Add(c.Amount, val)
-}
-
-func (c *Contract) Address() []byte {
- return c.address
-}
-
-func (c *Contract) RlpEncode() []byte {
- return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root})
-}
-
-func (c *Contract) RlpDecode(data []byte) {
- decoder := ethutil.NewValueFromBytes(data)
-
- c.Amount = decoder.Get(0).BigInt()
- c.Nonce = decoder.Get(1).Uint()
- c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
-}
-
-func MakeContract(tx *Transaction, state *State) *Contract {
- // Create contract if there's no recipient
- if tx.IsContract() {
- addr := tx.Hash()[12:]
-
- value := tx.Value
- contract := NewContract(addr, value, []byte(""))
- state.trie.Update(string(addr), string(contract.RlpEncode()))
- for i, val := range tx.Data {
- if len(val) > 0 {
- bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
- contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val)))
- }
- }
- state.trie.Update(string(addr), string(contract.RlpEncode()))
-
- return contract
- }
-
- return nil
-}
diff --git a/ethchain/dagger.go b/ethchain/dagger.go
index 5b4f8b2cd..9d2df4069 100644
--- a/ethchain/dagger.go
+++ b/ethchain/dagger.go
@@ -11,7 +11,7 @@ import (
)
type PoW interface {
- Search(block *Block) []byte
+ Search(block *Block, reactChan chan ethutil.React) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool
}
@@ -19,15 +19,30 @@ type EasyPow struct {
hash *big.Int
}
-func (pow *EasyPow) Search(block *Block) []byte {
+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 {
- sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
- if pow.Verify(hash, diff, sha) {
- return sha
+ select {
+ case <-reactChan:
+ log.Println("[POW] Received reactor event; breaking out.")
+ return nil
+ default:
+ i++
+ if i%1234567 == 0 {
+ elapsed := time.Now().UnixNano() - start
+ hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
+ log.Println("[POW] Hashing @", int64(hashes), "khash")
+ }
+
+ sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
+ if pow.Verify(hash, diff, sha) {
+ return sha
+ }
}
}
@@ -98,9 +113,9 @@ func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
for k := 0; k < amountOfRoutines; k++ {
go dag.Find(obj, resChan)
- }
- // Wait for each go routine to finish
+ // 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 {
diff --git a/ethchain/error.go b/ethchain/error.go
index 0f1d061c0..8d37b0208 100644
--- a/ethchain/error.go
+++ b/ethchain/error.go
@@ -1,6 +1,8 @@
package ethchain
-import "fmt"
+import (
+ "fmt"
+)
// Parent error. In case a parent is unknown this error will be thrown
// by the block manager
@@ -40,3 +42,22 @@ func IsValidationErr(err error) bool {
return ok
}
+
+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
+}
diff --git a/ethchain/keypair.go b/ethchain/keypair.go
index 9fdc95972..0f23bacdf 100644
--- a/ethchain/keypair.go
+++ b/ethchain/keypair.go
@@ -2,6 +2,7 @@ package ethchain
import (
"github.com/ethereum/eth-go/ethutil"
+ "github.com/obscuren/secp256k1-go"
"math/big"
)
@@ -10,10 +11,19 @@ type KeyPair struct {
PublicKey []byte
// The associated account
- account *Account
+ account *StateObject
state *State
}
+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 *ethutil.Value) *KeyPair {
keyPair := &KeyPair{PrivateKey: val.Get(0).Bytes(), PublicKey: val.Get(1).Bytes()}
@@ -24,7 +34,7 @@ func (k *KeyPair) Address() []byte {
return ethutil.Sha3Bin(k.PublicKey[1:])[12:]
}
-func (k *KeyPair) Account() *Account {
+func (k *KeyPair) Account() *StateObject {
if k.account == nil {
k.account = k.state.GetAccount(k.Address())
}
@@ -34,6 +44,7 @@ func (k *KeyPair) Account() *Account {
// Create transaction, creates a new and signed transaction, ready for processing
func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Transaction {
+ /* TODO
tx := NewTransaction(receiver, value, data)
tx.Nonce = k.account.Nonce
@@ -41,6 +52,8 @@ func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Tran
tx.Sign(k.PrivateKey)
return tx
+ */
+ return nil
}
func (k *KeyPair) RlpEncode() []byte {
diff --git a/ethchain/stack.go b/ethchain/stack.go
index 3c2899e62..e9297b324 100644
--- a/ethchain/stack.go
+++ b/ethchain/stack.go
@@ -6,152 +6,6 @@ import (
"math/big"
)
-type OpCode int
-
-// Op codes
-const (
- // 0x0 range - arithmetic ops
- oSTOP = 0x00
- oADD = 0x01
- oMUL = 0x02
- oSUB = 0x03
- oDIV = 0x04
- oSDIV = 0x05
- oMOD = 0x06
- oSMOD = 0x07
- oEXP = 0x08
- oNEG = 0x09
- oLT = 0x0a
- oGT = 0x0b
- oEQ = 0x0c
- oNOT = 0x0d
-
- // 0x10 range - bit ops
- oAND = 0x10
- oOR = 0x11
- oXOR = 0x12
- oBYTE = 0x13
-
- // 0x20 range - crypto
- oSHA3 = 0x20
-
- // 0x30 range - closure state
- oADDRESS = 0x30
- oBALANCE = 0x31
- oORIGIN = 0x32
- oCALLER = 0x33
- oCALLVALUE = 0x34
- oCALLDATA = 0x35
- oCALLDATASIZE = 0x36
- oGASPRICE = 0x37
-
- // 0x40 range - block operations
- oPREVHASH = 0x40
- oCOINBASE = 0x41
- oTIMESTAMP = 0x42
- oNUMBER = 0x43
- oDIFFICULTY = 0x44
- oGASLIMIT = 0x45
-
- // 0x50 range - 'storage' and execution
- oPUSH = 0x50
- oPOP = 0x51
- oDUP = 0x52
- oSWAP = 0x53
- oMLOAD = 0x54
- oMSTORE = 0x55
- oMSTORE8 = 0x56
- oSLOAD = 0x57
- oSSTORE = 0x58
- oJUMP = 0x59
- oJUMPI = 0x5a
- oPC = 0x5b
- oMSIZE = 0x5c
-
- // 0x60 range - closures
- oCREATE = 0x60
- oCALL = 0x61
- oRETURN = 0x62
-
- // 0x70 range - other
- oLOG = 0x70 // XXX Unofficial
- oSUICIDE = 0x7f
-)
-
-// Since the opcodes aren't all in order we can't use a regular slice
-var opCodeToString = map[OpCode]string{
- // 0x0 range - arithmetic ops
- oSTOP: "STOP",
- oADD: "ADD",
- oMUL: "MUL",
- oSUB: "SUB",
- oDIV: "DIV",
- oSDIV: "SDIV",
- oMOD: "MOD",
- oSMOD: "SMOD",
- oEXP: "EXP",
- oNEG: "NEG",
- oLT: "LT",
- oGT: "GT",
- oEQ: "EQ",
- oNOT: "NOT",
-
- // 0x10 range - bit ops
- oAND: "AND",
- oOR: "OR",
- oXOR: "XOR",
- oBYTE: "BYTE",
-
- // 0x20 range - crypto
- oSHA3: "SHA3",
-
- // 0x30 range - closure state
- oADDRESS: "ADDRESS",
- oBALANCE: "BALANCE",
- oORIGIN: "ORIGIN",
- oCALLER: "CALLER",
- oCALLVALUE: "CALLVALUE",
- oCALLDATA: "CALLDATA",
- oCALLDATASIZE: "CALLDATASIZE",
- oGASPRICE: "TXGASPRICE",
-
- // 0x40 range - block operations
- oPREVHASH: "PREVHASH",
- oCOINBASE: "COINBASE",
- oTIMESTAMP: "TIMESTAMP",
- oNUMBER: "NUMBER",
- oDIFFICULTY: "DIFFICULTY",
- oGASLIMIT: "GASLIMIT",
-
- // 0x50 range - 'storage' and execution
- oPUSH: "PUSH",
- oPOP: "POP",
- oDUP: "DUP",
- oSWAP: "SWAP",
- oMLOAD: "MLOAD",
- oMSTORE: "MSTORE",
- oMSTORE8: "MSTORE8",
- oSLOAD: "SLOAD",
- oSSTORE: "SSTORE",
- oJUMP: "JUMP",
- oJUMPI: "JUMPI",
- oPC: "PC",
- oMSIZE: "MSIZE",
-
- // 0x60 range - closures
- oCREATE: "CREATE",
- oCALL: "CALL",
- oRETURN: "RETURN",
-
- // 0x70 range - other
- oLOG: "LOG",
- oSUICIDE: "SUICIDE",
-}
-
-func (o OpCode) String() string {
- return opCodeToString[o]
-}
-
type OpType int
const (
@@ -172,22 +26,34 @@ 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[0]
- st.data = st.data[1:]
+ 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[:2]
- st.data = st.data[2:]
+ 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[0]
+ str := st.data[len(st.data)-1]
return str
}
@@ -201,8 +67,20 @@ func (st *Stack) Peekn() (*big.Int, *big.Int) {
func (st *Stack) Push(d *big.Int) {
st.data = append(st.data, 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 ###")
+ fmt.Println("### stack ###")
if len(st.data) > 0 {
for i, val := range st.data {
fmt.Printf("%-3d %v\n", i, val)
@@ -241,16 +119,20 @@ func (m *Memory) Len() int {
return len(m.store)
}
+func (m *Memory) Data() []byte {
+ return m.store
+}
+
func (m *Memory) Print() {
- fmt.Println("### MEM ###")
+ 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 %v\n", addr, m.store[i:i+32])
+ 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("###########")
+ fmt.Println("####################")
}
diff --git a/ethchain/state.go b/ethchain/state.go
index 1860647f2..1b5655d4c 100644
--- a/ethchain/state.go
+++ b/ethchain/state.go
@@ -34,12 +34,12 @@ func (s *State) Reset() {
// Syncs the trie and all siblings
func (s *State) Sync() {
- s.trie.Sync()
-
// Sync all nested states
for _, state := range s.states {
state.Sync()
}
+
+ s.trie.Sync()
}
// Purges the current trie.
@@ -47,23 +47,15 @@ func (s *State) Purge() int {
return s.trie.NewIterator().Purge()
}
-func (s *State) GetContract(addr []byte) *Contract {
+// XXX Deprecated
+func (s *State) GetContract(addr []byte) *StateObject {
data := s.trie.Get(string(addr))
if data == "" {
return nil
}
- // Whet get contract is called the retrieved value might
- // be an account. The StateManager uses this to check
- // to see if the address a tx was sent to is a contract
- // or an account
- value := ethutil.NewValueFromBytes([]byte(data))
- if value.Len() == 2 {
- return nil
- }
-
// build contract
- contract := NewContractFromBytes(addr, []byte(data))
+ contract := NewStateObjectFromBytes(addr, []byte(data))
// Check if there's a cached state for this contract
cachedState := s.states[string(addr)]
@@ -77,28 +69,43 @@ func (s *State) GetContract(addr []byte) *Contract {
return contract
}
-func (s *State) UpdateContract(contract *Contract) {
- addr := contract.Address()
+func (s *State) GetStateObject(addr []byte) *StateObject {
+ data := s.trie.Get(string(addr))
+ if data == "" {
+ return nil
+ }
+
+ stateObject := NewStateObjectFromBytes(addr, []byte(data))
+
+ // Check if there's a cached state for this contract
+ cachedStateObject := s.states[string(addr)]
+ if cachedStateObject != nil {
+ stateObject.state = cachedStateObject
+ } else {
+ // If it isn't cached, cache the state
+ s.states[string(addr)] = stateObject.state
+ }
+
+ return stateObject
+}
+
+func (s *State) SetStateObject(stateObject *StateObject) {
+ s.states[string(stateObject.address)] = stateObject.state
- s.states[string(addr)] = contract.state
- s.trie.Update(string(addr), string(contract.RlpEncode()))
+ s.UpdateStateObject(stateObject)
}
-func (s *State) GetAccount(addr []byte) (account *Account) {
+func (s *State) GetAccount(addr []byte) (account *StateObject) {
data := s.trie.Get(string(addr))
if data == "" {
account = NewAccount(addr, big.NewInt(0))
} else {
- account = NewAccountFromData(addr, []byte(data))
+ account = NewStateObjectFromBytes(addr, []byte(data))
}
return
}
-func (s *State) UpdateAccount(addr []byte, account *Account) {
- s.trie.Update(string(addr), string(account.RlpEncode()))
-}
-
func (s *State) Cmp(other *State) bool {
return s.trie.Cmp(other.trie)
}
@@ -117,9 +124,10 @@ const (
UnknownTy
)
+/*
// Returns the object stored at key and the type stored at key
// Returns nil if nothing is stored
-func (s *State) Get(key []byte) (*ethutil.Value, ObjType) {
+func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) {
// Fetch data from the trie
data := s.trie.Get(string(key))
// Returns the nil type, indicating nothing could be retrieved.
@@ -144,35 +152,23 @@ func (s *State) Get(key []byte) (*ethutil.Value, ObjType) {
return val, typ
}
+*/
-func (s *State) Put(key, object []byte) {
- s.trie.Update(string(key), string(object))
-}
-
-func (s *State) Root() interface{} {
- return s.trie.Root
-}
-
-// Script compilation functions
-// Compiles strings to machine code
-func Compile(code []string) (script []string) {
- script = make([]string, len(code))
- for i, val := range code {
- instr, _ := ethutil.CompileInstr(val)
+// Updates any given state object
+func (s *State) UpdateStateObject(object *StateObject) {
+ addr := object.Address()
- script[i] = string(instr)
+ if object.state != nil {
+ s.states[string(addr)] = object.state
}
- return
+ s.trie.Update(string(addr), string(object.RlpEncode()))
}
-func CompileToValues(code []string) (script []*ethutil.Value) {
- script = make([]*ethutil.Value, len(code))
- for i, val := range code {
- instr, _ := ethutil.CompileInstr(val)
-
- script[i] = ethutil.NewValue(instr)
- }
+func (s *State) Put(key, object []byte) {
+ s.trie.Update(string(key), string(object))
+}
- return
+func (s *State) Root() interface{} {
+ return s.trie.Root
}
diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go
index 3b5507740..501ec102b 100644
--- a/ethchain/state_manager.go
+++ b/ethchain/state_manager.go
@@ -19,6 +19,7 @@ type EthManager interface {
BlockChain() *BlockChain
TxPool() *TxPool
Broadcast(msgType ethwire.MsgType, data []interface{})
+ Reactor() *ethutil.ReactorEngine
}
type StateManager struct {
@@ -29,7 +30,7 @@ type StateManager struct {
bc *BlockChain
// States for addresses. You can watch any address
// at any given time
- addrStateStore *AddrStateStore
+ stateObjectCache *StateObjectCache
// Stack for processing contracts
stack *Stack
@@ -50,20 +51,20 @@ type StateManager struct {
// results
compState *State
- miningState *State
+ manifest *Manifest
}
func NewStateManager(ethereum EthManager) *StateManager {
sm := &StateManager{
- stack: NewStack(),
- mem: make(map[string]*big.Int),
- Pow: &EasyPow{},
- Ethereum: ethereum,
- addrStateStore: NewAddrStateStore(),
- bc: ethereum.BlockChain(),
+ stack: NewStack(),
+ mem: make(map[string]*big.Int),
+ Pow: &EasyPow{},
+ Ethereum: ethereum,
+ stateObjectCache: NewStateObjectCache(),
+ bc: ethereum.BlockChain(),
+ manifest: NewManifest(),
}
sm.procState = ethereum.BlockChain().CurrentBlock.State()
-
return sm
}
@@ -72,18 +73,18 @@ func (sm *StateManager) ProcState() *State {
}
// Watches any given address and puts it in the address state store
-func (sm *StateManager) WatchAddr(addr []byte) *AccountState {
+func (sm *StateManager) WatchAddr(addr []byte) *CachedStateObject {
//XXX account := sm.bc.CurrentBlock.state.GetAccount(addr)
account := sm.procState.GetAccount(addr)
- return sm.addrStateStore.Add(addr, account)
+ return sm.stateObjectCache.Add(addr, account)
}
-func (sm *StateManager) GetAddrState(addr []byte) *AccountState {
- account := sm.addrStateStore.Get(addr)
+func (sm *StateManager) GetAddrState(addr []byte) *CachedStateObject {
+ account := sm.stateObjectCache.Get(addr)
if account == nil {
- a := sm.bc.CurrentBlock.state.GetAccount(addr)
- account = &AccountState{Nonce: a.Nonce, Account: a}
+ a := sm.procState.GetAccount(addr)
+ account = &CachedStateObject{Nonce: a.Nonce, Object: a}
}
return account
@@ -93,29 +94,44 @@ func (sm *StateManager) BlockChain() *BlockChain {
return sm.bc
}
-func (sm *StateManager) MakeContract(tx *Transaction) {
+func (sm *StateManager) MakeContract(tx *Transaction) *StateObject {
contract := MakeContract(tx, sm.procState)
if contract != nil {
sm.procState.states[string(tx.Hash()[12:])] = contract.state
+
+ return contract
}
+
+ return nil
}
+// Apply transactions uses the transaction passed to it and applies them onto
+// the current processing state.
func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) {
// Process each transaction/contract
for _, tx := range txs {
// If there's no recipient, it's a contract
+ // Check if this is a contract creation traction and if so
+ // create a contract of this tx.
if tx.IsContract() {
- sm.MakeContract(tx)
- //XXX block.MakeContract(tx)
- } else {
- if contract := sm.procState.GetContract(tx.Recipient); contract != nil {
- //XXX if contract := block.state.GetContract(tx.Recipient); contract != nil {
- sm.ProcessContract(contract, tx, block)
- } else {
- err := sm.Ethereum.TxPool().ProcessTransaction(tx, block)
- if err != nil {
- ethutil.Config.Log.Infoln("[STATE]", err)
+ err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false)
+ if err == nil {
+ contract := sm.MakeContract(tx)
+ if contract != nil {
+ sm.EvalScript(contract.Init(), contract, tx, block)
+ } else {
+ ethutil.Config.Log.Infoln("[STATE] Unable to create contract")
}
+ } else {
+ ethutil.Config.Log.Infoln("[STATE] contract create:", err)
+ }
+ } else {
+ err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false)
+ contract := sm.procState.GetContract(tx.Recipient)
+ if err == nil && len(contract.Script()) > 0 {
+ sm.EvalScript(contract.Script(), contract, tx, block)
+ } else if err != nil {
+ ethutil.Config.Log.Infoln("[STATE] process:", err)
}
}
}
@@ -123,9 +139,9 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) {
// The prepare function, prepares the state manager for the next
// "ProcessBlock" action.
-func (sm *StateManager) Prepare(processer *State, comparative *State) {
+func (sm *StateManager) Prepare(processor *State, comparative *State) {
sm.compState = comparative
- sm.procState = processer
+ sm.procState = processor
}
// Default prepare function
@@ -134,22 +150,23 @@ func (sm *StateManager) PrepareDefault(block *Block) {
}
// Block processing and validating with a given (temporarily) state
-func (sm *StateManager) ProcessBlock(block *Block) error {
+func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error {
// Processing a blocks may never happen simultaneously
sm.mutex.Lock()
defer sm.mutex.Unlock()
- // Defer the Undo on the Trie. If the block processing happened
- // we don't want to undo but since undo only happens on dirty
- // nodes this won't happen because Commit would have been called
- // before that.
- defer sm.bc.CurrentBlock.Undo()
-
hash := block.Hash()
if sm.bc.HasBlock(hash) {
+ //fmt.Println("[STATE] We already have this block, ignoring")
return nil
}
+ // 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 sm.bc.CurrentBlock.Undo()
+
// Check if we have the parent hash, if it isn't known we discard it
// Reasons might be catching up or simply an invalid block
if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil {
@@ -161,30 +178,26 @@ func (sm *StateManager) ProcessBlock(block *Block) error {
// Block validation
if err := sm.ValidateBlock(block); err != nil {
+ fmt.Println("[SM] 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(block); err != nil {
+ fmt.Println("[SM] Error accumulating reward", err)
return err
}
- // if !sm.compState.Cmp(sm.procState)
if !sm.compState.Cmp(sm.procState) {
- //XXX return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().trie.Root, sm.bc.CurrentBlock.State().trie.Root)
return fmt.Errorf("Invalid merkle root. Expected %x, got %x", sm.compState.trie.Root, sm.procState.trie.Root)
}
// 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
- //XXX sm.bc.CurrentBlock.Sync()
sm.procState.Sync()
- // Broadcast the valid block back to the wire
- //sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
-
// Add the block to the chain
sm.bc.Add(block)
@@ -195,13 +208,19 @@ func (sm *StateManager) ProcessBlock(block *Block) error {
}
ethutil.Config.Log.Infof("[STATE] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
+ if dontReact == false {
+ sm.Ethereum.Reactor().Post("newBlock", block)
+
+ sm.notifyChanges()
+
+ sm.manifest.Reset()
+ }
} else {
fmt.Println("total diff failed")
}
return nil
}
-
func (sm *StateManager) CalculateTD(block *Block) bool {
uncleDiff := new(big.Int)
for _, uncle := range block.Uncles {
@@ -272,21 +291,20 @@ func CalculateUncleReward(block *Block) *big.Int {
}
func (sm *StateManager) AccumelateRewards(block *Block) error {
- // Get the coinbase rlp data
- //XXX addr := processor.state.GetAccount(block.Coinbase)
- addr := sm.procState.GetAccount(block.Coinbase)
+ // Get the account associated with the coinbase
+ account := sm.procState.GetAccount(block.Coinbase)
// Reward amount of ether to the coinbase address
- addr.AddFee(CalculateBlockReward(block, len(block.Uncles)))
+ account.AddAmount(CalculateBlockReward(block, len(block.Uncles)))
- //XXX processor.state.UpdateAccount(block.Coinbase, addr)
- sm.procState.UpdateAccount(block.Coinbase, addr)
+ addr := make([]byte, len(block.Coinbase))
+ copy(addr, block.Coinbase)
+ sm.procState.UpdateStateObject(account)
for _, uncle := range block.Uncles {
- uncleAddr := sm.procState.GetAccount(uncle.Coinbase)
- uncleAddr.AddFee(CalculateUncleReward(uncle))
+ uncleAccount := sm.procState.GetAccount(uncle.Coinbase)
+ uncleAccount.AddAmount(CalculateUncleReward(uncle))
- //processor.state.UpdateAccount(uncle.Coinbase, uncleAddr)
- sm.procState.UpdateAccount(uncle.Coinbase, uncleAddr)
+ sm.procState.UpdateStateObject(uncleAccount)
}
return nil
@@ -296,26 +314,76 @@ func (sm *StateManager) Stop() {
sm.bc.Stop()
}
-func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) {
- // Recovering function in case the VM had any errors
- /*
- defer func() {
- if r := recover(); r != nil {
- fmt.Println("Recovered from VM execution with err =", r)
- }
- }()
- */
- caller := sm.procState.GetAccount(tx.Sender())
- closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value)
- vm := NewVm(sm.procState, RuntimeVars{
- origin: caller.Address(),
- blockNumber: block.BlockInfo().Number,
- prevHash: block.PrevHash,
- coinbase: block.Coinbase,
- time: block.Time,
- diff: block.Difficulty,
- // XXX Tx data? Could be just an argument to the closure instead
- txData: nil,
+func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) {
+ account := sm.procState.GetAccount(tx.Sender())
+
+ err := account.ConvertGas(tx.Gas, tx.GasPrice)
+ if err != nil {
+ ethutil.Config.Log.Debugln(err)
+ return
+ }
+
+ closure := NewClosure(account, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value)
+ vm := NewVm(sm.procState, sm, RuntimeVars{
+ Origin: account.Address(),
+ BlockNumber: block.BlockInfo().Number,
+ PrevHash: block.PrevHash,
+ Coinbase: block.Coinbase,
+ Time: block.Time,
+ Diff: block.Difficulty,
+ //Price: tx.GasPrice,
})
- closure.Call(vm, nil)
+ closure.Call(vm, tx.Data, nil)
+
+ // Update the account (refunds)
+ sm.procState.UpdateStateObject(account)
+ sm.manifest.AddObjectChange(account)
+
+ sm.procState.UpdateStateObject(object)
+ sm.manifest.AddObjectChange(object)
+}
+
+func (sm *StateManager) notifyChanges() {
+ for addr, stateObject := range sm.manifest.objectChanges {
+ sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
+ }
+
+ for stateObjectAddr, mappedObjects := range sm.manifest.storageChanges {
+ for addr, value := range mappedObjects {
+ sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &StorageState{[]byte(stateObjectAddr), []byte(addr), value})
+ }
+ }
+}
+
+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_object.go b/ethchain/state_object.go
new file mode 100644
index 000000000..617646077
--- /dev/null
+++ b/ethchain/state_object.go
@@ -0,0 +1,194 @@
+package ethchain
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+type StateObject struct {
+ // Address of the object
+ address []byte
+ // Shared attributes
+ Amount *big.Int
+ Nonce uint64
+ // Contract related attributes
+ state *State
+ script []byte
+ initScript []byte
+}
+
+// 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() {
+ // FIXME
+ addr := tx.Hash()[12:]
+
+ value := tx.Value
+ contract := NewContract(addr, value, []byte(""))
+ state.UpdateStateObject(contract)
+
+ contract.script = tx.Data
+ contract.initScript = tx.Init
+
+ state.UpdateStateObject(contract)
+
+ return contract
+ }
+
+ return nil
+}
+
+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) SetMem(num *big.Int, val *ethutil.Value) {
+ addr := ethutil.BigToBytes(num, 256)
+ c.SetAddr(addr, val)
+}
+
+func (c *StateObject) GetMem(num *big.Int) *ethutil.Value {
+ nb := ethutil.BigToBytes(num, 256)
+
+ return c.Addr(nb)
+}
+
+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))
+}
+
+func (c *StateObject) SubAmount(amount *big.Int) {
+ c.SetAmount(new(big.Int).Sub(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
+}
+
+// Returns the address of the contract/account
+func (c *StateObject) Address() []byte {
+ return c.address
+}
+
+// Returns the main script body
+func (c *StateObject) Script() []byte {
+ return c.script
+}
+
+// Returns the initialization script
+func (c *StateObject) Init() []byte {
+ 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 = nil
+ }
+ return ethutil.Encode([]interface{}{c.Amount, c.Nonce, root, c.script})
+}
+
+func (c *StateObject) RlpDecode(data []byte) {
+ decoder := ethutil.NewValueFromBytes(data)
+
+ c.Amount = decoder.Get(0).BigInt()
+ c.Nonce = decoder.Get(1).Uint()
+ c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
+ c.script = decoder.Get(3).Bytes()
+}
+
+// The cached state and state object cache are helpers which will give you somewhat
+// control over the nonce. When creating new transactions you're interested in the 'next'
+// nonce rather than the current nonce. This to avoid creating invalid-nonce transactions.
+type StateObjectCache struct {
+ cachedObjects map[string]*CachedStateObject
+}
+
+func NewStateObjectCache() *StateObjectCache {
+ return &StateObjectCache{cachedObjects: make(map[string]*CachedStateObject)}
+}
+
+func (s *StateObjectCache) Add(addr []byte, object *StateObject) *CachedStateObject {
+ state := &CachedStateObject{Nonce: object.Nonce, Object: object}
+ s.cachedObjects[string(addr)] = state
+
+ return state
+}
+
+func (s *StateObjectCache) Get(addr []byte) *CachedStateObject {
+ return s.cachedObjects[string(addr)]
+}
+
+type CachedStateObject struct {
+ Nonce uint64
+ Object *StateObject
+}
+
+type StorageState struct {
+ StateAddress []byte
+ Address []byte
+ Value *big.Int
+}
diff --git a/ethchain/transaction.go b/ethchain/transaction.go
index 3b07c81d4..e93e610be 100644
--- a/ethchain/transaction.go
+++ b/ethchain/transaction.go
@@ -1,7 +1,6 @@
package ethchain
import (
- "bytes"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
"math/big"
@@ -14,33 +13,22 @@ type Transaction struct {
Recipient []byte
Value *big.Int
Gas *big.Int
- Gasprice *big.Int
- Data []string
+ GasPrice *big.Int
+ Data []byte
+ Init []byte
v byte
r, s []byte
-}
-
-func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
- tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data}
-
- return &tx
-}
-func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction {
- return &Transaction{Value: value, Gasprice: gasprice, Data: data}
+ // Indicates whether this tx is a contract creation transaction
+ contractCreation bool
}
-func NewContractMessageTx(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction {
- return &Transaction{Recipient: to, Value: value, Gasprice: gasprice, Gas: gas, Data: data}
+func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte, init []byte) *Transaction {
+ return &Transaction{Value: value, Gas: gas, GasPrice: gasPrice, Data: script, Init: init, contractCreation: true}
}
-func NewTx(to []byte, value *big.Int, data []string) *Transaction {
- return &Transaction{Recipient: to, Value: value, Gasprice: big.NewInt(0), Gas: big.NewInt(0), Nonce: 0, Data: data}
-}
-
-// XXX Deprecated
-func NewTransactionFromData(data []byte) *Transaction {
- return NewTransactionFromBytes(data)
+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 {
@@ -58,23 +46,21 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
}
func (tx *Transaction) Hash() []byte {
- data := make([]interface{}, len(tx.Data))
- for i, val := range tx.Data {
- data[i] = val
- }
+ data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data}
- preEnc := []interface{}{
- tx.Nonce,
- tx.Recipient,
- tx.Value,
- data,
+ if tx.contractCreation {
+ data = append(data, tx.Init)
}
- return ethutil.Sha3Bin(ethutil.Encode(preEnc))
+ return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
}
func (tx *Transaction) IsContract() bool {
- return bytes.Compare(tx.Recipient, ContractAddr) == 0
+ return tx.contractCreation
+}
+
+func (tx *Transaction) CreationAddress() []byte {
+ return tx.Hash()[12:]
}
func (tx *Transaction) Signature(key []byte) []byte {
@@ -123,17 +109,16 @@ func (tx *Transaction) Sign(privk []byte) error {
return nil
}
+// [ NONCE, VALUE, GASPRICE, GAS, TO, DATA, V, R, S ]
+// [ NONCE, VALUE, GASPRICE, GAS, 0, CODE, INIT, V, R, S ]
func (tx *Transaction) RlpData() interface{} {
- // Prepare the transaction for serialization
- return []interface{}{
- tx.Nonce,
- tx.Recipient,
- tx.Value,
- ethutil.NewSliceValue(tx.Data).Slice(),
- tx.v,
- tx.r,
- tx.s,
+ data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data}
+
+ if tx.contractCreation {
+ data = append(data, tx.Init)
}
+
+ return append(data, tx.v, tx.r, tx.s)
}
func (tx *Transaction) RlpValue() *ethutil.Value {
@@ -150,17 +135,23 @@ func (tx *Transaction) RlpDecode(data []byte) {
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.Nonce = decoder.Get(0).Uint()
- tx.Recipient = decoder.Get(1).Bytes()
- tx.Value = decoder.Get(2).BigInt()
-
- d := decoder.Get(3)
- tx.Data = make([]string, d.Len())
- for i := 0; i < d.Len(); i++ {
- tx.Data[i] = d.Get(i).Str()
+ tx.Value = decoder.Get(1).BigInt()
+ tx.GasPrice = decoder.Get(2).BigInt()
+ tx.Gas = decoder.Get(3).BigInt()
+ tx.Recipient = decoder.Get(4).Bytes()
+ tx.Data = decoder.Get(5).Bytes()
+
+ // If the list is of length 10 it's a contract creation tx
+ if decoder.Len() == 10 {
+ tx.contractCreation = true
+ tx.Init = decoder.Get(6).Bytes()
+
+ tx.v = byte(decoder.Get(7).Uint())
+ tx.r = decoder.Get(8).Bytes()
+ tx.s = decoder.Get(9).Bytes()
+ } else {
+ tx.v = byte(decoder.Get(6).Uint())
+ tx.r = decoder.Get(7).Bytes()
+ tx.s = decoder.Get(8).Bytes()
}
-
- // TODO something going wrong here
- tx.v = byte(decoder.Get(4).Uint())
- tx.r = decoder.Get(5).Bytes()
- tx.s = decoder.Get(6).Bytes()
}
diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go
index fdc386303..72836d6cb 100644
--- a/ethchain/transaction_pool.go
+++ b/ethchain/transaction_pool.go
@@ -90,7 +90,7 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
// Process transaction validates the Tx and processes funds from the
// sender to the recipient.
-func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
+func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract bool) (err error) {
defer func() {
if r := recover(); r != nil {
log.Println(r)
@@ -100,19 +100,15 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
// Get the sender
sender := block.state.GetAccount(tx.Sender())
+ if sender.Nonce != tx.Nonce {
+ return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transaction nonce is %d instead", sender.Nonce, tx.Nonce)
+ }
+
// 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, new(big.Int).Mul(TxFee, TxFeeRat))
if sender.Amount.Cmp(totAmount) < 0 {
- return errors.New("Insufficient amount in sender's account")
- }
-
- if sender.Nonce != tx.Nonce {
- if ethutil.Config.Debug {
- return fmt.Errorf("Invalid nonce %d(%d) continueing anyway", tx.Nonce, sender.Nonce)
- } else {
- return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce)
- }
+ return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
}
// Get the receiver
@@ -122,22 +118,21 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
// Send Tx to self
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
// Subtract the fee
- sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat))
+ sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat))
} else {
// Subtract the amount from the senders account
- sender.Amount.Sub(sender.Amount, totAmount)
+ sender.SubAmount(totAmount)
// Add the amount to receivers account which should conclude this transaction
- receiver.Amount.Add(receiver.Amount, tx.Value)
+ receiver.AddAmount(tx.Value)
- block.state.UpdateAccount(tx.Recipient, receiver)
+ block.state.UpdateStateObject(receiver)
}
- block.state.UpdateAccount(tx.Sender(), sender)
+ block.state.UpdateStateObject(sender)
log.Printf("[TXPL] Processed Tx %x\n", tx.Hash())
- // Notify the subscribers
pool.notifySubscribers(TxPost, tx)
return
@@ -149,18 +144,18 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
block := pool.Ethereum.BlockChain().CurrentBlock
// Something has gone horribly wrong if this happens
if block == nil {
- return errors.New("No last block on the block chain")
+ return errors.New("[TXPL] No last block on the block chain")
}
// Get the sender
accountState := pool.Ethereum.StateManager().GetAddrState(tx.Sender())
- sender := accountState.Account
+ sender := accountState.Object
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(totAmount) < 0 {
- return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
+ return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
}
// Increment the nonce making each tx valid only once to prevent replay
@@ -190,11 +185,13 @@ out:
log.Println("Validating Tx failed", err)
}
} else {
- // Call blocking version. At this point it
- // doesn't matter since this is a goroutine
+ // Call blocking version.
pool.addTransaction(tx)
// Notify the subscribers
+ pool.Ethereum.Reactor().Post("newTx", tx)
+
+ // Notify the subscribers
pool.notifySubscribers(TxPre, tx)
}
case <-pool.quit:
@@ -207,7 +204,7 @@ func (pool *TxPool) QueueTransaction(tx *Transaction) {
pool.queueChan <- tx
}
-func (pool *TxPool) Flush() []*Transaction {
+func (pool *TxPool) CurrentTransactions() []*Transaction {
pool.mutex.Lock()
defer pool.mutex.Unlock()
@@ -221,6 +218,12 @@ func (pool *TxPool) Flush() []*Transaction {
i++
}
+ return txList
+}
+
+func (pool *TxPool) Flush() []*Transaction {
+ txList := pool.CurrentTransactions()
+
// Recreate a new list all together
// XXX Is this the fastest way?
pool.pool = list.New()
diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go
index a49768aea..3603fd8a7 100644
--- a/ethchain/transaction_test.go
+++ b/ethchain/transaction_test.go
@@ -1,54 +1 @@
package ethchain
-
-import (
- "encoding/hex"
- "math/big"
- "testing"
-)
-
-func TestAddressRetrieval(t *testing.T) {
- // TODO
- // 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
- key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
-
- tx := &Transaction{
- Nonce: 0,
- Recipient: ZeroHash160,
- Value: big.NewInt(0),
- Data: nil,
- }
- //fmt.Printf("rlp %x\n", tx.RlpEncode())
- //fmt.Printf("sha rlp %x\n", tx.Hash())
-
- tx.Sign(key)
-
- //fmt.Printf("hex tx key %x\n", tx.PublicKey())
- //fmt.Printf("seder %x\n", tx.Sender())
-}
-
-func TestAddressRetrieval2(t *testing.T) {
- // TODO
- // 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
- key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
- addr, _ := hex.DecodeString("944400f4b88ac9589a0f17ed4671da26bddb668b")
- tx := &Transaction{
- Nonce: 0,
- Recipient: addr,
- Value: big.NewInt(1000),
- Data: nil,
- }
- tx.Sign(key)
- //data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
- //tx := NewTransactionFromData(data)
- /*
- fmt.Println(tx.RlpValue())
-
- fmt.Printf("rlp %x\n", tx.RlpEncode())
- fmt.Printf("sha rlp %x\n", tx.Hash())
-
- //tx.Sign(key)
-
- fmt.Printf("hex tx key %x\n", tx.PublicKey())
- fmt.Printf("seder %x\n", tx.Sender())
- */
-}
diff --git a/ethchain/types.go b/ethchain/types.go
new file mode 100644
index 000000000..827d4f27f
--- /dev/null
+++ b/ethchain/types.go
@@ -0,0 +1,230 @@
+package ethchain
+
+type OpCode int
+
+// Op codes
+const (
+ // 0x0 range - arithmetic ops
+ oSTOP = 0x00
+ oADD = 0x01
+ oMUL = 0x02
+ oSUB = 0x03
+ oDIV = 0x04
+ oSDIV = 0x05
+ oMOD = 0x06
+ oSMOD = 0x07
+ oEXP = 0x08
+ oNEG = 0x09
+ oLT = 0x0a
+ oGT = 0x0b
+ oEQ = 0x0c
+ oNOT = 0x0d
+
+ // 0x10 range - bit ops
+ oAND = 0x10
+ oOR = 0x11
+ oXOR = 0x12
+ oBYTE = 0x13
+
+ // 0x20 range - crypto
+ oSHA3 = 0x20
+
+ // 0x30 range - closure state
+ oADDRESS = 0x30
+ oBALANCE = 0x31
+ oORIGIN = 0x32
+ oCALLER = 0x33
+ oCALLVALUE = 0x34
+ oCALLDATALOAD = 0x35
+ oCALLDATASIZE = 0x36
+ oGASPRICE = 0x37
+
+ // 0x40 range - block operations
+ oPREVHASH = 0x40
+ oCOINBASE = 0x41
+ oTIMESTAMP = 0x42
+ oNUMBER = 0x43
+ oDIFFICULTY = 0x44
+ oGASLIMIT = 0x45
+
+ // 0x50 range - 'storage' and execution
+ oPUSH = 0x50
+ oPUSH20 = 0x80
+ oPOP = 0x51
+ oDUP = 0x52
+ oSWAP = 0x53
+ oMLOAD = 0x54
+ oMSTORE = 0x55
+ oMSTORE8 = 0x56
+ oSLOAD = 0x57
+ oSSTORE = 0x58
+ oJUMP = 0x59
+ oJUMPI = 0x5a
+ oPC = 0x5b
+ oMSIZE = 0x5c
+
+ // 0x60 range - closures
+ oCREATE = 0x60
+ oCALL = 0x61
+ oRETURN = 0x62
+
+ // 0x70 range - other
+ oLOG = 0x70 // XXX Unofficial
+ oSUICIDE = 0x7f
+)
+
+// Since the opcodes aren't all in order we can't use a regular slice
+var opCodeToString = map[OpCode]string{
+ // 0x0 range - arithmetic ops
+ oSTOP: "STOP",
+ oADD: "ADD",
+ oMUL: "MUL",
+ oSUB: "SUB",
+ oDIV: "DIV",
+ oSDIV: "SDIV",
+ oMOD: "MOD",
+ oSMOD: "SMOD",
+ oEXP: "EXP",
+ oNEG: "NEG",
+ oLT: "LT",
+ oGT: "GT",
+ oEQ: "EQ",
+ oNOT: "NOT",
+
+ // 0x10 range - bit ops
+ oAND: "AND",
+ oOR: "OR",
+ oXOR: "XOR",
+ oBYTE: "BYTE",
+
+ // 0x20 range - crypto
+ oSHA3: "SHA3",
+
+ // 0x30 range - closure state
+ oADDRESS: "ADDRESS",
+ oBALANCE: "BALANCE",
+ oORIGIN: "ORIGIN",
+ oCALLER: "CALLER",
+ oCALLVALUE: "CALLVALUE",
+ oCALLDATALOAD: "CALLDATALOAD",
+ oCALLDATASIZE: "CALLDATASIZE",
+ oGASPRICE: "TXGASPRICE",
+
+ // 0x40 range - block operations
+ oPREVHASH: "PREVHASH",
+ oCOINBASE: "COINBASE",
+ oTIMESTAMP: "TIMESTAMP",
+ oNUMBER: "NUMBER",
+ oDIFFICULTY: "DIFFICULTY",
+ oGASLIMIT: "GASLIMIT",
+
+ // 0x50 range - 'storage' and execution
+ oPUSH: "PUSH",
+ oPOP: "POP",
+ oDUP: "DUP",
+ oSWAP: "SWAP",
+ oMLOAD: "MLOAD",
+ oMSTORE: "MSTORE",
+ oMSTORE8: "MSTORE8",
+ oSLOAD: "SLOAD",
+ oSSTORE: "SSTORE",
+ oJUMP: "JUMP",
+ oJUMPI: "JUMPI",
+ oPC: "PC",
+ oMSIZE: "MSIZE",
+
+ // 0x60 range - closures
+ oCREATE: "CREATE",
+ oCALL: "CALL",
+ oRETURN: "RETURN",
+
+ // 0x70 range - other
+ oLOG: "LOG",
+ oSUICIDE: "SUICIDE",
+}
+
+func (o OpCode) String() string {
+ return opCodeToString[o]
+}
+
+// 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
+ "PUSH": 0x50,
+
+ "PUSH20": 0x80,
+
+ "POP": 0x51,
+ "DUP": 0x52,
+ "SWAP": 0x53,
+ "MLOAD": 0x54,
+ "MSTORE": 0x55,
+ "MSTORE8": 0x56,
+ "SLOAD": 0x57,
+ "SSTORE": 0x58,
+ "JUMP": 0x59,
+ "JUMPI": 0x5a,
+ "PC": 0x5b,
+ "MSIZE": 0x5c,
+
+ // 0x60 range - closures
+ "CREATE": 0x60,
+ "CALL": 0x61,
+ "RETURN": 0x62,
+
+ // 0x70 range - other
+ "LOG": 0x70,
+ "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
index 126592b25..3a3b3447a 100644
--- a/ethchain/vm.go
+++ b/ethchain/vm.go
@@ -2,14 +2,35 @@ package ethchain
import (
_ "bytes"
- _ "fmt"
+ "fmt"
"github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go"
- "log"
_ "math"
"math/big"
)
+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)
+)
+
+func CalculateTxGas(initSize, scriptSize *big.Int) *big.Int {
+ totalGas := new(big.Int)
+ totalGas.Add(totalGas, GasCreate)
+
+ txTotalBytes := new(big.Int).Add(initSize, scriptSize)
+ 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
@@ -20,99 +41,158 @@ type Vm struct {
vars RuntimeVars
state *State
+
+ stateManager *StateManager
}
type RuntimeVars struct {
- origin []byte
- blockNumber uint64
- prevHash []byte
- coinbase []byte
- time int64
- diff *big.Int
- txData []string
+ Origin []byte
+ BlockNumber uint64
+ PrevHash []byte
+ Coinbase []byte
+ Time int64
+ Diff *big.Int
+ TxData []string
}
-func NewVm(state *State, vars RuntimeVars) *Vm {
- return &Vm{vars: vars, state: state}
+func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
+ return &Vm{vars: vars, state: state, stateManager: stateManager}
}
var Pow256 = ethutil.BigPow(2, 256)
-func (vm *Vm) RunClosure(closure *Closure) []byte {
- // If the amount of gas supplied is less equal to 0
- if closure.Gas.Cmp(big.NewInt(0)) <= 0 {
- // TODO Do something
- }
+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 /*&& isRequireError*/ {
+ ret = closure.Return(nil)
+ err = fmt.Errorf("%v", r)
+ fmt.Println("vm err", err)
+ }
+ }()
+
+ ethutil.Config.Log.Debugf("[VM] Running closure %x\n", closure.object.Address())
// Memory for the current closure
mem := &Memory{}
// New stack (should this be shared?)
stack := NewStack()
+ require := func(m int) {
+ if stack.Len() < m {
+ isRequireError = true
+ panic(fmt.Sprintf("stack = %d, req = %d", stack.Len(), m))
+ }
+ }
+
// Instruction pointer
pc := big.NewInt(0)
// Current step count
step := 0
- // The base for all big integer arithmetic
- base := new(big.Int)
if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("# op\n")
}
for {
+ // The base for all big integer arithmetic
+ base := new(big.Int)
+
step++
// Get the memory location of pc
- val := closure.GetMem(pc)
+ val := closure.Get(pc)
// Get the opcode (it must be an opcode!)
op := OpCode(val.Uint())
- if ethutil.Config.Debug {
- ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
+ /*
+ if ethutil.Config.Debug {
+ ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
+ }
+ */
+
+ gas := new(big.Int)
+ useGas := func(amount *big.Int) {
+ gas.Add(gas, amount)
+ }
+
+ switch op {
+ case oSHA3:
+ useGas(GasSha)
+ case oSLOAD:
+ useGas(GasSLoad)
+ case oSSTORE:
+ 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
+ }
+ useGas(new(big.Int).Mul(mult, GasSStore))
+ case oBALANCE:
+ useGas(GasBalance)
+ case oCREATE:
+ require(3)
+
+ args := stack.Get(big.NewInt(3))
+ initSize := new(big.Int).Add(args[1], args[0])
+
+ useGas(CalculateTxGas(initSize, ethutil.Big0))
+ case oCALL:
+ useGas(GasCall)
+ case oMLOAD, oMSIZE, oMSTORE8, oMSTORE:
+ useGas(GasMemory)
+ default:
+ useGas(GasStep)
}
- // TODO Get each instruction cost properly
- fee := new(big.Int)
- fee.Add(fee, big.NewInt(1000))
+ if closure.Gas.Cmp(gas) < 0 {
+ ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas)
- if closure.Gas.Cmp(fee) < 0 {
- return closure.Return(nil)
+ return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas)
}
+ // Sub the amount of gas from the remaining
+ closure.Gas.Sub(closure.Gas, gas)
+
switch op {
case oLOG:
stack.Print()
mem.Print()
- case oSTOP: // Stop the closure
- return closure.Return(nil)
-
- // 0x20 range
+ // 0x20 range
case oADD:
+ require(2)
x, y := stack.Popn()
// (x + y) % 2 ** 256
base.Add(x, y)
- base.Mod(base, Pow256)
// Pop result back on the stack
stack.Push(base)
case oSUB:
+ require(2)
x, y := stack.Popn()
// (x - y) % 2 ** 256
base.Sub(x, y)
- base.Mod(base, Pow256)
// Pop result back on the stack
stack.Push(base)
case oMUL:
+ require(2)
x, y := stack.Popn()
// (x * y) % 2 ** 256
base.Mul(x, y)
- base.Mod(base, Pow256)
// Pop result back on the stack
stack.Push(base)
case oDIV:
+ require(2)
x, y := stack.Popn()
// floor(x / y)
base.Div(x, y)
// Pop result back on the stack
stack.Push(base)
case oSDIV:
+ require(2)
x, y := stack.Popn()
// n > 2**255
if x.Cmp(Pow256) > 0 {
@@ -129,10 +209,12 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// Push result on to the stack
stack.Push(z)
case oMOD:
+ require(2)
x, y := stack.Popn()
base.Mod(x, y)
stack.Push(base)
case oSMOD:
+ require(2)
x, y := stack.Popn()
// n > 2**255
if x.Cmp(Pow256) > 0 {
@@ -149,14 +231,17 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// Push result on to the stack
stack.Push(z)
case oEXP:
+ require(2)
x, y := stack.Popn()
base.Exp(x, y, Pow256)
stack.Push(base)
case oNEG:
+ require(1)
base.Sub(Pow256, stack.Pop())
stack.Push(base)
case oLT:
+ require(2)
x, y := stack.Popn()
// x < y
if x.Cmp(y) < 0 {
@@ -165,6 +250,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
stack.Push(ethutil.BigFalse)
}
case oGT:
+ require(2)
x, y := stack.Popn()
// x > y
if x.Cmp(y) > 0 {
@@ -172,184 +258,286 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
} else {
stack.Push(ethutil.BigFalse)
}
- case oNOT:
+ case oEQ:
+ require(2)
x, y := stack.Popn()
- // x != y
- if x.Cmp(y) != 0 {
+ // x == y
+ if x.Cmp(y) == 0 {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+ case oNOT:
+ require(1)
+ x := stack.Pop()
+ if x.Cmp(ethutil.BigFalse) == 0 {
stack.Push(ethutil.BigTrue)
} else {
stack.Push(ethutil.BigFalse)
}
- // 0x10 range
+ // 0x10 range
case oAND:
+ require(2)
+ x, y := stack.Popn()
+ if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+
case oOR:
+ require(2)
+ x, y := stack.Popn()
+ if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
case oXOR:
+ require(2)
+ x, y := stack.Popn()
+ stack.Push(base.Xor(x, y))
case oBYTE:
+ 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
+ // 0x20 range
case oSHA3:
+ require(2)
+ size, offset := stack.Popn()
+ data := mem.Get(offset.Int64(), size.Int64())
- // 0x30 range
+ stack.Push(ethutil.BigD(data))
+ // 0x30 range
case oADDRESS:
stack.Push(ethutil.BigD(closure.Object().Address()))
case oBALANCE:
stack.Push(closure.Value)
case oORIGIN:
- stack.Push(ethutil.BigD(vm.vars.origin))
+ stack.Push(ethutil.BigD(vm.vars.Origin))
case oCALLER:
stack.Push(ethutil.BigD(closure.Callee().Address()))
case oCALLVALUE:
// FIXME: Original value of the call, not the current value
stack.Push(closure.Value)
- case oCALLDATA:
- offset := stack.Pop()
- mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args)
+ case oCALLDATALOAD:
+ require(1)
+ offset := stack.Pop().Int64()
+ val := closure.Args[offset : offset+32]
+
+ stack.Push(ethutil.BigD(val))
case oCALLDATASIZE:
stack.Push(big.NewInt(int64(len(closure.Args))))
case oGASPRICE:
- // TODO
+ stack.Push(closure.Price)
- // 0x40 range
+ // 0x40 range
case oPREVHASH:
- stack.Push(ethutil.BigD(vm.vars.prevHash))
+ stack.Push(ethutil.BigD(vm.vars.PrevHash))
case oCOINBASE:
- stack.Push(ethutil.BigD(vm.vars.coinbase))
+ stack.Push(ethutil.BigD(vm.vars.Coinbase))
case oTIMESTAMP:
- stack.Push(big.NewInt(vm.vars.time))
+ stack.Push(big.NewInt(vm.vars.Time))
case oNUMBER:
- stack.Push(big.NewInt(int64(vm.vars.blockNumber)))
+ stack.Push(big.NewInt(int64(vm.vars.BlockNumber)))
case oDIFFICULTY:
- stack.Push(vm.vars.diff)
+ stack.Push(vm.vars.Diff)
case oGASLIMIT:
// TODO
+ stack.Push(big.NewInt(0))
// 0x50 range
case oPUSH: // Push PC+1 on to the stack
pc.Add(pc, ethutil.Big1)
+ data := closure.Gets(pc, big.NewInt(32))
+ val := ethutil.BigD(data.Bytes())
+
+ // Push value to stack
+ stack.Push(val)
+
+ pc.Add(pc, big.NewInt(31))
+ step++
+ case oPUSH20:
+ pc.Add(pc, ethutil.Big1)
+ data := closure.Gets(pc, big.NewInt(20))
+ val := ethutil.BigD(data.Bytes())
- val := closure.GetMem(pc).BigInt()
+ // Push value to stack
stack.Push(val)
+
+ pc.Add(pc, big.NewInt(19))
+ step++
case oPOP:
+ require(1)
stack.Pop()
case oDUP:
+ require(1)
stack.Push(stack.Peek())
case oSWAP:
+ require(2)
x, y := stack.Popn()
stack.Push(y)
stack.Push(x)
case oMLOAD:
+ require(1)
offset := stack.Pop()
stack.Push(ethutil.BigD(mem.Get(offset.Int64(), 32)))
case oMSTORE: // 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))
case oMSTORE8:
+ require(2)
val, mStart := stack.Popn()
base.And(val, new(big.Int).SetInt64(0xff))
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
case oSLOAD:
+ require(1)
loc := stack.Pop()
val := closure.GetMem(loc)
stack.Push(val.BigInt())
case oSSTORE:
+ require(2)
val, loc := stack.Popn()
closure.SetMem(loc, ethutil.NewValue(val))
+
+ // Add the change to manifest
+ vm.stateManager.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
case oJUMP:
+ require(1)
pc = stack.Pop()
+ // Reduce pc by one because of the increment that's at the end of this for loop
+ pc.Sub(pc, ethutil.Big1)
case oJUMPI:
- pos, cond := stack.Popn()
- if cond.Cmp(big.NewInt(0)) > 0 {
+ require(2)
+ cond, pos := stack.Popn()
+ if cond.Cmp(ethutil.BigTrue) == 0 {
pc = pos
+ pc.Sub(pc, ethutil.Big1)
}
case oPC:
stack.Push(pc)
case oMSIZE:
stack.Push(big.NewInt(int64(mem.Len())))
- // 0x60 range
+ // 0x60 range
+ case oCREATE:
+ require(3)
+
+ value := stack.Pop()
+ size, offset := stack.Popn()
+
+ // Generate a new address
+ addr := ethutil.CreateAddress(closure.callee.Address(), closure.callee.N())
+ // Create a new contract
+ contract := NewContract(addr, value, []byte(""))
+ // Set the init script
+ contract.initScript = mem.Get(offset.Int64(), size.Int64())
+ // Transfer all remaining gas to the new
+ // contract so it may run the init script
+ gas := new(big.Int).Set(closure.Gas)
+ closure.Gas.Sub(closure.Gas, gas)
+ // Create the closure
+ closure := NewClosure(closure.callee,
+ closure.Object(),
+ contract.initScript,
+ vm.state,
+ gas,
+ closure.Price,
+ value)
+ // Call the closure and set the return value as
+ // main script.
+ closure.Script, err = closure.Call(vm, nil, hook)
+ if err != nil {
+ stack.Push(ethutil.BigFalse)
+ } else {
+ stack.Push(ethutil.BigD(addr))
+
+ vm.state.SetStateObject(contract)
+ }
case oCALL:
- // Pop return size and offset
- retSize, retOffset := stack.Popn()
+ require(7)
+ // Closure addr
+ addr := stack.Pop()
+ // Pop gas and value of the stack.
+ gas, value := stack.Popn()
// Pop input size and offset
inSize, inOffset := stack.Popn()
+ // Pop return size and offset
+ retSize, retOffset := stack.Popn()
+ // Make sure there's enough gas
+ if closure.Gas.Cmp(gas) < 0 {
+ stack.Push(ethutil.BigFalse)
+
+ break
+ }
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
- // Pop gas and value of the stack.
- gas, value := stack.Popn()
- // Closure addr
- addr := stack.Pop()
// Fetch the contract which will serve as the closure body
contract := vm.state.GetContract(addr.Bytes())
- // Create a new callable closure
- closure := NewClosure(closure, contract, vm.state, gas, value)
- // Executer the closure and get the return value (if any)
- ret := closure.Call(vm, args)
- mem.Set(retOffset.Int64(), retSize.Int64(), ret)
+ if contract != nil {
+ // Prepay for the gas
+ // If gas is set to 0 use all remaining gas for the next call
+ if gas.Cmp(big.NewInt(0)) == 0 {
+ // Copy
+ gas = new(big.Int).Set(closure.Gas)
+ }
+ closure.Gas.Sub(closure.Gas, gas)
+ // Create a new callable closure
+ closure := NewClosure(closure.Object(), contract, contract.script, vm.state, gas, closure.Price, value)
+ // Executer the closure and get the return value (if any)
+ ret, err := closure.Call(vm, args, hook)
+ if err != nil {
+ stack.Push(ethutil.BigFalse)
+ // Reset the changes applied this object
+ //contract.State().Reset()
+ } else {
+ stack.Push(ethutil.BigTrue)
+ // Notify of the changes
+ vm.stateManager.manifest.AddObjectChange(contract)
+ }
+
+ mem.Set(retOffset.Int64(), retSize.Int64(), ret)
+ } else {
+ ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes())
+ stack.Push(ethutil.BigFalse)
+ }
case oRETURN:
+ require(2)
size, offset := stack.Popn()
ret := mem.Get(offset.Int64(), size.Int64())
- return closure.Return(ret)
+ return closure.Return(ret), nil
case oSUICIDE:
- /*
- recAddr := stack.Pop().Bytes()
- // Purge all memory
- deletedMemory := contract.state.Purge()
- // Add refunds to the pop'ed address
- refund := new(big.Int).Mul(StoreFee, big.NewInt(int64(deletedMemory)))
- account := state.GetAccount(recAddr)
- account.Amount.Add(account.Amount, refund)
- // Update the refunding address
- state.UpdateAccount(recAddr, account)
- // Delete the contract
- state.trie.Update(string(addr), "")
-
- ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr)
- break out
- */
- default:
- ethutil.Config.Log.Debugln("Invalid opcode", op)
- }
+ require(1)
- pc.Add(pc, ethutil.Big1)
- }
-}
+ receiver := vm.state.GetAccount(stack.Pop().Bytes())
+ receiver.AddAmount(closure.object.Amount)
-func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
- ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length)
- j := int64(0)
- dataItems := make([]string, int(length.Uint64()))
- for i := from.Int64(); i < length.Int64(); i++ {
- dataItems[j] = contract.GetMem(big.NewInt(j)).Str()
- j++
- }
+ vm.stateManager.manifest.AddObjectChange(receiver)
- tx := NewTransaction(addr, value, dataItems)
- if tx.IsContract() {
- contract := MakeContract(tx, state)
- state.UpdateContract(contract)
- } else {
- account := state.GetAccount(tx.Recipient)
- account.Amount.Add(account.Amount, tx.Value)
- state.UpdateAccount(tx.Recipient, account)
- }
-}
+ closure.object.state.Purge()
-// Returns an address from the specified contract's address
-func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.Int {
- contract := state.GetContract(contractAddr)
- if contract == nil {
- log.Panicf("invalid contract addr %x", contractAddr)
- }
- val := state.trie.Get(memAddr.String())
+ fallthrough
+ case oSTOP: // Stop the closure
+ return closure.Return(nil), nil
+ default:
+ ethutil.Config.Log.Debugf("Invalid opcode %x\n", op)
- // decode the object as a big integer
- decoder := ethutil.NewValueFromBytes([]byte(val))
- if decoder.IsNil() {
- return ethutil.BigFalse
- }
+ return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
+ }
+
+ pc.Add(pc, ethutil.Big1)
- return decoder.BigInt()
+ if hook != nil {
+ hook(step-1, op, mem, stack)
+ }
+ }
}
diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go
index 047531e09..35a7b2e3f 100644
--- a/ethchain/vm_test.go
+++ b/ethchain/vm_test.go
@@ -1,114 +1,17 @@
package ethchain
import (
- "bytes"
+ _ "bytes"
+ "fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
+ "github.com/obscuren/mutan"
"math/big"
+ "strings"
"testing"
)
/*
-
-func TestRun(t *testing.T) {
- InitFees()
-
- ethutil.ReadConfig("")
-
- db, _ := ethdb.NewMemDatabase()
- state := NewState(ethutil.NewTrie(db, ""))
-
- script := Compile([]string{
- "TXSENDER",
- "SUICIDE",
- })
-
- tx := NewTransaction(ContractAddr, big.NewInt(1e17), script)
- fmt.Printf("contract addr %x\n", tx.Hash()[12:])
- contract := MakeContract(tx, state)
- vm := &Vm{}
-
- vm.Process(contract, state, RuntimeVars{
- address: tx.Hash()[12:],
- blockNumber: 1,
- sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
- prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
- coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
- time: 1,
- diff: big.NewInt(256),
- txValue: tx.Value,
- txData: tx.Data,
- })
-}
-
-func TestRun1(t *testing.T) {
- ethutil.ReadConfig("")
-
- db, _ := ethdb.NewMemDatabase()
- state := NewState(ethutil.NewTrie(db, ""))
-
- script := Compile([]string{
- "PUSH", "0",
- "PUSH", "0",
- "TXSENDER",
- "PUSH", "10000000",
- "MKTX",
- })
- fmt.Println(ethutil.NewValue(script))
-
- tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
- fmt.Printf("contract addr %x\n", tx.Hash()[12:])
- contract := MakeContract(tx, state)
- vm := &Vm{}
-
- vm.Process(contract, state, RuntimeVars{
- address: tx.Hash()[12:],
- blockNumber: 1,
- sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
- prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
- coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
- time: 1,
- diff: big.NewInt(256),
- txValue: tx.Value,
- txData: tx.Data,
- })
-}
-
-func TestRun2(t *testing.T) {
- ethutil.ReadConfig("")
-
- db, _ := ethdb.NewMemDatabase()
- state := NewState(ethutil.NewTrie(db, ""))
-
- script := Compile([]string{
- "PUSH", "0",
- "PUSH", "0",
- "TXSENDER",
- "PUSH", "10000000",
- "MKTX",
- })
- fmt.Println(ethutil.NewValue(script))
-
- tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
- fmt.Printf("contract addr %x\n", tx.Hash()[12:])
- contract := MakeContract(tx, state)
- vm := &Vm{}
-
- vm.Process(contract, state, RuntimeVars{
- address: tx.Hash()[12:],
- blockNumber: 1,
- sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
- prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
- coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
- time: 1,
- diff: big.NewInt(256),
- txValue: tx.Value,
- txData: tx.Data,
- })
-}
-*/
-
-// XXX Full stack test
func TestRun3(t *testing.T) {
ethutil.ReadConfig("")
@@ -127,12 +30,12 @@ func TestRun3(t *testing.T) {
"PUSH", "0",
"RETURN",
})
- tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
+ tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script)
addr := tx.Hash()[12:]
contract := MakeContract(tx, state)
state.UpdateContract(contract)
- callerScript := ethutil.Compile(
+ callerScript := ethutil.Assemble(
"PUSH", 1337, // Argument
"PUSH", 65, // argument mem offset
"MSTORE",
@@ -149,7 +52,7 @@ func TestRun3(t *testing.T) {
"PUSH", 0,
"RETURN",
)
- callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript)
+ callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript)
// Contract addr as test address
account := NewAccount(ContractAddr, big.NewInt(10000000))
@@ -171,4 +74,87 @@ func TestRun3(t *testing.T) {
if bytes.Compare(ret, exp) != 0 {
t.Errorf("expected return value to be %v, got %v", exp, ret)
}
+}*/
+
+func TestRun4(t *testing.T) {
+ ethutil.ReadConfig("")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ script, err := mutan.Compile(strings.NewReader(`
+ int32 a = 10
+ int32 b = 20
+ if a > b {
+ int32 c = this.Caller()
+ }
+ Exit()
+ `), false)
+ tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), script, nil)
+ addr := tx.Hash()[12:]
+ contract := MakeContract(tx, state)
+ state.UpdateStateObject(contract)
+ fmt.Printf("%x\n", addr)
+
+ callerScript, err := mutan.Compile(strings.NewReader(`
+ // Check if there's any cash in the initial store
+ if store[1000] == 0 {
+ store[1000] = 10^20
+ }
+
+
+ store[1001] = this.Value() * 20
+ store[this.Origin()] = store[this.Origin()] + 1000
+
+ if store[1001] > 20 {
+ store[1001] = 10^50
+ }
+
+ int8 ret = 0
+ int8 arg = 10
+ Call(0xe6a12555fad1fb6eaaaed69001a87313d1fd7b54, 0, 100, arg, ret)
+
+ big t
+ for int8 i = 0; i < 10; i++ {
+ t = i
+ }
+
+ if 10 > 20 {
+ int8 shouldnt = 2
+ } else {
+ int8 should = 1
+ }
+ `), false)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript, nil)
+
+ // 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, c.script, state, gas, gasPrice, big.NewInt(0))
+
+ vm := NewVm(state, nil, RuntimeVars{
+ Origin: account.Address(),
+ BlockNumber: 1,
+ PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
+ Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
+ Time: 1,
+ Diff: big.NewInt(256),
+ })
+ _, e = callerClosure.Call(vm, nil, nil)
+ if e != nil {
+ fmt.Println("error", e)
+ }
+ fmt.Println("account.Amount =", account.Amount)
}
diff --git a/ethereum.go b/ethereum.go
index fb97c34b1..707938639 100644
--- a/ethereum.go
+++ b/ethereum.go
@@ -4,6 +4,7 @@ import (
"container/list"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"io/ioutil"
@@ -60,6 +61,10 @@ type Ethereum struct {
// Specifies the desired amount of maximum peers
MaxPeers int
+
+ reactor *ethutil.ReactorEngine
+
+ RpcServer *ethrpc.JsonRpcServer
}
func New(caps Caps, usePnp bool) (*Ethereum, error) {
@@ -89,6 +94,8 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
serverCaps: caps,
nat: nat,
}
+ ethereum.reactor = ethutil.NewReactorEngine()
+
ethereum.txPool = ethchain.NewTxPool(ethereum)
ethereum.blockChain = ethchain.NewBlockChain(ethereum)
ethereum.stateManager = ethchain.NewStateManager(ethereum)
@@ -99,6 +106,10 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
return ethereum, nil
}
+func (s *Ethereum) Reactor() *ethutil.ReactorEngine {
+ return s.reactor
+}
+
func (s *Ethereum) BlockChain() *ethchain.BlockChain {
return s.blockChain
}
@@ -328,6 +339,7 @@ func (s *Ethereum) Stop() {
close(s.quit)
+ s.RpcServer.Stop()
s.txPool.Stop()
s.stateManager.Stop()
@@ -342,7 +354,7 @@ func (s *Ethereum) WaitForShutdown() {
func (s *Ethereum) upnpUpdateThread() {
// Go off immediately to prevent code duplication, thereafter we renew
// lease every 15 minutes.
- timer := time.NewTimer(0 * time.Second)
+ timer := time.NewTimer(5 * time.Minute)
lport, _ := strconv.ParseInt(s.Port, 10, 16)
first := true
out:
diff --git a/ethminer/miner.go b/ethminer/miner.go
new file mode 100644
index 000000000..3796c873e
--- /dev/null
+++ b/ethminer/miner.go
@@ -0,0 +1,156 @@
+package ethminer
+
+import (
+ "bytes"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/eth-go/ethwire"
+ "log"
+)
+
+type Miner struct {
+ pow ethchain.PoW
+ ethereum ethchain.EthManager
+ coinbase []byte
+ reactChan chan ethutil.React
+ txs []*ethchain.Transaction
+ uncles []*ethchain.Block
+ block *ethchain.Block
+ powChan chan []byte
+ quitChan chan ethutil.React
+}
+
+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
+ quitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread
+
+ ethereum.Reactor().Subscribe("newBlock", reactChan)
+ ethereum.Reactor().Subscribe("newTx", 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", quitChan)
+ ethereum.Reactor().Subscribe("newTx", quitChan)
+
+ miner := Miner{
+ pow: &ethchain.EasyPow{},
+ ethereum: ethereum,
+ coinbase: coinbase,
+ reactChan: reactChan,
+ powChan: powChan,
+ quitChan: quitChan,
+ }
+
+ // Insert initial TXs in our little miner 'pool'
+ miner.txs = ethereum.TxPool().Flush()
+ miner.block = ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
+
+ return miner
+}
+func (miner *Miner) Start() {
+ // Prepare inital block
+ miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
+ go func() { miner.listener() }()
+}
+func (miner *Miner) listener() {
+ for {
+ select {
+ case chanMessage := <-miner.reactChan:
+ if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
+ log.Println("[MINER] 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
+ log.Println("[MINER] 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 {
+ log.Println("[MINER] Adding uncle block")
+ miner.uncles = append(miner.uncles, block)
+ miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
+ }
+ }
+ }
+
+ if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok {
+ //log.Println("[MINER] Got new transaction from Reactor", tx)
+ found := false
+ for _, ctx := range miner.txs {
+ if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found {
+ break
+ }
+
+ }
+ if found == false {
+ //log.Println("[MINER] We did not know about this transaction, adding")
+ miner.txs = append(miner.txs, tx)
+ miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
+ miner.block.SetTransactions(miner.txs)
+ } else {
+ //log.Println("[MINER] We already had this transaction, ignoring")
+ }
+ }
+ default:
+ log.Println("[MINER] Mining on block. Includes", len(miner.txs), "transactions")
+
+ // Apply uncles
+ if len(miner.uncles) > 0 {
+ miner.block.SetUncles(miner.uncles)
+ }
+
+ // FIXME @ maranh, first block doesn't need this. Everything after the first block does.
+ // Please check and fix
+ miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
+ // Apply all transactions to the block
+ miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions())
+ miner.ethereum.StateManager().AccumelateRewards(miner.block)
+
+ // Search the nonce
+ //log.Println("[MINER] Initialision complete, starting mining")
+ miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan)
+ if miner.block.Nonce != nil {
+ miner.ethereum.StateManager().PrepareDefault(miner.block)
+ err := miner.ethereum.StateManager().ProcessBlock(miner.block, true)
+ if err != nil {
+ log.Println(err)
+ miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
+ miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
+ } else {
+
+ /*
+ // XXX @maranh This is already done in the state manager, why a 2nd time?
+ if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) {
+ log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce))
+ }
+ */
+ miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val})
+ log.Printf("[MINER] 🔨 Mined block %x\n", miner.block.Hash())
+
+ miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
+ miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
+ }
+ }
+ }
+ }
+}
diff --git a/ethpub/pub.go b/ethpub/pub.go
new file mode 100644
index 000000000..5e7792a9f
--- /dev/null
+++ b/ethpub/pub.go
@@ -0,0 +1,147 @@
+package ethpub
+
+import (
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+)
+
+type PEthereum struct {
+ stateManager *ethchain.StateManager
+ blockChain *ethchain.BlockChain
+ txPool *ethchain.TxPool
+}
+
+func NewPEthereum(sm *ethchain.StateManager, bc *ethchain.BlockChain, txp *ethchain.TxPool) *PEthereum {
+ return &PEthereum{
+ sm,
+ bc,
+ txp,
+ }
+}
+
+func (lib *PEthereum) GetBlock(hexHash string) *PBlock {
+ hash := ethutil.FromHex(hexHash)
+
+ block := lib.blockChain.GetBlock(hash)
+
+ var blockInfo *PBlock
+
+ if block != nil {
+ blockInfo = &PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
+ } else {
+ blockInfo = &PBlock{Number: -1, Hash: ""}
+ }
+
+ return blockInfo
+}
+
+func (lib *PEthereum) GetKey() *PKey {
+ keyPair, err := ethchain.NewKeyPairFromSec(ethutil.Config.Db.GetKeys()[0].PrivateKey)
+ if err != nil {
+ return nil
+ }
+
+ return NewPKey(keyPair)
+}
+
+func (lib *PEthereum) GetStateObject(address string) *PStateObject {
+ stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
+ if stateObject != nil {
+ return NewPStateObject(stateObject)
+ }
+
+ // See GetStorage for explanation on "nil"
+ return NewPStateObject(nil)
+}
+
+func (lib *PEthereum) GetStorage(address, storageAddress string) string {
+ return lib.GetStateObject(address).GetStorage(storageAddress)
+}
+
+func (lib *PEthereum) GetTxCount(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 := ethchain.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, initStr, bodyStr string) (*PReceipt, error) {
+ return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, initStr, bodyStr)
+}
+
+func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, initStr, scriptStr string) (*PReceipt, error) {
+ var hash []byte
+ var contractCreation bool
+ if len(recipient) == 0 {
+ contractCreation = true
+ } else {
+ hash = ethutil.FromHex(recipient)
+ }
+
+ keyPair, err := ethchain.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 initScript, mainScript []byte
+ var err error
+ if ethutil.IsHex(initStr) {
+ initScript = ethutil.FromHex(initStr[2:])
+ } else {
+ initScript, err = ethutil.Compile(initStr)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if ethutil.IsHex(scriptStr) {
+ mainScript = ethutil.FromHex(scriptStr[2:])
+ } else {
+ mainScript, err = ethutil.Compile(scriptStr)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript)
+ } else {
+ // Just in case it was submitted as a 0x prefixed string
+ if len(initStr) > 0 && initStr[0:2] == "0x" {
+ initStr = initStr[2:len(initStr)]
+ }
+ tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, ethutil.FromHex(initStr))
+ }
+
+ acc := lib.stateManager.GetAddrState(keyPair.Address())
+ tx.Nonce = acc.Nonce
+ tx.Sign(keyPair.PrivateKey)
+ lib.txPool.QueueTransaction(tx)
+
+ if contractCreation {
+ ethutil.Config.Log.Infof("Contract addr %x", tx.CreationAddress())
+ } else {
+ ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
+ }
+
+ 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..7f25e48a6
--- /dev/null
+++ b/ethpub/types.go
@@ -0,0 +1,123 @@
+package ethpub
+
+import (
+ "encoding/hex"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+)
+
+// Block interface exposed to QML
+type PBlock struct {
+ Number int `json:"number"`
+ Hash string `json:"hash"`
+}
+
+// Creates a new QML Block from a chain block
+func NewPBlock(block *ethchain.Block) *PBlock {
+ info := block.BlockInfo()
+ hash := hex.EncodeToString(block.Hash())
+
+ return &PBlock{Number: int(info.Number), Hash: hash}
+}
+
+type PTx struct {
+ Value, Hash, Address string
+ Contract bool
+}
+
+func NewPTx(tx *ethchain.Transaction) *PTx {
+ hash := hex.EncodeToString(tx.Hash())
+ sender := hex.EncodeToString(tx.Recipient)
+ isContract := len(tx.Data) > 0
+
+ return &PTx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender, Contract: isContract}
+}
+
+type PKey struct {
+ Address string `json:"address"`
+ PrivateKey string `json:"privateKey"`
+ PublicKey string `json:"publicKey"`
+}
+
+func NewPKey(key *ethchain.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) IsContract() bool {
+ if c.object != nil {
+ return len(c.object.Script()) > 0
+ }
+
+ return false
+}
+
+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..b989a65cb
--- /dev/null
+++ b/ethrpc/packages.go
@@ -0,0 +1,215 @@
+package ethrpc
+
+import (
+ "encoding/json"
+ "errors"
+ "github.com/ethereum/eth-go/ethpub"
+ _ "log"
+)
+
+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.Init, 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)
+ value := state.GetStorage(args.Key)
+ *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value})
+ 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..40787fade
--- /dev/null
+++ b/ethrpc/server.go
@@ -0,0 +1,62 @@
+package ethrpc
+
+import (
+ "github.com/ethereum/eth-go/ethpub"
+ "github.com/ethereum/eth-go/ethutil"
+ "net"
+ "net/rpc"
+ "net/rpc/jsonrpc"
+)
+
+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
+ }
+ }
+
+ ethutil.Config.Log.Infoln("[JSON] Shutdown JSON-RPC server")
+}
+
+func (s *JsonRpcServer) Stop() {
+ close(s.quit)
+}
+
+func (s *JsonRpcServer) Start() {
+ ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server")
+ go s.exitHandler()
+ rpc.Register(&EthereumApi{ethp: s.ethp})
+ rpc.HandleHTTP()
+
+ for {
+ conn, err := s.listener.Accept()
+ if err != nil {
+ ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err)
+ break
+ }
+ ethutil.Config.Log.Debugln("[JSON] Incoming request.")
+ go jsonrpc.ServeConn(conn)
+ }
+}
+
+func NewJsonRpcServer(ethp *ethpub.PEthereum) *JsonRpcServer {
+ l, err := net.Listen("tcp", ":30304")
+ if err != nil {
+ ethutil.Config.Log.Infoln("Error starting JSON-RPC")
+ }
+
+ return &JsonRpcServer{
+ listener: l,
+ quit: make(chan bool),
+ ethp: ethp,
+ }
+}
diff --git a/ethutil/big.go b/ethutil/big.go
index 1a3902fa3..891d476ad 100644
--- a/ethutil/big.go
+++ b/ethutil/big.go
@@ -12,7 +12,9 @@ var BigTrue *big.Int = big.NewInt(1)
// False
var BigFalse *big.Int = big.NewInt(0)
-// Returns the power of two integers
+// 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))
@@ -20,7 +22,9 @@ func BigPow(a, b int) *big.Int {
return c
}
-// Like big.NewInt(uint64); this takes a string instead.
+// Big
+//
+// Shortcut for new(big.Int).SetString(..., 0)
func Big(num string) *big.Int {
n := new(big.Int)
n.SetString(num, 0)
@@ -28,7 +32,9 @@ func Big(num string) *big.Int {
return n
}
-// Like big.NewInt(uint64); this takes a byte buffer instead.
+// BigD
+//
+// Shortcut for new(big.Int).SetBytes(...)
func BigD(data []byte) *big.Int {
n := new(big.Int)
n.SetBytes(data)
@@ -36,17 +42,30 @@ func BigD(data []byte) *big.Int {
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)
return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
}
-// Functions like the build in "copy" function
-// but works on big integers
-func BigCopy(src *big.Int) (ret *big.Int) {
- ret = new(big.Int)
- ret.Add(ret, src)
+// 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 x
+ }
- return
+ return y
}
diff --git a/ethutil/bytes.go b/ethutil/bytes.go
index 40903a5f1..b298675a2 100644
--- a/ethutil/bytes.go
+++ b/ethutil/bytes.go
@@ -6,6 +6,9 @@ import (
"fmt"
)
+// 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)
@@ -16,6 +19,9 @@ func NumberToBytes(num interface{}, bits int) []byte {
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
@@ -32,7 +38,9 @@ func BytesToNumber(b []byte) uint64 {
return number
}
-// Read variable integer in big endian
+// 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
@@ -55,6 +63,9 @@ func ReadVarint(reader *bytes.Reader) (ret uint64) {
return ret
}
+// Binary length
+//
+// Returns the true binary length of the given number
func BinaryLength(num int) int {
if num == 0 {
return 0
@@ -62,3 +73,18 @@ func BinaryLength(num int) int {
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"
+}
diff --git a/ethutil/common.go b/ethutil/common.go
index f15b10e6d..983ea5d1b 100644
--- a/ethutil/common.go
+++ b/ethutil/common.go
@@ -5,16 +5,20 @@ import (
"math/big"
)
+// The different number of units
var (
Ether = BigPow(10, 18)
Finney = BigPow(10, 15)
Szabo = BigPow(10, 12)
- Vito = BigPow(10, 9)
+ Vita = BigPow(10, 9)
Turing = BigPow(10, 6)
Eins = 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(Ether) >= 0:
@@ -23,8 +27,8 @@ func CurrencyToString(num *big.Int) string {
return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney))
case num.Cmp(Szabo) >= 0:
return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo))
- case num.Cmp(Vito) >= 0:
- return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito))
+ case num.Cmp(Vita) >= 0:
+ return fmt.Sprintf("%v Vita", new(big.Int).Div(num, Vita))
case num.Cmp(Turing) >= 0:
return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing))
case num.Cmp(Eins) >= 0:
@@ -34,8 +38,18 @@ func CurrencyToString(num *big.Int) string {
return fmt.Sprintf("%v Wei", num)
}
+// Common big integers often used
var (
Big1 = big.NewInt(1)
+ Big2 = big.NewInt(1)
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/config.go b/ethutil/config.go
index 54b066fb9..382396ceb 100644
--- a/ethutil/config.go
+++ b/ethutil/config.go
@@ -9,6 +9,7 @@ import (
"runtime"
)
+// Log types available
type LogType byte
const (
@@ -16,7 +17,7 @@ const (
LogTypeFile = 2
)
-// Config struct isn't exposed
+// Config struct
type config struct {
Db Database
@@ -31,7 +32,9 @@ type config struct {
var Config *config
-// Read config doesn't read anything yet.
+// Read config
+//
+// Initialize the global Config variable with default settings
func ReadConfig(base string) *config {
if Config == nil {
usr, _ := user.Current()
@@ -48,7 +51,7 @@ func ReadConfig(base string) *config {
}
}
- Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"}
+ Config = &config{ExecPath: path, Debug: true, Ver: "0.5 RC1"}
Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
Config.SetClientString("/Ethereum(G)")
}
@@ -56,6 +59,8 @@ func ReadConfig(base string) *config {
return Config
}
+// Set client string
+//
func (c *config) SetClientString(str string) {
Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS)
}
@@ -134,7 +139,7 @@ func (log *Logger) Infoln(v ...interface{}) {
return
}
- fmt.Println(len(log.logSys))
+ //fmt.Println(len(log.logSys))
for _, logger := range log.logSys {
logger.Println(v...)
}
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/parsing.go b/ethutil/parsing.go
deleted file mode 100644
index 8929f0829..000000000
--- a/ethutil/parsing.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package ethutil
-
-import (
- "math/big"
- "strconv"
-)
-
-// Op codes
-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,
- "CALLDATA": 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
- "PUSH": 0x50,
- "POP": 0x51,
- "DUP": 0x52,
- "SWAP": 0x53,
- "MLOAD": 0x54,
- "MSTORE": 0x55,
- "MSTORE8": 0x56,
- "SLOAD": 0x57,
- "SSTORE": 0x58,
- "JUMP": 0x59,
- "JUMPI": 0x5a,
- "PC": 0x5b,
- "MSIZE": 0x5c,
-
- // 0x60 range - closures
- "CREATE": 0x60,
- "CALL": 0x61,
- "RETURN": 0x62,
-
- // 0x70 range - other
- "LOG": 0x70,
- "SUICIDE": 0x7f,
-}
-
-func IsOpCode(s string) bool {
- for key, _ := range OpCodes {
- if key == s {
- return true
- }
- }
- return false
-}
-
-func CompileInstr(s interface{}) ([]byte, error) {
- switch s.(type) {
- case string:
- str := s.(string)
- isOp := IsOpCode(str)
- if isOp {
- return []byte{OpCodes[str]}, nil
- }
-
- num := new(big.Int)
- _, success := num.SetString(str, 0)
- // Assume regular bytes during compilation
- if !success {
- num.SetBytes([]byte(str))
- }
-
- return num.Bytes(), nil
- case int:
- return big.NewInt(int64(s.(int))).Bytes(), nil
- case []byte:
- return BigD(s.([]byte)).Bytes(), nil
- }
-
- return nil, nil
-}
-
-func Instr(instr string) (int, []string, error) {
-
- base := new(big.Int)
- base.SetString(instr, 0)
-
- args := make([]string, 7)
- for i := 0; i < 7; i++ {
- // int(int(val) / int(math.Pow(256,float64(i)))) % 256
- exp := BigPow(256, i)
- num := new(big.Int)
- num.Div(base, exp)
-
- args[i] = num.Mod(num, big.NewInt(256)).String()
- }
- op, _ := strconv.Atoi(args[0])
-
- return op, args[1:7], nil
-}
-
-// Script compilation functions
-// Compiles strings to machine code
-func Compile(instructions ...interface{}) (script []string) {
- script = make([]string, len(instructions))
-
- for i, val := range instructions {
- instr, _ := CompileInstr(val)
-
- script[i] = string(instr)
- }
-
- return
-}
diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go
deleted file mode 100644
index 6b59777e6..000000000
--- a/ethutil/parsing_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package ethutil
-
-/*
-import (
- "math"
- "testing"
-)
-
-func TestCompile(t *testing.T) {
- instr, err := CompileInstr("PUSH")
-
- if err != nil {
- t.Error("Failed compiling instruction")
- }
-
- calc := (48 + 0*256 + 0*int64(math.Pow(256, 2)))
- if BigD(instr).Int64() != calc {
- t.Error("Expected", calc, ", got:", instr)
- }
-}
-
-func TestValidInstr(t *testing.T) {
- op, args, err := Instr("68163")
- if err != nil {
- t.Error("Error decoding instruction")
- }
-
-}
-
-func TestInvalidInstr(t *testing.T) {
-}
-*/
diff --git a/ethutil/rlp.go b/ethutil/rlp.go
index 33ec0d359..69f80a0a6 100644
--- a/ethutil/rlp.go
+++ b/ethutil/rlp.go
@@ -26,16 +26,6 @@ func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
return Encode(rlpData)
}
-/*
-func FromBin(data []byte) uint64 {
- if len(data) == 0 {
- return 0
- }
-
- return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1])
-}
-*/
-
const (
RlpEmptyList = 0x80
RlpEmptyStr = 0x40
@@ -57,7 +47,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
switch {
case char == 0:
return nil
- case char <= 0x7c:
+ case char <= 0x7f:
return char
case char <= 0xb7:
@@ -186,7 +176,12 @@ func Encode(object interface{}) []byte {
case byte:
buff.Write(Encode(big.NewInt(int64(t))))
case *big.Int:
- buff.Write(Encode(t.Bytes()))
+ // 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)
diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go
index 2a58bfc0f..9e8127aab 100644
--- a/ethutil/rlp_test.go
+++ b/ethutil/rlp_test.go
@@ -2,6 +2,7 @@ package ethutil
import (
"bytes"
+ "fmt"
"math/big"
"reflect"
"testing"
@@ -55,6 +56,15 @@ func TestValue(t *testing.T) {
}
}
+func TestEncodeDecodeMaran(t *testing.T) {
+ b := NewValue([]interface{}{"dog", 15, []interface{}{"cat", "cat", []interface{}{}}, 1024, "tachikoma"})
+ a := b.Encode()
+ fmt.Println("voor maran", a)
+ f, i := Decode(a, 0)
+ fmt.Println("voor maran 2", f)
+ fmt.Println(i)
+}
+
func TestEncode(t *testing.T) {
strRes := "\x83dog"
bytes := Encode("dog")
@@ -119,6 +129,11 @@ func TestEncodeDecodeBytes(t *testing.T) {
}
}
+func TestEncodeZero(t *testing.T) {
+ b := NewValue(0).Encode()
+ fmt.Println(b)
+}
+
func BenchmarkEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
bytes := Encode([]interface{}{"dog", "god", "cat"})
diff --git a/ethutil/script.go b/ethutil/script.go
new file mode 100644
index 000000000..620658025
--- /dev/null
+++ b/ethutil/script.go
@@ -0,0 +1,41 @@
+package ethutil
+
+import (
+ "fmt"
+ "github.com/obscuren/mutan"
+ "strings"
+)
+
+// General compile function
+func Compile(script string) ([]byte, error) {
+ byteCode, errors := mutan.Compile(strings.NewReader(script), false)
+ 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
+}
+
+func CompileScript(script string) ([]byte, []byte, error) {
+ // Preprocess
+ mainInput, initInput := mutan.PreProcess(script)
+ // Compile main script
+ mainScript, err := Compile(mainInput)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Compile init script
+ initScript, err := Compile(initInput)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return mainScript, initScript, nil
+}
diff --git a/ethutil/trie.go b/ethutil/trie.go
index c67f750bc..4d088ccff 100644
--- a/ethutil/trie.go
+++ b/ethutil/trie.go
@@ -119,14 +119,29 @@ type Trie struct {
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 {
- return &Trie{cache: NewCache(db), Root: Root, prevRoot: Root}
+ // 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 = t.Root
+ t.prevRoot = copyRoot(t.Root)
}
func (t *Trie) Undo() {
diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go
index 79e5de921..0be512d9f 100644
--- a/ethutil/trie_test.go
+++ b/ethutil/trie_test.go
@@ -1,7 +1,7 @@
package ethutil
import (
- "fmt"
+ _ "fmt"
"reflect"
"testing"
)
diff --git a/ethutil/value.go b/ethutil/value.go
index 46681ec2a..b7756f9b1 100644
--- a/ethutil/value.go
+++ b/ethutil/value.go
@@ -20,7 +20,12 @@ func (val *Value) String() string {
}
func NewValue(val interface{}) *Value {
- return &Value{Val: val}
+ t := val
+ if v, ok := val.(*Value); ok {
+ t = v.Val
+ }
+
+ return &Value{Val: t}
}
func (val *Value) Type() reflect.Kind {
@@ -149,6 +154,15 @@ 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)
}
diff --git a/ethwire/messaging.go b/ethwire/messaging.go
index 185faa341..b622376f3 100644
--- a/ethwire/messaging.go
+++ b/ethwire/messaging.go
@@ -32,6 +32,7 @@ const (
MsgBlockTy = 0x13
MsgGetChainTy = 0x14
MsgNotInChainTy = 0x15
+ MsgGetTxsTy = 0x16
MsgTalkTy = 0xff
)
@@ -46,6 +47,7 @@ var msgTypeToString = map[MsgType]string{
MsgTxTy: "Transactions",
MsgBlockTy: "Blocks",
MsgGetChainTy: "Get chain",
+ MsgGetTxsTy: "Get Txs",
MsgNotInChainTy: "Not in chain",
}
diff --git a/natupnp.go b/natupnp.go
index e4072d0dd..c7f9eeb62 100644
--- a/natupnp.go
+++ b/natupnp.go
@@ -246,6 +246,10 @@ func soapRequest(url, function, message string) (r *http.Response, err error) {
//fmt.Println(fullMessage)
r, err = http.DefaultClient.Do(req)
+ if err != nil {
+ return
+ }
+
if r.Body != nil {
defer r.Body.Close()
}
diff --git a/peer.go b/peer.go
index 24a5e97c9..80ddc5142 100644
--- a/peer.go
+++ b/peer.go
@@ -125,7 +125,8 @@ type Peer struct {
pubkey []byte
// Indicated whether the node is catching up or not
- catchingUp bool
+ catchingUp bool
+ blocksRequested int
Version string
}
@@ -135,15 +136,16 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes()
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,
+ 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,
}
}
@@ -290,17 +292,69 @@ func (p *Peer) HandleInbound() {
// Get all blocks and process them
var block, lastBlock *ethchain.Block
var err error
+
+ // 1. Compare the first block over the wire's prev-hash with the hash of your last block
+ // 2. If these two values are the same you can just link the chains together.
+ // [1:0,2:1,3:2] <- Current blocks (format block:previous_block)
+ // [1:0,2:1,3:2,4:3,5:4] <- incoming blocks
+ // == [1,2,3,4,5]
+ // 3. If the values are not the same we will have to go back and calculate the chain with the highest total difficulty
+ // [1:0,2:1,3:2,11:3,12:11,13:12]
+ // [1:0,2:1,3:2,4:3,5:4,6:5]
+
+ // [3:2,11:3,12:11,13:12]
+ // [3:2,4:3,5:4,6:5]
+ // Heb ik dit blok?
+ // Nee: heb ik een blok met PrevHash 3?
+ // Ja: DIVERSION
+ // Nee; Adding to chain
+
+ // See if we can find a common ancestor
+ // 1. Get the earliest block in the package.
+ // 2. Do we have this block?
+ // 3. Yes: Let's continue what we are doing
+ // 4. No: Let's request more blocks back.
+
+ // Make sure we are actually receiving anything
+ if msg.Data.Len()-1 > 1 && p.catchingUp {
+ // 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.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.
+ //fmt.Println("[PEER] No common ancestor found, requesting more blocks.")
+ p.blocksRequested = p.blocksRequested * 2
+ p.catchingUp = false
+ p.SyncWithBlocks()
+ }
+
+ 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) {
+ //ethutil.Config.Log.Infof("[PEER] Local and foreign chain have diverted after %x, finding best chain!\n", block.PrevHash)
+ if p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) {
+ return
+ }
+ }
+ }
+ }
+ }
+
for i := msg.Data.Len() - 1; i >= 0; i-- {
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
p.ethereum.StateManager().PrepareDefault(block)
- err = p.ethereum.StateManager().ProcessBlock(block)
+ err = p.ethereum.StateManager().ProcessBlock(block, false)
if err != nil {
if ethutil.Config.Debug {
ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash())
ethutil.Config.Log.Infof("[PEER] %v\n", err)
- ethutil.Config.Log.Infoln(block)
}
break
} else {
@@ -313,7 +367,7 @@ func (p *Peer) HandleInbound() {
if ethchain.IsParentErr(err) {
ethutil.Config.Log.Infoln("Attempting to catch up")
p.catchingUp = false
- p.CatchupWithPeer()
+ p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
} else if ethchain.IsValidationErr(err) {
// TODO
}
@@ -326,7 +380,7 @@ func (p *Peer) HandleInbound() {
ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
}
p.catchingUp = false
- p.CatchupWithPeer()
+ p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
}
}
case ethwire.MsgTxTy:
@@ -334,7 +388,8 @@ func (p *Peer) HandleInbound() {
// in the TxPool where it will undergo validation and
// processing when a new block is found
for i := 0; i < msg.Data.Len(); i++ {
- p.ethereum.TxPool().QueueTransaction(ethchain.NewTransactionFromData(msg.Data.Get(i).Encode()))
+ 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
@@ -373,11 +428,11 @@ func (p *Peer) HandleInbound() {
// 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) {
+ if data := msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) {
parent = p.ethereum.BlockChain().GetBlock(data)
break
}
@@ -385,9 +440,14 @@ func (p *Peer) HandleInbound() {
// If a parent is found send back a reply
if parent != nil {
+ ethutil.Config.Log.Debugf("[PEER] Found conical block, returning chain from: %x ", parent.Hash())
chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks)
- p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
+ if len(chain) > 0 {
+ ethutil.Config.Log.Debugf("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash())
+ p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
+ }
} else {
+ ethutil.Config.Log.Debugf("[PEER] 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
lastHash := msg.Data.Get(l - 1)
@@ -395,8 +455,18 @@ func (p *Peer) HandleInbound() {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
}
case ethwire.MsgNotInChainTy:
- ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data)
+ ethutil.Config.Log.Debugf("Not in chain %x\n", msg.Data)
// TODO
+ 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:
@@ -408,29 +478,6 @@ func (p *Peer) HandleInbound() {
p.Stop()
}
-func packAddr(address, port string) ([]interface{}, 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 := []interface{}{int32(a), int32(b), int32(c), int32(d)}
- prt, _ := strconv.Atoi(port)
-
- return host, uint16(prt)
-}
-
-func unpackAddr(value *ethutil.Value, p uint64) string {
- a := strconv.Itoa(int(value.Get(0).Uint()))
- b := strconv.Itoa(int(value.Get(1).Uint()))
- c := strconv.Itoa(int(value.Get(2).Uint()))
- d := strconv.Itoa(int(value.Get(3).Uint()))
- host := strings.Join([]string{a, b, c, d}, ".")
- port := strconv.Itoa(int(p))
-
- return net.JoinHostPort(host, port)
-}
-
func (p *Peer) Start() {
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
@@ -526,7 +573,8 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
}
// Catch up with the connected peer
- p.CatchupWithPeer()
+ // p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
+ p.SyncWithBlocks()
// Set the peer's caps
p.caps = Caps(c.Get(3).Byte())
@@ -553,17 +601,64 @@ func (p *Peer) String() string {
return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps)
}
+func (p *Peer) SyncWithBlocks() {
+ if !p.catchingUp {
+ p.catchingUp = true
+ // FIXME: THIS SHOULD NOT BE NEEDED
+ if p.blocksRequested == 0 {
+ p.blocksRequested = 10
+ }
+ 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(50))
+
+ msg := ethwire.NewMessage(ethwire.MsgGetChainTy, msgInfo)
+ p.QueueMessage(msg)
+ }
+}
-func (p *Peer) CatchupWithPeer() {
+func (p *Peer) CatchupWithPeer(blockHash []byte) {
if !p.catchingUp {
p.catchingUp = true
- msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockChain().CurrentBlock.Hash(), uint64(50)})
+ msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(50)})
p.QueueMessage(msg)
ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4])
+
+ msg = ethwire.NewMessage(ethwire.MsgGetTxsTy, []interface{}{})
+ p.QueueMessage(msg)
+ ethutil.Config.Log.Debugln("Requested transactions")
}
}
func (p *Peer) RlpData() []interface{} {
return []interface{}{p.host, p.port, p.pubkey}
}
+
+func packAddr(address, port string) ([]interface{}, 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 := []interface{}{int32(a), int32(b), int32(c), int32(d)}
+ prt, _ := strconv.Atoi(port)
+
+ return host, uint16(prt)
+}
+
+func unpackAddr(value *ethutil.Value, p uint64) string {
+ a := strconv.Itoa(int(value.Get(0).Uint()))
+ b := strconv.Itoa(int(value.Get(1).Uint()))
+ c := strconv.Itoa(int(value.Get(2).Uint()))
+ d := strconv.Itoa(int(value.Get(3).Uint()))
+ host := strings.Join([]string{a, b, c, d}, ".")
+ port := strconv.Itoa(int(p))
+
+ return net.JoinHostPort(host, port)
+}