diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 20e0a13a2..e5941b165 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -145,27 +145,23 @@ done: return receipts, handled, unhandled, err } -func (sm *StateManager) Process(block *Block, dontReact bool) error { +func (sm *StateManager) Process(block *Block, dontReact bool) (err error) { + // Processing a blocks may never happen simultaneously + sm.mutex.Lock() + defer sm.mutex.Unlock() + + if sm.bc.HasBlock(block.Hash()) { + return nil + } + if !sm.bc.HasBlock(block.PrevHash) { return ParentError(block.PrevHash) } - parent := sm.bc.GetBlock(block.PrevHash) - - return sm.ProcessBlock(parent.State(), parent, block, dontReact) - -} - -// Block processing and validating with a given (temporarily) state -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() - hash := block.Hash() - - if sm.bc.HasBlock(hash) { - return nil - } + var ( + parent = sm.bc.GetBlock(block.PrevHash) + state = parent.State() + ) // Defer the Undo on the Trie. If the block processing happened // we don't want to undo but since undo only happens on dirty @@ -173,17 +169,7 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea // before that. defer state.Reset() - // 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 { - return ParentError(block.PrevHash) - } - - coinbase := state.GetOrNewStateObject(block.Coinbase) - coinbase.SetGasPool(block.CalcGasLimit(parent)) - - // Process the transactions on to current block - receipts, _, _, _ := sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) + receipts, err := sm.ApplyDiff(state, parent, block) defer func() { if err != nil { if len(receipts) == len(block.Receipts()) { @@ -196,6 +182,10 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea } }() + if err != nil { + return err + } + // Block validation if err = sm.ValidateBlock(block); err != nil { statelogger.Errorln("Error validating block:", err) @@ -239,6 +229,17 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea return nil } + +func (sm *StateManager) ApplyDiff(state *State, parent, block *Block) (receipts Receipts, err error) { + coinbase := state.GetOrNewStateObject(block.Coinbase) + coinbase.SetGasPool(block.CalcGasLimit(parent)) + + // Process the transactions on to current block + receipts, _, _, _ = sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) + + return receipts, nil +} + func (sm *StateManager) CalculateTD(block *Block) bool { uncleDiff := new(big.Int) for _, uncle := range block.Uncles { diff --git a/ethchain/state_object.go b/ethchain/state_object.go index f53f47d7e..7d7352af4 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -90,7 +90,14 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) { func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) { addr := ethutil.BigToBytes(num, 256) - //fmt.Printf("sstore %x => %v\n", addr, val) + + // FIXME This should be handled in the Trie it self + if val.BigInt().Cmp(ethutil.Big0) == 0 { + c.state.trie.Delete(string(addr)) + + return + } + c.SetAddr(addr, val) } diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 34ab357a1..2ab681030 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -89,11 +89,12 @@ func (tx *Transaction) Signature(key []byte) []byte { func (tx *Transaction) PublicKey() []byte { hash := tx.Hash() - // If we don't make a copy we will overwrite the existing underlying array - dst := make([]byte, len(tx.r)) - copy(dst, tx.r) + r := make([]byte, 32-len(tx.r)) + s := make([]byte, 32-len(tx.s)) + r = append(r, ethutil.CopyBytes(tx.r)...) + s = append(s, ethutil.CopyBytes(tx.s)...) - sig := append(dst, tx.s...) + sig := append(r, s...) sig = append(sig, tx.v-27) pubkey, _ := secp256k1.RecoverPubkey(hash, sig) @@ -127,6 +128,8 @@ func (tx *Transaction) Sign(privk []byte) error { func (tx *Transaction) RlpData() interface{} { data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} + // TODO Remove prefixing zero's + return append(data, tx.v, tx.r, tx.s) } @@ -150,6 +153,7 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.Value = decoder.Get(4).BigInt() tx.Data = decoder.Get(5).Bytes() tx.v = byte(decoder.Get(6).Uint()) + tx.r = decoder.Get(7).Bytes() tx.s = decoder.Get(8).Bytes() diff --git a/ethchain/vm.go b/ethchain/vm.go index 75bcfd782..3a999f0a4 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -328,21 +328,21 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(base) case LT: require(2) - y, x := stack.Popn() - vm.Printf(" %v < %v", x, y) + x, y := stack.Popn() + vm.Printf(" %v < %v", y, x) // x < y - if x.Cmp(y) < 0 { + if y.Cmp(x) < 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case GT: require(2) - y, x := stack.Popn() - vm.Printf(" %v > %v", x, y) + x, y := stack.Popn() + vm.Printf(" %v > %v", y, x) // x > y - if x.Cmp(y) > 0 { + if y.Cmp(x) > 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) @@ -361,10 +361,10 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case NOT: require(1) x := stack.Pop() - if x.Cmp(ethutil.BigFalse) == 0 { - stack.Push(ethutil.BigTrue) - } else { + if x.Cmp(ethutil.BigFalse) > 0 { stack.Push(ethutil.BigFalse) + } else { + stack.Push(ethutil.BigTrue) } // 0x10 range @@ -523,7 +523,10 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case MLOAD: require(1) offset := stack.Pop() - stack.Push(ethutil.BigD(mem.Get(offset.Int64(), 32))) + val := ethutil.BigD(mem.Get(offset.Int64(), 32)) + stack.Push(val) + + vm.Printf(" => 0x%x", val.Bytes()) case MSTORE: // Store the value at stack top-1 in to memory at location stack top require(2) // Pop value of the stack @@ -542,17 +545,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro require(1) loc := stack.Pop() val := closure.GetMem(loc) + stack.Push(val.BigInt()) - vm.Printf(" {} 0x%x", val) + vm.Printf(" {0x%x} 0x%x", loc.Bytes(), val.Bytes()) case SSTORE: require(2) val, loc := stack.Popn() - - // FIXME This should be handled in the Trie it self - if val.Cmp(big.NewInt(0)) != 0 { - closure.SetStorage(loc, ethutil.NewValue(val)) - } + closure.SetStorage(loc, ethutil.NewValue(val)) // Add the change to manifest vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val) @@ -691,7 +691,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro fallthrough case STOP: // Stop the closure - vm.Printf(" (g) %v", closure.Gas).Endl() + vm.Endl() return closure.Return(nil), nil default: diff --git a/ethereum.go b/ethereum.go index 1de671712..0dde28e51 100644 --- a/ethereum.go +++ b/ethereum.go @@ -115,6 +115,24 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { return ethereum, nil } +// Replay block +func (self *Ethereum) BlockDo(hash []byte) error { + block := self.blockChain.GetBlock(hash) + if block == nil { + return fmt.Errorf("unknown block %x", hash) + } + + parent := self.blockChain.GetBlock(block.PrevHash) + + _, err := self.stateManager.ApplyDiff(parent.State(), parent, block) + if err != nil { + return err + } + + return nil + +} + func (s *Ethereum) Reactor() *ethutil.ReactorEngine { return s.reactor } diff --git a/ethpub/types.go b/ethpub/types.go index 31b92f6ed..352598148 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -244,6 +244,14 @@ func (c *PStateObject) Script() string { return "" } +func (c *PStateObject) HexScript() string { + if c.object != nil { + return ethutil.Hex(c.object.Script()) + } + + return "" +} + type PStorageState struct { StateAddress string Address string diff --git a/ethutil/script.go b/ethutil/script.go index 94e401406..c8b1da51c 100644 --- a/ethutil/script.go +++ b/ethutil/script.go @@ -3,23 +3,35 @@ package ethutil import ( "fmt" "github.com/obscuren/mutan" + "github.com/obscuren/serpent-go" "strings" ) // General compile function -func Compile(script string) ([]byte, error) { - byteCode, errors := mutan.Compile(strings.NewReader(script), false) - if len(errors) > 0 { - var errs string - for _, er := range errors { - if er != nil { - errs += er.Error() - } - } - return nil, fmt.Errorf("%v", errs) - } +func Compile(script string) (ret []byte, err error) { + c := strings.Split(script, "\n")[0] - return byteCode, nil + if c == "#!serpent" { + byteCode, err := serpent.Compile(script) + if err != nil { + return nil, err + } + + return byteCode, nil + } else { + byteCode, errors := mutan.Compile(strings.NewReader(script), false) + if len(errors) > 0 { + var errs string + for _, er := range errors { + if er != nil { + errs += er.Error() + } + } + return nil, fmt.Errorf("%v", errs) + } + + return byteCode, nil + } } func CompileScript(script string) ([]byte, []byte, error) { diff --git a/peer.go b/peer.go index fd8a33d65..18d1da455 100644 --- a/peer.go +++ b/peer.go @@ -141,6 +141,8 @@ type Peer struct { // We use this to give some kind of pingtime to a node, not very accurate, could be improved. pingTime time.Duration pingStartTime time.Time + + lastRequestedBlock *ethchain.Block } func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer { @@ -354,6 +356,11 @@ func (p *Peer) HandleInbound() { // We requested blocks and now we need to make sure we have a common ancestor somewhere in these blocks so we can find // common ground to start syncing from lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1)) + if p.lastRequestedBlock != nil && bytes.Compare(lastBlock.Hash(), p.lastRequestedBlock.Hash()) == 0 { + p.catchingUp = false + continue + } + p.lastRequestedBlock = lastBlock peerlogger.Infof("Last block: %x. Checking if we have it locally.\n", lastBlock.Hash()) for i := msg.Data.Len() - 1; i >= 0; i-- { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))