diff --git a/README.md b/README.md index 4a835afbf..dda1e3a83 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Ethereum Ethereum Go Development package (C) Jeffrey Wilcke Ethereum is currently in its testing phase. The current state is "Proof -of Concept 5.0 RC13". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 5.0 RC14". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). Ethereum Go is split up in several sub packages Please refer to each individual package for more information. diff --git a/ethchain/asm.go b/ethchain/asm.go index c267f9b55..277326ff9 100644 --- a/ethchain/asm.go +++ b/ethchain/asm.go @@ -28,7 +28,7 @@ func Disassemble(script []byte) (asm []string) { if len(data) == 0 { data = []byte{0} } - asm = append(asm, fmt.Sprintf("%#x", data)) + asm = append(asm, fmt.Sprintf("0x%x", data)) pc.Add(pc, big.NewInt(a-1)) } diff --git a/ethchain/deprecated.go b/ethchain/deprecated.go deleted file mode 100644 index 0985fa25d..000000000 --- a/ethchain/deprecated.go +++ /dev/null @@ -1,236 +0,0 @@ -package ethchain - -import ( - "bytes" - "fmt" - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject { - contract := MakeContract(tx, state) - if contract != nil { - state.states[string(tx.CreationAddress())] = contract.state - - return contract - } - - return nil -} - -func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) (ret []byte, gas *big.Int, err error) { - account := state.GetAccount(tx.Sender()) - - err = account.ConvertGas(tx.Gas, tx.GasPrice) - if err != nil { - ethutil.Config.Log.Debugln(err) - return - } - - closure := NewClosure(account, object, script, state, tx.Gas, tx.GasPrice) - vm := NewVm(state, sm, RuntimeVars{ - Origin: account.Address(), - BlockNumber: block.BlockInfo().Number, - PrevHash: block.PrevHash, - Coinbase: block.Coinbase, - Time: block.Time, - Diff: block.Difficulty, - Value: tx.Value, - //Price: tx.GasPrice, - }) - ret, gas, err = closure.Call(vm, tx.Data, nil) - - // Update the account (refunds) - state.UpdateStateObject(account) - state.UpdateStateObject(object) - - return -} - -func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObject, state *State, toContract bool) (gas *big.Int, err error) { - fmt.Printf("state root before update %x\n", state.Root()) - defer func() { - if r := recover(); r != nil { - ethutil.Config.Log.Infoln(r) - err = fmt.Errorf("%v", r) - } - }() - - gas = new(big.Int) - addGas := func(g *big.Int) { gas.Add(gas, g) } - addGas(GasTx) - - // Get the sender - sender := state.GetAccount(tx.Sender()) - - if sender.Nonce != tx.Nonce { - err = NonceError(tx.Nonce, sender.Nonce) - return - } - - sender.Nonce += 1 - defer func() { - //state.UpdateStateObject(sender) - // Notify all subscribers - self.Ethereum.Reactor().Post("newTx:post", tx) - }() - - txTotalBytes := big.NewInt(int64(len(tx.Data))) - //fmt.Println("txTotalBytes", txTotalBytes) - //txTotalBytes.Div(txTotalBytes, ethutil.Big32) - addGas(new(big.Int).Mul(txTotalBytes, GasData)) - - rGas := new(big.Int).Set(gas) - rGas.Mul(gas, tx.GasPrice) - - // Make sure there's enough in the sender's account. Having insufficient - // funds won't invalidate this transaction but simple ignores it. - totAmount := new(big.Int).Add(tx.Value, rGas) - if sender.Amount.Cmp(totAmount) < 0 { - state.UpdateStateObject(sender) - err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) - return - } - - coinbase.BuyGas(gas, tx.GasPrice) - state.UpdateStateObject(coinbase) - fmt.Printf("1. root %x\n", state.Root()) - - // Get the receiver - receiver := state.GetAccount(tx.Recipient) - - // Send Tx to self - if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { - // Subtract the fee - sender.SubAmount(rGas) - } else { - // Subtract the amount from the senders account - sender.SubAmount(totAmount) - state.UpdateStateObject(sender) - fmt.Printf("3. root %x\n", state.Root()) - - // Add the amount to receivers account which should conclude this transaction - receiver.AddAmount(tx.Value) - state.UpdateStateObject(receiver) - fmt.Printf("2. root %x\n", state.Root()) - } - - ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) - - return -} - -func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) { - /* - Applies transactions to the given state and creates new - state objects where needed. - - If said objects needs to be created - run the initialization script provided by the transaction and - assume there's a return value. The return value will be set to - the script section of the state object. - */ - var ( - addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) } - gas = new(big.Int) - script []byte - ) - totalGasUsed = big.NewInt(0) - snapshot := state.Snapshot() - - ca := state.GetAccount(coinbase) - // Apply the transaction to the current state - gas, err = sm.ProcessTransaction(tx, ca, state, false) - addTotalGas(gas) - fmt.Println("gas used by tx", gas) - - if tx.CreatesContract() { - if err == nil { - // Create a new state object and the transaction - // as it's data provider. - contract := sm.MakeStateObject(state, tx) - if contract != nil { - fmt.Println(Disassemble(contract.Init())) - // Evaluate the initialization script - // and use the return value as the - // script section for the state object. - script, gas, err = sm.EvalScript(state, contract.Init(), contract, tx, block) - fmt.Println("gas used by eval", gas) - addTotalGas(gas) - fmt.Println("total =", totalGasUsed) - - fmt.Println("script len =", len(script)) - - if err != nil { - err = fmt.Errorf("[STATE] Error during init script run %v", err) - return - } - contract.script = script - state.UpdateStateObject(contract) - } else { - err = fmt.Errorf("[STATE] Unable to create contract") - } - } else { - err = fmt.Errorf("[STATE] contract creation tx: %v for sender %x", err, tx.Sender()) - } - } else { - // Find the state object at the "recipient" address. If - // there's an object attempt to run the script. - stateObject := state.GetStateObject(tx.Recipient) - if err == nil && stateObject != nil && len(stateObject.Script()) > 0 { - _, gas, err = sm.EvalScript(state, stateObject.Script(), stateObject, tx, block) - addTotalGas(gas) - } - } - - parent := sm.bc.GetBlock(block.PrevHash) - total := new(big.Int).Add(block.GasUsed, totalGasUsed) - limit := block.CalcGasLimit(parent) - if total.Cmp(limit) > 0 { - state.Revert(snapshot) - err = GasLimitError(total, limit) - } - - return -} - -// Apply transactions uses the transaction passed to it and applies them onto -// the current processing state. -func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) { - // Process each transaction/contract - var receipts []*Receipt - var validTxs []*Transaction - var ignoredTxs []*Transaction // Transactions which go over the gasLimit - - totalUsedGas := big.NewInt(0) - - for _, tx := range txs { - usedGas, err := sm.ApplyTransaction(coinbase, state, block, tx) - if err != nil { - if IsNonceErr(err) { - continue - } - if IsGasLimitErr(err) { - ignoredTxs = append(ignoredTxs, tx) - // We need to figure out if we want to do something with thse txes - ethutil.Config.Log.Debugln("Gastlimit:", err) - continue - } - - ethutil.Config.Log.Infoln(err) - } - - accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) - receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} - - receipts = append(receipts, receipt) - validTxs = append(validTxs, tx) - } - - fmt.Println("################# MADE\n", receipts, "\n############################") - - // Update the total gas used for the block (to be mined) - block.GasUsed = totalUsedGas - - return receipts, validTxs -} diff --git a/ethchain/stack.go b/ethchain/stack.go index 37d1f84b9..a9fa2e522 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -2,7 +2,7 @@ package ethchain import ( "fmt" - _ "github.com/ethereum/eth-go/ethutil" + "math" "math/big" ) @@ -118,7 +118,13 @@ func (m *Memory) Resize(size uint64) { } func (m *Memory) Get(offset, size int64) []byte { - return m.store[offset : offset+size] + if len(m.store) > int(offset) { + end := int(math.Min(float64(len(m.store)), float64(offset+size))) + + return m.store[offset:end] + } + + return nil } func (m *Memory) Len() int { diff --git a/ethchain/state.go b/ethchain/state.go index 9a9d0a278..a08dfac83 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -13,8 +13,6 @@ import ( type State struct { // The trie for this structure trie *ethutil.Trie - // Nested states - states map[string]*State stateObjects map[string]*StateObject @@ -23,7 +21,7 @@ type State struct { // Create a new state from a given trie func NewState(trie *ethutil.Trie) *State { - return &State{trie: trie, states: make(map[string]*State), stateObjects: make(map[string]*StateObject), manifest: NewManifest()} + return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} } // Resets the trie and all siblings @@ -38,12 +36,16 @@ func (s *State) Reset() { stateObject.state.Reset() } + + s.Empty() } // Syncs the trie and all siblings func (s *State) Sync() { // Sync all nested states for _, stateObject := range s.stateObjects { + s.UpdateStateObject(stateObject) + if stateObject.state == nil { continue } @@ -52,6 +54,18 @@ func (s *State) Sync() { } s.trie.Sync() + + s.Empty() +} + +func (self *State) Empty() { + self.stateObjects = make(map[string]*StateObject) +} + +func (self *State) Update() { + for _, stateObject := range self.stateObjects { + self.UpdateStateObject(stateObject) + } } // Purges the current trie. @@ -64,6 +78,12 @@ func (s *State) EachStorage(cb ethutil.EachCallback) { it.Each(cb) } +func (self *State) ResetStateObject(stateObject *StateObject) { + delete(self.stateObjects, string(stateObject.Address())) + + stateObject.state.Reset() +} + func (self *State) UpdateStateObject(stateObject *StateObject) { addr := stateObject.Address() @@ -98,13 +118,21 @@ func (self *State) GetStateObject(addr []byte) *StateObject { func (self *State) GetOrNewStateObject(addr []byte) *StateObject { stateObject := self.GetStateObject(addr) if stateObject == nil { - stateObject = NewStateObject(addr) - self.stateObjects[string(addr)] = stateObject + stateObject = self.NewStateObject(addr) } return stateObject } +func (self *State) NewStateObject(addr []byte) *StateObject { + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(+) %x\n", addr) + + stateObject := NewStateObject(addr) + self.stateObjects[string(addr)] = stateObject + + return stateObject +} + func (self *State) GetAccount(addr []byte) *StateObject { return self.GetOrNewStateObject(addr) } @@ -126,13 +154,10 @@ func (self *State) Copy() *State { return nil } -func (s *State) Snapshot() *State { - return s.Copy() -} - -func (s *State) Revert(snapshot *State) { - s.trie = snapshot.trie - s.states = snapshot.states +func (self *State) Set(state *State) { + //s.trie = snapshot.trie + //s.stateObjects = snapshot.stateObjects + self = state } func (s *State) Put(key, object []byte) { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 36bb14846..36ba1731c 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -127,6 +127,9 @@ done: // Notify all subscribers self.Ethereum.Reactor().Post("newTx:post", tx) + // Update the state with pending changes + state.Update() + txGas.Sub(txGas, st.gas) accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas)) receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} @@ -135,8 +138,6 @@ done: handled = append(handled, tx) } - fmt.Println("################# MADE\n", receipts, "\n############################") - parent.GasUsed = totalUsedGas return receipts, handled, unhandled, err @@ -154,7 +155,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) error { } // Block processing and validating with a given (temporarily) state -func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontReact bool) error { +func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontReact bool) (err error) { // Processing a blocks may never happen simultaneously sm.mutex.Lock() defer sm.mutex.Unlock() @@ -175,30 +176,40 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil { return ParentError(block.PrevHash) } - fmt.Println(block.Receipts()) coinbase := state.GetOrNewStateObject(block.Coinbase) coinbase.SetGasPool(block.CalcGasLimit(parent)) // Process the transactions on to current block - //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) - sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) + receipts, _, _, _ := sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) + defer func() { + if err != nil { + if len(receipts) == len(block.Receipts()) { + for i, receipt := range block.Receipts() { + ethutil.Config.Log.Debugf("diff (r) %v ~ %x <=> (c) %v ~ %x (%x)\n", receipt.CumulativeGasUsed, receipt.PostState[0:4], receipts[i].CumulativeGasUsed, receipts[i].PostState[0:4], receipt.Tx.Hash()) + } + } else { + ethutil.Config.Log.Debugln("Unable to print receipt diff. Length didn't match", len(receipts), "for", len(block.Receipts())) + } + } + }() // Block validation - if err := sm.ValidateBlock(block); err != nil { + 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(state, block); err != nil { + if err = sm.AccumelateRewards(state, block); err != nil { fmt.Println("[SM] Error accumulating reward", err) return err } if !block.State().Cmp(state) { - return fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root) + err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root) + return } // Calculate the new total difficulty and sync back to the db @@ -270,10 +281,12 @@ func (sm *StateManager) ValidateBlock(block *Block) error { return ValidationError("Block timestamp less then prev block %v", diff) } + /* XXX // New blocks must be within the 15 minute range of the last block. if diff > int64(15*time.Minute) { return ValidationError("Block is too far in the future of last block (> 15 minutes)") } + */ // Verify the nonce of the block. Return an error if it's not valid if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) { diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 1445bcd82..5b64c3b37 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -4,8 +4,15 @@ import ( "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" + "strings" ) +type Code []byte + +func (self Code) String() string { + return strings.Join(Disassemble(self), " ") +} + type StateObject struct { // Address of the object address []byte @@ -15,8 +22,8 @@ type StateObject struct { Nonce uint64 // Contract related attributes state *State - script []byte - initScript []byte + script Code + initScript Code // Total gas pool is the total amount of gas currently // left if this object is the coinbase. Gas is directly @@ -30,12 +37,9 @@ func MakeContract(tx *Transaction, state *State) *StateObject { if tx.IsContract() { addr := tx.CreationAddress() - value := tx.Value - contract := NewContract(addr, value, ZeroHash256) - + contract := state.NewStateObject(addr) contract.initScript = tx.Data - - state.UpdateStateObject(contract) + contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, "")) return contract } @@ -44,7 +48,7 @@ func MakeContract(tx *Transaction, state *State) *StateObject { } func NewStateObject(addr []byte) *StateObject { - return &StateObject{address: addr, Amount: new(big.Int)} + return &StateObject{address: addr, Amount: new(big.Int), gasPool: new(big.Int)} } func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { @@ -120,13 +124,13 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { func (c *StateObject) AddAmount(amount *big.Int) { c.SetAmount(new(big.Int).Add(c.Amount, amount)) - ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) } func (c *StateObject) SubAmount(amount *big.Int) { c.SetAmount(new(big.Int).Sub(c.Amount, amount)) - ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) } func (c *StateObject) SetAmount(amount *big.Int) { @@ -172,6 +176,26 @@ func (self *StateObject) RefundGas(gas, price *big.Int) { self.Amount.Sub(self.Amount, rGas) } +func (self *StateObject) Copy() *StateObject { + stateObject := NewStateObject(self.Address()) + stateObject.Amount.Set(self.Amount) + stateObject.ScriptHash = ethutil.CopyBytes(self.ScriptHash) + stateObject.Nonce = self.Nonce + if self.state != nil { + stateObject.state = self.state.Copy() + } + stateObject.script = ethutil.CopyBytes(self.script) + stateObject.initScript = ethutil.CopyBytes(self.initScript) + //stateObject.gasPool.Set(self.gasPool) + + return self +} + +func (self *StateObject) Set(stateObject *StateObject) { + self = stateObject +} + +/* func (self *StateObject) Copy() *StateObject { stCopy := &StateObject{} stCopy.address = make([]byte, len(self.address)) @@ -190,6 +214,7 @@ func (self *StateObject) Copy() *StateObject { return stCopy } +*/ // Returns the address of the contract/account func (c *StateObject) Address() []byte { @@ -197,12 +222,12 @@ func (c *StateObject) Address() []byte { } // Returns the main script body -func (c *StateObject) Script() []byte { +func (c *StateObject) Script() Code { return c.script } // Returns the initialization script -func (c *StateObject) Init() []byte { +func (c *StateObject) Init() Code { return c.initScript } diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 25efd64cc..1f5b4f959 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -23,17 +23,19 @@ import ( * 6) Derive new state root */ type StateTransition struct { - coinbase []byte - tx *Transaction - gas *big.Int - state *State - block *Block + coinbase, receiver []byte + tx *Transaction + gas, gasPrice *big.Int + value *big.Int + data []byte + state *State + block *Block cb, rec, sen *StateObject } func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition { - return &StateTransition{coinbase.Address(), tx, new(big.Int), state, block, coinbase, nil, nil} + return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil} } func (self *StateTransition) Coinbase() *StateObject { @@ -53,7 +55,7 @@ func (self *StateTransition) Sender() *StateObject { return self.sen } func (self *StateTransition) Receiver() *StateObject { - if self.tx.CreatesContract() { + if self.tx != nil && self.tx.CreatesContract() { return nil } @@ -67,13 +69,8 @@ func (self *StateTransition) Receiver() *StateObject { func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject { contract := MakeContract(tx, state) - if contract != nil { - state.states[string(tx.CreationAddress())] = contract.state - return contract - } - - return nil + return contract } func (self *StateTransition) UseGas(amount *big.Int) error { @@ -94,7 +91,7 @@ func (self *StateTransition) BuyGas() error { sender := self.Sender() if sender.Amount.Cmp(self.tx.GasValue()) < 0 { - return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), self.tx.Value) + return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Amount) } coinbase := self.Coinbase() @@ -102,7 +99,6 @@ func (self *StateTransition) BuyGas() error { if err != nil { return err } - //self.state.UpdateStateObject(coinbase) self.AddGas(self.tx.Gas) sender.SubAmount(self.tx.GasValue()) @@ -119,22 +115,10 @@ func (self *StateTransition) RefundGas() { sender.AddAmount(remaining) } -func (self *StateTransition) TransitionState() (err error) { - //snapshot := st.state.Snapshot() - - /* - defer func() { - if r := recover(); r != nil { - ethutil.Config.Log.Infoln(r) - err = fmt.Errorf("state transition err %v", r) - } - }() - */ - +func (self *StateTransition) preCheck() (err error) { var ( - tx = self.tx - sender = self.Sender() - receiver *StateObject + tx = self.tx + sender = self.Sender() ) // Make sure this transaction's nonce is correct @@ -147,38 +131,49 @@ func (self *StateTransition) TransitionState() (err error) { return err } + return nil +} + +func (self *StateTransition) TransitionState() (err error) { + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(~) %x\n", self.tx.Hash()) + + /* + defer func() { + if r := recover(); r != nil { + ethutil.Config.Log.Infoln(r) + err = fmt.Errorf("state transition err %v", r) + } + }() + */ + // XXX Transactions after this point are considered valid. + if err = self.preCheck(); err != nil { + return + } - defer func() { - self.RefundGas() + var ( + tx = self.tx + sender = self.Sender() + receiver *StateObject + ) - if sender != nil { - self.state.UpdateStateObject(sender) - } - - if receiver != nil { - self.state.UpdateStateObject(receiver) - } - - self.state.UpdateStateObject(self.Coinbase()) - }() + defer self.RefundGas() // Increment the nonce for the next transaction sender.Nonce += 1 - // Get the receiver (TODO fix this, if coinbase is the receiver we need to save/retrieve) receiver = self.Receiver() // Transaction gas if err = self.UseGas(GasTx); err != nil { - return err + return } // Pay data gas - dataPrice := big.NewInt(int64(len(tx.Data))) + dataPrice := big.NewInt(int64(len(self.data))) dataPrice.Mul(dataPrice, GasData) if err = self.UseGas(dataPrice); err != nil { - return err + return } // If the receiver is nil it's a contract (\0*32). @@ -186,75 +181,83 @@ func (self *StateTransition) TransitionState() (err error) { // Create a new state object for the contract receiver = self.MakeStateObject(self.state, tx) if receiver == nil { - return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx) + return fmt.Errorf("Unable to create contract") } } // Transfer value from sender to receiver if err = self.transferValue(sender, receiver); err != nil { - return err + return } // Process the init code and create 'valid' contract - if tx.CreatesContract() { + if IsContractAddr(self.receiver) { // Evaluate the initialization script // and use the return value as the // script section for the state object. - //script, gas, err = sm.Eval(state, contract.Init(), contract, tx, block) - code, err := self.Eval(receiver.Init(), receiver) - if err != nil { - return fmt.Errorf("Error during init script run %v", err) + self.data = nil + + code, err, deepErr := self.Eval(receiver.Init(), receiver) + if err != nil || deepErr { + self.state.ResetStateObject(receiver) + + return fmt.Errorf("Error during init script run %v (deepErr = %v)", err, deepErr) } receiver.script = code } else { if len(receiver.Script()) > 0 { - _, err := self.Eval(receiver.Script(), receiver) + var deepErr bool + _, err, deepErr = self.Eval(receiver.Script(), receiver) if err != nil { - return fmt.Errorf("Error during code execution %v", err) + self.state.ResetStateObject(receiver) + + return fmt.Errorf("Error during code execution %v (deepErr = %v)", err, deepErr) } } } - return nil + return } func (self *StateTransition) transferValue(sender, receiver *StateObject) error { - if sender.Amount.Cmp(self.tx.Value) < 0 { - return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount) + if sender.Amount.Cmp(self.value) < 0 { + return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount) } - if self.tx.Value.Cmp(ethutil.Big0) > 0 { - // Subtract the amount from the senders account - sender.SubAmount(self.tx.Value) - // Add the amount to receivers account which should conclude this transaction - receiver.AddAmount(self.tx.Value) + //if self.value.Cmp(ethutil.Big0) > 0 { + // Subtract the amount from the senders account + sender.SubAmount(self.value) + // Add the amount to receivers account which should conclude this transaction + receiver.AddAmount(self.value) - ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], self.tx.Value, self.tx.Hash()) - } + //ethutil.Config.Log.Debugf("%x => %x (%v)\n", sender.Address()[:4], receiver.Address()[:4], self.value) + //} return nil } -func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error) { +func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error, deepErr bool) { var ( - tx = self.tx block = self.block initiator = self.Sender() state = self.state ) - closure := NewClosure(initiator, context, script, state, self.gas, tx.GasPrice) + closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice) vm := NewVm(state, nil, RuntimeVars{ Origin: initiator.Address(), - BlockNumber: block.BlockInfo().Number, + Block: block, + BlockNumber: block.Number, PrevHash: block.PrevHash, Coinbase: block.Coinbase, Time: block.Time, Diff: block.Difficulty, - Value: tx.Value, + Value: self.value, }) - ret, _, err = closure.Call(vm, tx.Data, nil) + vm.Verbose = true + ret, _, err = closure.Call(vm, self.data, nil) + deepErr = vm.err != nil return } diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 3d52e4f73..34ab357a1 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -10,6 +10,10 @@ import ( var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +func IsContractAddr(addr []byte) bool { + return bytes.Compare(addr, ContractAddr) == 0 +} + type Transaction struct { Nonce uint64 Recipient []byte @@ -65,7 +69,7 @@ func (tx *Transaction) CreatesContract() bool { return tx.contractCreation } -/* Depricated */ +/* Deprecated */ func (tx *Transaction) IsContract() bool { return tx.CreatesContract() } @@ -149,7 +153,7 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.r = decoder.Get(7).Bytes() tx.s = decoder.Get(8).Bytes() - if bytes.Compare(tx.Recipient, ContractAddr) == 0 { + if IsContractAddr(tx.Recipient) { tx.contractCreation = true } } diff --git a/ethchain/types.go b/ethchain/types.go index d89fad147..9e7269f74 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -166,6 +166,7 @@ var opCodeToString = map[OpCode]string{ GASLIMIT: "GASLIMIT", // 0x50 range - 'storage' and execution + POP: "POP", DUP: "DUP", SWAP: "SWAP", MLOAD: "MLOAD", @@ -226,7 +227,7 @@ var opCodeToString = map[OpCode]string{ func (o OpCode) String() string { str := opCodeToString[o] if len(str) == 0 { - return fmt.Sprintf("Missing opcode %#x", int(o)) + return fmt.Sprintf("Missing opcode 0x%x", int(o)) } return str diff --git a/ethchain/vm.go b/ethchain/vm.go index 2ba0e2ef3..fc3c37dc1 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -1,12 +1,9 @@ package ethchain import ( - _ "bytes" "fmt" "github.com/ethereum/eth-go/ethutil" - _ "github.com/obscuren/secp256k1-go" "math" - _ "math" "math/big" ) @@ -45,11 +42,18 @@ type Vm struct { state *State stateManager *StateManager + + Verbose bool + + logStr string + + err error } type RuntimeVars struct { Origin []byte - BlockNumber uint64 + Block *Block + BlockNumber *big.Int PrevHash []byte Coinbase []byte Time int64 @@ -58,6 +62,23 @@ type RuntimeVars struct { Value *big.Int } +func (self *Vm) Printf(format string, v ...interface{}) *Vm { + if self.Verbose { + self.logStr += fmt.Sprintf(format, v...) + } + + return self +} + +func (self *Vm) Endl() *Vm { + if self.Verbose { + ethutil.Config.Log.Infoln(self.logStr) + self.logStr = "" + } + + return self +} + func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm { return &Vm{vars: vars, state: state, stateManager: stateManager} } @@ -69,14 +90,14 @@ 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*/ { + if r := recover(); r != nil { 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()) + ethutil.Config.Log.Debugf("[VM] (~) %x gas: %v (d) %x\n", closure.object.Address(), closure.Gas, closure.Args) // Memory for the current closure mem := &Memory{} @@ -95,10 +116,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro step := 0 prevStep := 0 - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("# op\n") - } - for { prevStep = step // The base for all big integer arithmetic @@ -109,13 +126,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro 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()) - } gas := new(big.Int) addStepGasUsage := func(amount *big.Int) { - gas.Add(gas, amount) + if amount.Cmp(ethutil.Big0) >= 0 { + gas.Add(gas, amount) + } } addStepGasUsage(GasStep) @@ -123,7 +139,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro var newMemSize uint64 = 0 switch op { case STOP: + gas.Set(ethutil.Big0) case SUICIDE: + gas.Set(ethutil.Big0) case SLOAD: gas.Set(GasSLoad) case SSTORE: @@ -169,7 +187,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case CALL: require(7) gas.Set(GasCall) - addStepGasUsage(stack.data[stack.Len()-2]) + addStepGasUsage(stack.data[stack.Len()-1]) x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64() y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64() @@ -189,11 +207,16 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } if !closure.UseGas(gas) { - ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas) + err := fmt.Errorf("Insufficient gas for %v. req %v has %v", op, gas, closure.Gas) - return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) + closure.UseGas(closure.Gas) + + return closure.Return(nil), err } + vm.Printf("(pc) %-3d -o- %-14s", pc, op.String()) + vm.Printf(" (g) %-3v (%v)", gas, closure.Gas) + mem.Resize(newMemSize) switch op { @@ -204,29 +227,41 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case ADD: require(2) x, y := stack.Popn() - // (x + y) % 2 ** 256 - base.Add(x, y) + vm.Printf(" %v + %v", y, x) + + base.Add(y, x) + + vm.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) case SUB: require(2) x, y := stack.Popn() - // (x - y) % 2 ** 256 - base.Sub(x, y) + vm.Printf(" %v - %v", y, x) + + base.Sub(y, x) + + vm.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) case MUL: require(2) x, y := stack.Popn() - // (x * y) % 2 ** 256 - base.Mul(x, y) + vm.Printf(" %v * %v", y, x) + + base.Mul(y, x) + + vm.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) case DIV: require(2) x, y := stack.Popn() - // floor(x / y) - base.Div(x, y) + vm.Printf(" %v / %v", y, x) + + base.Div(y, x) + + vm.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) case SDIV: @@ -249,7 +284,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case MOD: require(2) x, y := stack.Popn() - base.Mod(x, y) + + vm.Printf(" %v %% %v", y, x) + + base.Mod(y, x) + + vm.Printf(" = %v", base) stack.Push(base) case SMOD: require(2) @@ -271,7 +311,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case EXP: require(2) x, y := stack.Popn() - base.Exp(x, y, Pow256) + + vm.Printf(" %v ** %v", y, x) + + base.Exp(y, x, Pow256) + + vm.Printf(" = %v", base) stack.Push(base) case NEG: @@ -280,7 +325,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(base) case LT: require(2) - x, y := stack.Popn() + y, x := stack.Popn() + vm.Printf(" %v < %v", x, y) // x < y if x.Cmp(y) < 0 { stack.Push(ethutil.BigTrue) @@ -289,7 +335,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } case GT: require(2) - x, y := stack.Popn() + y, x := stack.Popn() + vm.Printf(" %v > %v", x, y) + // x > y if x.Cmp(y) > 0 { stack.Push(ethutil.BigTrue) @@ -299,6 +347,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case EQ: require(2) x, y := stack.Popn() + vm.Printf(" %v == %v", y, x) + // x == y if x.Cmp(y) == 0 { stack.Push(ethutil.BigTrue) @@ -318,24 +368,21 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case AND: 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) - } + vm.Printf(" %v & %v", y, x) + stack.Push(base.And(y, x)) case OR: 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) - } + vm.Printf(" %v | %v", y, x) + + stack.Push(base.Or(y, x)) case XOR: require(2) x, y := stack.Popn() - stack.Push(base.Xor(x, y)) + vm.Printf(" %v ^ %v", y, x) + + stack.Push(base.Xor(y, x)) case BYTE: require(2) val, th := stack.Popn() @@ -360,25 +407,35 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case ORIGIN: stack.Push(ethutil.BigD(vm.vars.Origin)) case CALLER: - stack.Push(ethutil.BigD(closure.caller.Address())) + caller := closure.caller.Address() + stack.Push(ethutil.BigD(caller)) + + vm.Printf(" => %x", caller) case CALLVALUE: stack.Push(vm.vars.Value) case CALLDATALOAD: require(1) offset := stack.Pop().Int64() - var data []byte - if len(closure.Args) >= int(offset+32) { - data = closure.Args[offset : offset+32] - } else { - data = []byte{0} + data := make([]byte, 32) + if len(closure.Args) >= int(offset) { + l := int64(math.Min(float64(offset+32), float64(len(closure.Args)))) + + copy(data, closure.Args[offset:l]) } + vm.Printf(" => 0x%x", data) + stack.Push(ethutil.BigD(data)) case CALLDATASIZE: - stack.Push(big.NewInt(int64(len(closure.Args)))) + l := int64(len(closure.Args)) + stack.Push(big.NewInt(l)) + + vm.Printf(" => %d", l) case CALLDATACOPY: + panic("not implemented") case CODESIZE: + stack.Push(big.NewInt(int64(len(closure.Script)))) case CODECOPY: var ( size = int64(len(closure.Script)) @@ -408,7 +465,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case TIMESTAMP: stack.Push(big.NewInt(vm.vars.Time)) case NUMBER: - stack.Push(big.NewInt(int64(vm.vars.BlockNumber))) + stack.Push(vm.vars.BlockNumber) case DIFFICULTY: stack.Push(vm.vars.Diff) case GASLIMIT: @@ -426,12 +483,16 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro pc.Add(pc, a.Sub(a, big.NewInt(1))) step += int(op) - int(PUSH1) + 1 + + vm.Printf(" => 0x%x", data.Bytes()) case POP: require(1) stack.Pop() case DUP: require(1) stack.Push(stack.Peek()) + + vm.Printf(" => 0x%x", stack.Peek().Bytes()) case SWAP: require(2) x, y := stack.Popn() @@ -446,38 +507,53 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Pop value of the stack val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) + + vm.Printf(" => 0x%x", val) case MSTORE8: require(2) val, mStart := stack.Popn() base.And(val, new(big.Int).SetInt64(0xff)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) + + vm.Printf(" => 0x%x", val) case SLOAD: require(1) loc := stack.Pop() val := closure.GetMem(loc) - //fmt.Println("get", val.BigInt(), "@", loc) stack.Push(val.BigInt()) + + vm.Printf(" {} 0x%x", val) case SSTORE: require(2) val, loc := stack.Popn() - //fmt.Println("storing", val, "@", loc) - closure.SetStorage(loc, ethutil.NewValue(val)) + + // FIXME This should be handled in the Trie it self + if val.Cmp(big.NewInt(0)) != 0 { + closure.SetStorage(loc, ethutil.NewValue(val)) + } // Add the change to manifest vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val) + + vm.Printf(" {0x%x} 0x%x", loc, val) case JUMP: require(1) pc = stack.Pop() // Reduce pc by one because of the increment that's at the end of this for loop - //pc.Sub(pc, ethutil.Big1) + vm.Printf(" ~> %v", pc).Endl() + continue case JUMPI: require(2) cond, pos := stack.Popn() - if cond.Cmp(ethutil.BigTrue) == 0 { + if cond.Cmp(ethutil.BigTrue) >= 0 { pc = pos - //pc.Sub(pc, ethutil.Big1) + + vm.Printf(" ~> %v (t)", pc).Endl() + continue + } else { + vm.Printf(" (f)") } case PC: stack.Push(pc) @@ -494,7 +570,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Snapshot the current stack so we are able to // revert back to it later. - snapshot := vm.state.Snapshot() + snapshot := vm.state.Copy() // Generate a new address addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N()) @@ -522,19 +598,18 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(ethutil.BigFalse) // Revert the state as it was before. - vm.state.Revert(snapshot) + vm.state.Set(snapshot) } else { stack.Push(ethutil.BigD(addr)) - - vm.state.UpdateStateObject(contract) } case CALL: - // TODO RE-WRITE require(7) - // Closure addr - addr := stack.Pop() + + vm.Endl() + + gas := stack.Pop() // Pop gas and value of the stack. - gas, value := stack.Popn() + value, addr := stack.Popn() // Pop input size and offset inSize, inOffset := stack.Popn() // Pop return size and offset @@ -543,39 +618,34 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) - snapshot := vm.state.Snapshot() - - closure.object.Nonce += 1 if closure.object.Amount.Cmp(value) < 0 { ethutil.Config.Log.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) stack.Push(ethutil.BigFalse) } else { - // Fetch the contract which will serve as the closure body - contract := vm.state.GetStateObject(addr.Bytes()) + snapshot := vm.state.Copy() - if contract != nil { - // Add the value to the state object - contract.AddAmount(value) + stateObject := vm.state.GetOrNewStateObject(addr.Bytes()) - // Create a new callable closure - closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price) - // 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 - vm.state.Revert(snapshot) - } else { - stack.Push(ethutil.BigTrue) + closure.object.SubAmount(value) + // Add the value to the state object + stateObject.AddAmount(value) - vm.state.UpdateStateObject(contract) - - mem.Set(retOffset.Int64(), retSize.Int64(), ret) - } - } else { - ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes()) + // Create a new callable closure + closure := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price) + // Executer the closure and get the return value (if any) + ret, _, err := closure.Call(vm, args, hook) + if err != nil { stack.Push(ethutil.BigFalse) + + ethutil.Config.Log.Debugf("Closure execution failed. %v\n", err) + + vm.err = err + vm.state.Set(snapshot) + } else { + stack.Push(ethutil.BigTrue) + + mem.Set(retOffset.Int64(), retSize.Int64(), ret) } } case RETURN: @@ -583,18 +653,24 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro size, offset := stack.Popn() ret := mem.Get(offset.Int64(), size.Int64()) + vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl() + return closure.Return(ret), nil case SUICIDE: require(1) receiver := vm.state.GetAccount(stack.Pop().Bytes()) receiver.AddAmount(closure.object.Amount) - vm.state.UpdateStateObject(receiver) - closure.object.state.Purge() + trie := closure.object.state.trie + trie.NewIterator().Each(func(key string, v *ethutil.Value) { + trie.Delete(key) + }) fallthrough case STOP: // Stop the closure + vm.Printf(" (g) %v", closure.Gas).Endl() + return closure.Return(nil), nil default: ethutil.Config.Log.Debugf("Invalid opcode %x\n", op) @@ -604,6 +680,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro pc.Add(pc, ethutil.Big1) + vm.Endl() + if hook != nil { if !hook(prevStep, op, mem, stack, closure.Object()) { return nil, nil diff --git a/ethereum.go b/ethereum.go index d9281cd57..a6cb78b1f 100644 --- a/ethereum.go +++ b/ethereum.go @@ -149,7 +149,9 @@ func (s *Ethereum) IsUpToDate() bool { }) return upToDate } - +func (s *Ethereum) PushPeer(peer *Peer) { + s.peers.PushBack(peer) +} func (s *Ethereum) IsListening() bool { return s.listening } @@ -159,14 +161,11 @@ func (s *Ethereum) AddPeer(conn net.Conn) { if peer != nil { if s.peers.Len() < s.MaxPeers { - s.peers.PushBack(peer) peer.Start() } else { ethutil.Config.Log.Debugf("[SERV] Max connected peers reached. Not adding incoming peer.") } } - - s.reactor.Post("peerList", s.peers) } func (s *Ethereum) ProcessPeerList(addrs []string) { @@ -233,12 +232,7 @@ func (s *Ethereum) ConnectToPeer(addr string) error { return nil } - peer := NewOutboundPeer(addr, s, s.serverCaps) - - s.peers.PushBack(peer) - - ethutil.Config.Log.Infof("[SERV] Adding peer (%s) %d / %d\n", addr, s.peers.Len(), s.MaxPeers) - s.reactor.Post("peerList", s.peers) + NewOutboundPeer(addr, s, s.serverCaps) } return nil diff --git a/ethminer/miner.go b/ethminer/miner.go index 1ef9ca229..4343b4333 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -154,6 +154,8 @@ func (self *Miner) mineNewBlock() { // Accumulate the rewards included for this block stateManager.AccumelateRewards(self.block.State(), self.block) + self.block.State().Update() + ethutil.Config.Log.Infoln("[MINER] Mining on block. Includes", len(self.txs), "transactions") // Find a valid nonce diff --git a/ethpub/pub.go b/ethpub/pub.go index 20ba79d0b..b475453af 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -170,11 +170,6 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, sc tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script) } else { - // Just in case it was submitted as a 0x prefixed string - if len(scriptStr) > 0 && scriptStr[0:2] == "0x" { - scriptStr = scriptStr[2:len(scriptStr)] - } - data := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { slice := strings.Split(s, "\n") for _, dataItem := range slice { diff --git a/ethpub/types.go b/ethpub/types.go index a76421007..31b92f6ed 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -104,16 +104,17 @@ type PTx struct { func NewPTx(tx *ethchain.Transaction) *PTx { hash := hex.EncodeToString(tx.Hash()) receiver := hex.EncodeToString(tx.Recipient) - - if receiver == "" { + if receiver == "0000000000000000000000000000000000000000" { receiver = hex.EncodeToString(tx.CreationAddress()) } sender := hex.EncodeToString(tx.Sender()) createsContract := tx.CreatesContract() - data := string(tx.Data) + var data string if tx.CreatesContract() { data = strings.Join(ethchain.Disassemble(tx.Data), "\n") + } else { + data = hex.EncodeToString(tx.Data) } return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)} diff --git a/ethutil/bytes.go b/ethutil/bytes.go index bd0df68ec..5e3ee4a6f 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "fmt" "math/big" + "strings" ) // Number to bytes @@ -91,7 +92,7 @@ func IsHex(str string) bool { } func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) { - if len(str) > 1 && str[0:2] == "0x" { + if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") { ret = FromHex(str[2:]) } else { ret = cb(str) diff --git a/ethutil/config.go b/ethutil/config.go index a24c39bfe..a573e108b 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -75,7 +75,7 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s if Config == nil { path := ApplicationFolder(base) - Config = &config{ExecPath: path, Debug: true, Ver: "0.5.13"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.5.14"} Config.conf = g Config.Identifier = id Config.Log = NewLogger(logTypes, LogLevelDebug) diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index c89f2fbb7..2937b1525 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -173,12 +173,21 @@ func TestTriePurge(t *testing.T) { func TestTrieIt(t *testing.T) { _, trie := New() - trie.Update("c", LONG_WORD) - trie.Update("ca", LONG_WORD) - trie.Update("cat", LONG_WORD) - it := trie.NewIterator() - it.Each(func(key string, node *Value) { - fmt.Println(key, ":", node.Str()) - }) + data := [][]string{ + {"do", "verb"}, + {"ether", "wookiedoo"}, + {"horse", "stallion"}, + {"shaman", "horse"}, + {"doge", "coin"}, + {"ether", ""}, + {"dog", "puppy"}, + {"shaman", ""}, + } + + for _, item := range data { + trie.Update(item[0], item[1]) + } + + fmt.Printf("root %x", trie.Root) } diff --git a/ethutil/value.go b/ethutil/value.go index c86c24a7a..ddd864d8a 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -114,6 +114,8 @@ func (val *Value) Str() string { func (val *Value) Bytes() []byte { if a, ok := val.Val.([]byte); ok { return a + } else if s, ok := val.Val.(byte); ok { + return []byte{s} } return []byte{} diff --git a/peer.go b/peer.go index 07c93e5b4..2ece9b359 100644 --- a/peer.go +++ b/peer.go @@ -2,6 +2,7 @@ package eth import ( "bytes" + "container/list" "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" @@ -124,6 +125,7 @@ type Peer struct { port uint16 caps Caps + // This peer's public key pubkey []byte // Indicated whether the node is catching up or not @@ -171,7 +173,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { // Set up the connection in another goroutine so we don't block the main thread go func() { - conn, err := net.DialTimeout("tcp", addr, 30*time.Second) + conn, err := net.DialTimeout("tcp", addr, 10*time.Second) if err != nil { ethutil.Config.Log.Debugln("Connection to peer failed", err) @@ -614,6 +616,30 @@ func (p *Peer) pushPeers() { func (p *Peer) handleHandshake(msg *ethwire.Msg) { c := msg.Data + // Set pubkey + p.pubkey = c.Get(5).Bytes() + + if p.pubkey == nil { + //ethutil.Config.Log.Debugln("Pubkey required, not supplied in handshake.") + p.Stop() + return + } + + usedPub := 0 + // This peer is already added to the peerlist so we expect to find a double pubkey at least once + + eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) { + if bytes.Compare(p.pubkey, peer.pubkey) == 0 { + usedPub++ + } + }) + + if usedPub > 0 { + //ethutil.Config.Log.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey) + p.Stop() + return + } + if c.Get(0).Uint() != ProtocolVersion { ethutil.Config.Log.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint()) p.Stop() @@ -625,7 +651,6 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { // If this is an inbound connection send an ack back if p.inbound { - p.pubkey = c.Get(5).Bytes() p.port = uint16(c.Get(4).Uint()) // Self connect detection @@ -647,6 +672,11 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { p.SetVersion(c.Get(2).Str()) } + p.ethereum.PushPeer(p) + p.ethereum.reactor.Post("peerList", p.ethereum.Peers()) + + ethutil.Config.Log.Infof("[SERV] Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers) + // Catch up with the connected peer if !p.ethereum.IsUpToDate() { ethutil.Config.Log.Debugln("Already syncing up with a peer; sleeping")