From 81245473486dd680b7121d4b227ca8a57d07b4b1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 13 Jun 2014 16:06:27 +0200 Subject: [PATCH] Moving a head closer to interop --- ethchain/closure.go | 26 ++++++++++++++------------ ethchain/state_object.go | 8 +++++--- ethchain/state_transition.go | 32 ++++++++++++++++++++++++-------- ethchain/vm.go | 31 ++++++++++++++++++------------- ethutil/common.go | 2 +- peer.go | 4 ++-- 6 files changed, 64 insertions(+), 39 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 5c9c3e47c..32b297e90 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -17,7 +17,7 @@ type ClosureRef interface { // Basic inline closure object which implement the 'closure' interface type Closure struct { - callee ClosureRef + caller ClosureRef object *StateObject Script []byte State *State @@ -28,12 +28,14 @@ type Closure struct { } // Create a new closure for the given data items -func NewClosure(callee ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure { - c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil} +func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure { + c := &Closure{caller: caller, object: object, Script: script, State: state, Args: nil} - // In most cases gas, price and value are pointers to transaction objects + // Gas should be a pointer so it can safely be reduced through the run + // This pointer will be off the state transition + c.Gas = gas //new(big.Int).Set(gas) + // In most cases 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.UsedGas = new(big.Int) @@ -83,11 +85,11 @@ func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) ([]byte, *big.Int, e } func (c *Closure) Return(ret []byte) []byte { - // Return the remaining gas to the callee - // If no callee is present return it to + // Return the remaining gas to the caller + // If no caller is present return it to // the origin (i.e. contract or tx) - if c.callee != nil { - c.callee.ReturnGas(c.Gas, c.Price, c.State) + if c.caller != nil { + c.caller.ReturnGas(c.Gas, c.Price, c.State) } else { c.object.ReturnGas(c.Gas, c.Price, c.State) } @@ -107,7 +109,7 @@ func (c *Closure) UseGas(gas *big.Int) bool { return true } -// Implement the Callee interface +// Implement the caller interface func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { // Return the gas to the closure c.Gas.Add(c.Gas, gas) @@ -118,8 +120,8 @@ func (c *Closure) Object() *StateObject { return c.object } -func (c *Closure) Callee() ClosureRef { - return c.callee +func (c *Closure) Caller() ClosureRef { + return c.caller } func (c *Closure) N() *big.Int { diff --git a/ethchain/state_object.go b/ethchain/state_object.go index b92374882..12ebf8e9b 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -77,7 +77,7 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) { func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) { addr := ethutil.BigToBytes(num, 256) - //fmt.Println("storing", val.BigInt(), "@", num) + //fmt.Printf("sstore %x => %v\n", addr, val) c.SetAddr(addr, val) } @@ -102,8 +102,10 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { // 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) + /* + remainder := new(big.Int).Mul(gas, price) + c.AddAmount(remainder) + */ } func (c *StateObject) AddAmount(amount *big.Int) { diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 1256d019c..a080c5602 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -116,7 +116,7 @@ func (self *StateTransition) TransitionState() (err error) { defer func() { if r := recover(); r != nil { ethutil.Config.Log.Infoln(r) - err = fmt.Errorf("%v", r) + err = fmt.Errorf("state transition err %v", r) } }() @@ -126,41 +126,51 @@ func (self *StateTransition) TransitionState() (err error) { receiver *StateObject ) + // Make sure this transaction's nonce is correct if sender.Nonce != tx.Nonce { return NonceError(tx.Nonce, sender.Nonce) } + // Increment the nonce for the next transaction sender.Nonce += 1 + // Pre-pay gas / Buy gas of the coinbase account if err = self.BuyGas(); err != nil { return err } + // 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 } + // Pay data gas dataPrice := big.NewInt(int64(len(tx.Data))) dataPrice.Mul(dataPrice, GasData) if err = self.UseGas(dataPrice); err != nil { return err } - if receiver == nil { // Contract + // If the receiver is nil it's a contract (\0*32). + if receiver == nil { + // 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) } } + // Transfer value from sender to receiver if err = self.transferValue(sender, receiver); err != nil { return err } + // Process the init code and create 'valid' contract if tx.CreatesContract() { - fmt.Println(Disassemble(receiver.Init())) + //fmt.Println(Disassemble(receiver.Init())) // Evaluate the initialization script // and use the return value as the // script section for the state object. @@ -173,6 +183,10 @@ func (self *StateTransition) TransitionState() (err error) { receiver.script = code } + // Return remaining gas + remaining := new(big.Int).Mul(self.gas, tx.GasPrice) + sender.AddAmount(remaining) + self.state.UpdateStateObject(sender) self.state.UpdateStateObject(receiver) @@ -184,12 +198,14 @@ func (self *StateTransition) transferValue(sender, receiver *StateObject) error return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount) } - // 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.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) - 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) %x\n", sender.Address()[:4], receiver.Address()[:4], self.tx.Value, self.tx.Hash()) + } return nil } diff --git a/ethchain/vm.go b/ethchain/vm.go index e17264697..f0059f6ac 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -114,14 +114,18 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } gas := new(big.Int) - setStepGasUsage := func(amount *big.Int) { + addStepGasUsage := func(amount *big.Int) { gas.Add(gas, amount) } + addStepGasUsage(GasStep) + var newMemSize uint64 = 0 switch op { + case STOP: + case SUICIDE: case SLOAD: - setStepGasUsage(GasSLoad) + gas.Set(GasSLoad) case SSTORE: var mult *big.Int y, x := stack.Peekn() @@ -133,12 +137,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } else { mult = ethutil.Big1 } - setStepGasUsage(new(big.Int).Mul(mult, GasSStore)) + gas = new(big.Int).Mul(mult, GasSStore) case BALANCE: - setStepGasUsage(GasBalance) + gas.Set(GasBalance) case MSTORE: require(2) newMemSize = stack.Peek().Uint64() + 32 + case MLOAD: + case MSTORE8: require(2) newMemSize = stack.Peek().Uint64() + 1 @@ -149,7 +155,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case SHA3: require(2) - setStepGasUsage(GasSha) + gas.Set(GasSha) newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() case CALLDATACOPY: @@ -162,7 +168,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() case CALL: require(7) - setStepGasUsage(GasCall) + gas.Set(GasCall) + 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() @@ -170,17 +177,15 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro newMemSize = uint64(math.Max(float64(x), float64(y))) case CREATE: require(3) - setStepGasUsage(GasCreate) + gas.Set(GasCreate) newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64() - default: - setStepGasUsage(GasStep) } newMemSize = (newMemSize + 31) / 32 * 32 if newMemSize > uint64(mem.Len()) { m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32 - setStepGasUsage(big.NewInt(int64(m))) + addStepGasUsage(big.NewInt(int64(m))) } if !closure.UseGas(gas) { @@ -355,7 +360,7 @@ 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.Callee().Address())) + stack.Push(ethutil.BigD(closure.caller.Address())) case CALLVALUE: stack.Push(vm.vars.Value) case CALLDATALOAD: @@ -492,7 +497,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro snapshot := vm.state.Snapshot() // Generate a new address - addr := ethutil.CreateAddress(closure.callee.Address(), closure.callee.N()) + addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N()) // Create a new contract contract := NewContract(addr, value, []byte("")) // Set the init script @@ -503,7 +508,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro //closure.UseGas(gas) // Create the closure - c := NewClosure(closure.callee, + c := NewClosure(closure.caller, closure.Object(), contract.initScript, vm.state, diff --git a/ethutil/common.go b/ethutil/common.go index ddaf78f88..f63ba5d83 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -47,7 +47,7 @@ func CurrencyToString(num *big.Int) string { // Common big integers often used var ( Big1 = big.NewInt(1) - Big2 = big.NewInt(1) + Big2 = big.NewInt(2) Big0 = big.NewInt(0) Big32 = big.NewInt(32) Big256 = big.NewInt(0xff) diff --git a/peer.go b/peer.go index 587bc1974..4434715fb 100644 --- a/peer.go +++ b/peer.go @@ -402,7 +402,7 @@ func (p *Peer) HandleInbound() { if err != nil { // If the parent is unknown try to catch up with this peer if ethchain.IsParentErr(err) { - ethutil.Config.Log.Infoln("Attempting to catch up since we don't know the parent") + ethutil.Config.Log.Infoln("Attempting to catch. Parent known") p.catchingUp = false p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) } else if ethchain.IsValidationErr(err) { @@ -414,7 +414,7 @@ func (p *Peer) HandleInbound() { if p.catchingUp && msg.Data.Len() > 1 { if lastBlock != nil { blockInfo := lastBlock.BlockInfo() - ethutil.Config.Log.Debugf("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) + ethutil.Config.Log.Debugf("Synced chain to #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) } p.catchingUp = false