diff --git a/README.md b/README.md index 0f0a33edb0..9d810643bf 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 3.5". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 5.0". 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/closure.go b/ethchain/closure.go index f8135c5143..57abaa91eb 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -8,21 +8,24 @@ import ( ) type Callee interface { - ReturnGas(*big.Int, *big.Int, *State) - Address() []byte } type Reference interface { Callee - ethutil.RlpEncodable +} + +type ClosureRef interface { + ReturnGas(*big.Int, *big.Int, *State) + Address() []byte 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 Reference + callee ClosureRef + object ClosureRef Script []byte State *State @@ -34,7 +37,7 @@ type Closure struct { } // Create a new closure for the given data items -func NewClosure(callee Callee, object Reference, script []byte, state *State, gas, price, val *big.Int) *Closure { +func NewClosure(callee, object ClosureRef, 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 @@ -105,10 +108,14 @@ func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { c.Gas.Add(c.Gas, gas) } -func (c *Closure) Object() Reference { +func (c *Closure) Object() ClosureRef { return c.object } -func (c *Closure) Callee() Callee { +func (c *Closure) Callee() ClosureRef { return c.callee } + +func (c *Closure) N() *big.Int { + return c.object.N() +} diff --git a/ethchain/stack.go b/ethchain/stack.go index 2883600627..e9297b3246 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -67,6 +67,18 @@ 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 ###") if len(st.data) > 0 { diff --git a/ethchain/state.go b/ethchain/state.go index fa63accf8f..1b5655d4cd 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -47,6 +47,7 @@ func (s *State) Purge() int { return s.trie.NewIterator().Purge() } +// XXX Deprecated func (s *State) GetContract(addr []byte) *StateObject { data := s.trie.Get(string(addr)) if data == "" { @@ -68,6 +69,32 @@ func (s *State) GetContract(addr []byte) *StateObject { return contract } +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.UpdateStateObject(stateObject) +} + func (s *State) GetAccount(addr []byte) (account *StateObject) { data := s.trie.Get(string(addr)) if data == "" { @@ -97,6 +124,7 @@ const ( UnknownTy ) +/* // Returns the object stored at key and the type stored at key // Returns nil if nothing is stored func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) { @@ -124,6 +152,7 @@ func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) { return val, typ } +*/ // Updates any given state object func (s *State) UpdateStateObject(object *StateObject) { diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 8d86ef44ee..8e921795d7 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -65,6 +65,10 @@ 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)))) } diff --git a/ethchain/vm.go b/ethchain/vm.go index a4b4d351ba..b983e88ff1 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -20,6 +20,17 @@ var ( 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 @@ -125,7 +136,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oBALANCE: useGas(GasBalance) case oCREATE: - useGas(GasCreate) + 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: @@ -413,6 +429,39 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(big.NewInt(int64(mem.Len()))) // 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: require(7) // Closure addr @@ -438,7 +487,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // 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 { - gas = closure.Gas + // Copy + gas = new(big.Int).Set(closure.Gas) } closure.Gas.Sub(closure.Gas, gas) // Create a new callable closure diff --git a/ethutil/big.go b/ethutil/big.go index 1a3902fa3e..c0488a71f8 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -50,3 +50,11 @@ func BigCopy(src *big.Int) (ret *big.Int) { return } + +func BigMax(x, y *big.Int) *big.Int { + if x.Cmp(y) <= 0 { + return x + } + + return y +} diff --git a/ethutil/common.go b/ethutil/common.go index c63af29a69..d0ee7b5389 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -38,5 +38,12 @@ var ( Big1 = big.NewInt(1) Big2 = big.NewInt(1) Big0 = big.NewInt(0) + Big32 = big.NewInt(32) Big256 = big.NewInt(0xff) ) + +func CreateAddress(b []byte, nonce *big.Int) []byte { + addrBytes := append(b, nonce.Bytes()...) + + return Sha3Bin(addrBytes)[12:] +} diff --git a/ethutil/config.go b/ethutil/config.go index 54b066fb95..86c0a855d3 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -48,7 +48,7 @@ func ReadConfig(base string) *config { } } - Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.5"} Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) Config.SetClientString("/Ethereum(G)") }