aboutsummaryrefslogtreecommitdiffstats
path: root/ethchain
diff options
context:
space:
mode:
Diffstat (limited to 'ethchain')
-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
21 files changed, 1529 insertions, 871 deletions
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)
}