diff options
Diffstat (limited to 'ethchain')
-rw-r--r-- | ethchain/address.go | 76 | ||||
-rw-r--r-- | ethchain/address_test.go | 8 | ||||
-rw-r--r-- | ethchain/asm.go | 59 | ||||
-rw-r--r-- | ethchain/block.go | 35 | ||||
-rw-r--r-- | ethchain/block_chain.go | 137 | ||||
-rw-r--r-- | ethchain/block_chain_test.go | 115 | ||||
-rw-r--r-- | ethchain/closure.go | 64 | ||||
-rw-r--r-- | ethchain/contract.go | 94 | ||||
-rw-r--r-- | ethchain/dagger.go | 31 | ||||
-rw-r--r-- | ethchain/error.go | 23 | ||||
-rw-r--r-- | ethchain/keypair.go | 17 | ||||
-rw-r--r-- | ethchain/stack.go | 194 | ||||
-rw-r--r-- | ethchain/state.go | 92 | ||||
-rw-r--r-- | ethchain/state_manager.go | 214 | ||||
-rw-r--r-- | ethchain/state_object.go | 194 | ||||
-rw-r--r-- | ethchain/transaction.go | 97 | ||||
-rw-r--r-- | ethchain/transaction_pool.go | 47 | ||||
-rw-r--r-- | ethchain/transaction_test.go | 53 | ||||
-rw-r--r-- | ethchain/types.go | 230 | ||||
-rw-r--r-- | ethchain/vm.go | 426 | ||||
-rw-r--r-- | ethchain/vm_test.go | 194 |
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) } |