From 344e827061c896a17124a65686bdc3fbbd03d7bb Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 17 Mar 2014 12:08:16 +0100 Subject: [PATCH 01/20] Added client string to configuration Clients can set their own client string which will be send to connected peers during the handshake. --- ethutil/config.go | 15 +++++++++------ peer.go | 3 +-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ethutil/config.go b/ethutil/config.go index 5fdc8e1c5..436c12b92 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -6,6 +6,7 @@ import ( "os" "os/user" "path" + "runtime" ) type LogType byte @@ -19,12 +20,13 @@ const ( type config struct { Db Database - Log *Logger - ExecPath string - Debug bool - Ver string - Pubkey []byte - Seed bool + Log *Logger + ExecPath string + Debug bool + Ver string + ClientString string + Pubkey []byte + Seed bool } var Config *config @@ -48,6 +50,7 @@ func ReadConfig(base string) *config { Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"} Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) + Config.ClientString = fmt.Sprintf("/Ethereum(G) v%s/%s", Config.Ver, runtime.GOOS) } return Config diff --git a/peer.go b/peer.go index 4e927ada4..24a5e97c9 100644 --- a/peer.go +++ b/peer.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "net" - "runtime" "strconv" "strings" "sync/atomic" @@ -158,7 +157,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { connected: 0, disconnect: 0, caps: caps, - Version: fmt.Sprintf("/Ethereum(G) v%s/%s", ethutil.Config.Ver, runtime.GOOS), + Version: ethutil.Config.ClientString, } // Set up the connection in another goroutine so we don't block the main thread From bdc0d1b7ad4e2a4ff78f287f088c13a5d6ab2148 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:24:02 +0100 Subject: [PATCH 02/20] Added AddFunds method --- ethchain/address.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ethchain/address.go b/ethchain/address.go index aa1709f2c..f1f27a1a5 100644 --- a/ethchain/address.go +++ b/ethchain/address.go @@ -22,7 +22,11 @@ func NewAccountFromData(data []byte) *Account { } func (a *Account) AddFee(fee *big.Int) { - a.Amount.Add(a.Amount, fee) + a.AddFunds(fee) +} + +func (a *Account) AddFunds(funds *big.Int) { + a.Amount.Add(a.Amount, funds) } func (a *Account) RlpEncode() []byte { From c135b389fe358006bd3586097e1b17232b0c86e6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:24:53 +0100 Subject: [PATCH 03/20] Commented out code due to rewrite vm --- ethchain/block_manager_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go index ec4fbe8c5..3a1e5f510 100644 --- a/ethchain/block_manager_test.go +++ b/ethchain/block_manager_test.go @@ -1,5 +1,6 @@ package ethchain +/* import ( _ "fmt" "github.com/ethereum/eth-go/ethdb" @@ -14,9 +15,10 @@ func TestVm(t *testing.T) { db, _ := ethdb.NewMemDatabase() ethutil.Config.Db = db - bm := NewBlockManager(nil) + bm := NewStateManager(nil) block := bm.bc.genesisBlock + bm.Prepare(block.State(), block.State()) script := Compile([]string{ "PUSH", "1", @@ -31,3 +33,4 @@ func TestVm(t *testing.T) { tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) bm.ApplyTransactions(block, []*Transaction{tx2}) } +*/ From 82d0f65dab253e215349ea685382bca9672378d8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:25:11 +0100 Subject: [PATCH 04/20] Comply to Callee structure --- ethchain/contract.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ethchain/contract.go b/ethchain/contract.go index 21ac828fe..10c5e2df6 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -43,12 +43,17 @@ func (c *Contract) State() *State { return c.state } -func (c *Contract) GetMem(num int) *ethutil.Value { - nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256) +func (c *Contract) GetMem(num int64) *ethutil.Value { + nb := ethutil.BigToBytes(big.NewInt(num), 256) return c.Addr(nb) } +// 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 MakeContract(tx *Transaction, state *State) *Contract { // Create contract if there's no recipient if tx.IsContract() { From 38ea6a6d5dffb7573b29541c2a6687145bdcc495 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:26:07 +0100 Subject: [PATCH 05/20] Closures and vm based on closures Status: Work in progress --- ethchain/closure.go | 68 +++++++++++++++++++++++++++++++ ethchain/vm.go | 98 ++++++++++++++++++++++++++++++++++++++++++--- ethchain/vm_test.go | 56 ++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 ethchain/closure.go diff --git a/ethchain/closure.go b/ethchain/closure.go new file mode 100644 index 000000000..4ef43a2da --- /dev/null +++ b/ethchain/closure.go @@ -0,0 +1,68 @@ +package ethchain + +// TODO Re write VM to use values instead of big integers? + +import ( + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type Callee interface { + ReturnGas(*big.Int, *State) +} + +type ClosureBody interface { + Callee + ethutil.RlpEncodable + GetMem(int64) *ethutil.Value +} + +// Basic inline closure object which implement the 'closure' interface +type Closure struct { + callee Callee + object ClosureBody + state *State + + gas *big.Int + val *big.Int +} + +// 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} +} + +// Retuns the x element in data slice +func (c *Closure) GetMem(x int64) *ethutil.Value { + m := c.object.GetMem(x) + if m == nil { + return ethutil.EmptyValue() + } + + return m +} + +func (c *Closure) Return(ret []byte) []byte { + // Return the remaining gas to the callee + // 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) + } 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 + } + + return ret +} + +// Implement the Callee interface +func (c *Closure) ReturnGas(gas *big.Int, state *State) { + // Return the gas to the closure + c.gas.Add(c.gas, gas) +} + +func (c *Closure) GetGas() *big.Int { + return c.gas +} diff --git a/ethchain/vm.go b/ethchain/vm.go index 7e119ac99..861b041d8 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -32,13 +32,101 @@ type RuntimeVars struct { txData []string } +func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byte { + // If the amount of gas supplied is less equal to 0 + if closure.GetGas().Cmp(big.NewInt(0)) <= 0 { + // TODO Do something + } + + // Memory for the current closure + var mem []byte + // New stack (should this be shared?) + stack := NewStack() + // Instruction pointer + pc := int64(0) + // Current address + //addr := vars.address + step := 0 + + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("# op\n") + } + + for { + step++ + // Get the memory location of pc + val := closure.GetMem(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()) + } + + // TODO Get each instruction cost properly + fee := new(big.Int) + fee.Add(fee, big.NewInt(1000)) + + if closure.GetGas().Cmp(fee) < 0 { + return closure.Return(nil) + } + + switch op { + case oSTOP: + return closure.Return(nil) + case oPUSH: + pc++ + val := closure.GetMem(pc).BigInt() + stack.Push(val) + case oMSTORE: + // Pop value of the stack + val := stack.Pop() + // Set the bytes to the memory field + mem = append(mem, ethutil.BigToBytes(val, 256)...) + case oCALL: + // Pop return size and offset + retSize, retOffset := stack.Popn() + // Pop input size and offset + inSize, inOffset := stack.Popn() + // TODO remove me. + fmt.Sprintln(inSize, inOffset) + // Pop gas and value of the stack. + gas, value := stack.Popn() + // Closure addr + addr := stack.Pop() + + contract := state.GetContract(addr.Bytes()) + closure := NewClosure(closure, contract, state, gas, value) + ret := vm.RunClosure(closure, state, vars) + + // Ensure that memory is large enough to hold the returned data + totSize := new(big.Int).Add(retOffset, retSize) + lenSize := big.NewInt(int64(len(mem))) + // Resize the current memory slice so that the return value may fit + if totSize.Cmp(lenSize) > 0 { + diff := new(big.Int).Sub(totSize, lenSize) + newSlice := make([]byte, diff.Int64()+1) + mem = append(mem, newSlice...) + } + copy(mem[retOffset.Int64():retOffset.Int64()+retSize.Int64()+1], ret) + case oRETURN: + size, offset := stack.Popn() + ret := mem[offset.Int64() : offset.Int64()+size.Int64()+1] + + return closure.Return(ret) + } + + pc++ + } +} + +// Old VM code func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) { vm.mem = make(map[string]*big.Int) vm.stack = NewStack() addr := vars.address // tx.Hash()[12:] // Instruction pointer - pc := 0 + pc := int64(0) if contract == nil { fmt.Println("Contract not found") @@ -344,7 +432,7 @@ out: contract.SetAddr(addr, y) //contract.State().Update(string(idx), string(y)) case oJMP: - x := int(vm.stack.Pop().Uint64()) + x := vm.stack.Pop().Int64() // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) pc = x pc-- @@ -352,7 +440,7 @@ out: x := vm.stack.Pop() // Set pc to x if it's non zero if x.Cmp(ethutil.BigFalse) != 0 { - pc = int(x.Uint64()) + pc = x.Int64() pc-- } case oIND: @@ -400,9 +488,9 @@ out: 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 := 0 + j := int64(0) dataItems := make([]string, int(length.Uint64())) - for i := from.Uint64(); i < length.Uint64(); i++ { + for i := from.Int64(); i < length.Int64(); i++ { dataItems[j] = contract.GetMem(j).Str() j++ } diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 6ceb0ff15..4e72c9249 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -8,6 +8,8 @@ import ( "testing" ) +/* + func TestRun(t *testing.T) { InitFees() @@ -104,3 +106,57 @@ func TestRun2(t *testing.T) { txData: tx.Data, }) } +*/ + +// XXX Full stack test +func TestRun3(t *testing.T) { + ethutil.ReadConfig("") + + db, _ := ethdb.NewMemDatabase() + state := NewState(ethutil.NewTrie(db, "")) + + script := Compile([]string{ + "PUSH", "300", + "MSTORE", + "PUSH", "300", + "MSTORE", + "PUSH", "62", + "PUSH", "0", + "RETURN", + }) + tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) + addr := tx.Hash()[12:] + fmt.Printf("addr contract %x\n", addr) + contract := MakeContract(tx, state) + state.UpdateContract(addr, contract) + + callerScript := Compile([]string{ + "PUSH", "62", // REND + "PUSH", "0", // RSTART + "PUSH", "22", // MEND + "PUSH", "15", // MSTART + "PUSH", "1000", /// Gas + "PUSH", "0", /// value + "PUSH", string(addr), // Sender + "CALL", + }) + callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) + callerAddr := callerTx.Hash()[12:] + executer := NewTransaction(ContractAddr, ethutil.Big("10000"), nil) + + executer.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + callerClosure := NewClosure(executer, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) + + vm := &Vm{} + vm.RunClosure(callerClosure, state, RuntimeVars{ + address: callerAddr, + blockNumber: 1, + sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), + prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), + coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + time: 1, + diff: big.NewInt(256), + txValue: big.NewInt(10000), + txData: nil, + }) +} From 59d8dc39505981d1ae07b05a71d3f4cf1f33319a Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:26:30 +0100 Subject: [PATCH 06/20] Fixed issue with stack where it sliced of the wrong values --- ethchain/stack.go | 86 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index 13b0f247b..349e7817a 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -2,6 +2,7 @@ package ethchain import ( "fmt" + "github.com/ethereum/eth-go/ethutil" "math/big" ) @@ -60,6 +61,10 @@ const ( oBALANCE = 0x3c oMKTX = 0x3d oSUICIDE = 0x3f + + // TODO FIX OPCODES + oCALL = 0x40 + oRETURN = 0x41 ) // Since the opcodes aren't all in order we can't use a regular slice @@ -114,6 +119,9 @@ var opCodeToString = map[OpCode]string{ oBALANCE: "BALANCE", oMKTX: "MKTX", oSUICIDE: "SUICIDE", + + oCALL: "CALL", + oRETURN: "RETURN", } func (o OpCode) String() string { @@ -141,35 +149,27 @@ func NewStack() *Stack { } func (st *Stack) Pop() *big.Int { - s := len(st.data) - - str := st.data[s-1] - st.data = st.data[:s-1] + str := st.data[0] + st.data = st.data[1:] return str } func (st *Stack) Popn() (*big.Int, *big.Int) { - s := len(st.data) - - ints := st.data[s-2:] - st.data = st.data[:s-2] + ints := st.data[:2] + st.data = st.data[2:] return ints[0], ints[1] } func (st *Stack) Peek() *big.Int { - s := len(st.data) - - str := st.data[s-1] + str := st.data[0] return str } func (st *Stack) Peekn() (*big.Int, *big.Int) { - s := len(st.data) - - ints := st.data[s-2:] + ints := st.data[:2] return ints[0], ints[1] } @@ -188,3 +188,61 @@ func (st *Stack) Print() { } fmt.Println("#############") } + +////////////// TODO this will eventually become the main stack once the big ints are removed from the VM +type ValueStack struct { + data []*ethutil.Value +} + +func NewValueStack() *ValueStack { + return &ValueStack{} +} + +func (st *ValueStack) Pop() *ethutil.Value { + s := len(st.data) + + str := st.data[s-1] + st.data = st.data[:s-1] + + return str +} + +func (st *ValueStack) Popn() (*ethutil.Value, *ethutil.Value) { + s := len(st.data) + + ints := st.data[s-2:] + st.data = st.data[:s-2] + + return ints[0], ints[1] +} + +func (st *ValueStack) Peek() *ethutil.Value { + s := len(st.data) + + str := st.data[s-1] + + return str +} + +func (st *ValueStack) Peekn() (*ethutil.Value, *ethutil.Value) { + s := len(st.data) + + ints := st.data[s-2:] + + return ints[0], ints[1] +} + +func (st *ValueStack) Push(d *ethutil.Value) { + st.data = append(st.data, d) +} +func (st *ValueStack) Print() { + fmt.Println("### STACK ###") + if len(st.data) > 0 { + for i, val := range st.data { + fmt.Printf("%-3d %v\n", i, val) + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("#############") +} From c17381b853e2d762787ca30c5ce45aeece99dfd1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:26:51 +0100 Subject: [PATCH 07/20] Moved code around --- ethchain/state.go | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/ethchain/state.go b/ethchain/state.go index b9c2c576d..b84d60c6c 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -79,20 +79,10 @@ func (s *State) GetContract(addr []byte) *Contract { } func (s *State) UpdateContract(addr []byte, contract *Contract) { + s.states[string(addr)] = contract.state s.trie.Update(string(addr), string(contract.RlpEncode())) } -func Compile(code []string) (script []string) { - script = make([]string, len(code)) - for i, val := range code { - instr, _ := ethutil.CompileInstr(val) - - script[i] = string(instr) - } - - return -} - func (s *State) GetAccount(addr []byte) (account *Account) { data := s.trie.Get(string(addr)) if data == "" { @@ -153,3 +143,31 @@ 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)) +} + +// 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) + + script[i] = string(instr) + } + + return +} + +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) + } + + return +} From 3520771d68df1e9becfb29cc2f85d7042f3fb9d1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:27:09 +0100 Subject: [PATCH 08/20] Comply to Callee interface --- ethchain/transaction.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 57df9cdc4..07e7ea970 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -29,6 +29,15 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { return &tx } +// Implements Callee +func (tx *Transaction) ReturnGas(value *big.Int, state *State) { + // Return the value back to the sender + sender := tx.Sender() + account := state.GetAccount(sender) + account.AddFunds(value) + state.UpdateAccount(sender, account) +} + // XXX Deprecated func NewTransactionFromData(data []byte) *Transaction { return NewTransactionFromBytes(data) From c642094cac6ee6fb0215d7510cc57a719c2a2689 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:27:26 +0100 Subject: [PATCH 09/20] Added encoder interface --- ethutil/rlp.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ethutil/rlp.go b/ethutil/rlp.go index e633f5f1d..33ec0d359 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -9,6 +9,10 @@ import ( "math/big" ) +type RlpEncodable interface { + RlpEncode() []byte +} + type RlpEncoder struct { rlpData []byte } From f21eb88ad1cf54b342187e8d3b55374a695cd524 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:27:48 +0100 Subject: [PATCH 10/20] Some minor updates --- ethutil/config.go | 6 +++++- ethutil/parsing.go | 10 +++++++++- ethutil/trie_test.go | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ethutil/config.go b/ethutil/config.go index 436c12b92..54b066fb9 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -50,12 +50,16 @@ func ReadConfig(base string) *config { Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"} Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) - Config.ClientString = fmt.Sprintf("/Ethereum(G) v%s/%s", Config.Ver, runtime.GOOS) + Config.SetClientString("/Ethereum(G)") } return Config } +func (c *config) SetClientString(str string) { + Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS) +} + type LoggerType byte const ( diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 553bb9717..459cdc284 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -58,6 +58,10 @@ var OpCodes = map[string]byte{ "BALANCE": 0x3c, "MKTX": 0x3d, "SUICIDE": 0x3f, + + // TODO FIX OPCODES + "CALL": 0x40, + "RETURN": 0x41, } func IsOpCode(s string) bool { @@ -76,7 +80,11 @@ func CompileInstr(s string) ([]byte, error) { } num := new(big.Int) - num.SetString(s, 0) + _, success := num.SetString(s, 0) + // Assume regular bytes during compilation + if !success { + num.SetBytes([]byte(s)) + } return num.Bytes(), nil } diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index 7c398f1de..79e5de921 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,6 +1,7 @@ package ethutil import ( + "fmt" "reflect" "testing" ) From c68ff9886bdd59294bc2bf0a6b8bf9b83645cc9a Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 19:50:53 +0100 Subject: [PATCH 11/20] Fixed MSTORE and added some more commets --- ethchain/closure.go | 16 ++++++++++++---- ethchain/stack.go | 1 + ethchain/vm.go | 46 +++++++++++++++++++++++++++++++++------------ ethchain/vm_test.go | 6 ++++-- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 4ef43a2da..204fbce06 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -21,15 +21,17 @@ type ClosureBody interface { type Closure struct { callee Callee object ClosureBody - state *State + State *State gas *big.Int val *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} + return &Closure{callee, object, state, gas, val, nil} } // Retuns the x element in data slice @@ -42,14 +44,20 @@ func (c *Closure) GetMem(x int64) *ethutil.Value { return m } +func (c *Closure) Call(vm *Vm, args []byte) []byte { + c.args = args + + return vm.RunClosure(c) +} + func (c *Closure) Return(ret []byte) []byte { // Return the remaining gas to the callee // 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.State) } else { - c.object.ReturnGas(c.gas, c.state) + 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 } diff --git a/ethchain/stack.go b/ethchain/stack.go index 349e7817a..bfb19614e 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -235,6 +235,7 @@ func (st *ValueStack) Peekn() (*ethutil.Value, *ethutil.Value) { func (st *ValueStack) Push(d *ethutil.Value) { st.data = append(st.data, d) } + func (st *ValueStack) Print() { fmt.Println("### STACK ###") if len(st.data) > 0 { diff --git a/ethchain/vm.go b/ethchain/vm.go index 861b041d8..2fa78a748 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -18,6 +18,8 @@ type Vm struct { mem map[string]*big.Int vars RuntimeVars + + state *State } type RuntimeVars struct { @@ -32,7 +34,11 @@ type RuntimeVars struct { txData []string } -func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byte { +func NewVm(state *State, vars RuntimeVars) *Vm { + return &Vm{vars: vars, state: state} +} + +func (vm *Vm) RunClosure(closure *Closure) []byte { // If the amount of gas supplied is less equal to 0 if closure.GetGas().Cmp(big.NewInt(0)) <= 0 { // TODO Do something @@ -71,17 +77,28 @@ func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byt } switch op { - case oSTOP: + case oSTOP: // Stop the closure return closure.Return(nil) - case oPUSH: + case oPUSH: // Push PC+1 on to the stack pc++ val := closure.GetMem(pc).BigInt() stack.Push(val) - case oMSTORE: + case oMSTORE: // Store the value at stack top-1 in to memory at location stack top // Pop value of the stack - val := stack.Pop() - // Set the bytes to the memory field - mem = append(mem, ethutil.BigToBytes(val, 256)...) + val, mStart := stack.Popn() + // Ensure that memory is large enough to hold the data + // If it isn't resize the memory slice so that it may hold the value + bytesLen := big.NewInt(32) + totSize := new(big.Int).Add(mStart, bytesLen) + lenSize := big.NewInt(int64(len(mem))) + if totSize.Cmp(lenSize) > 0 { + // Calculate the diff between the sizes + diff := new(big.Int).Sub(totSize, lenSize) + // Create a new empty slice and append it + newSlice := make([]byte, diff.Int64()+1) + mem = append(mem, newSlice...) + } + copy(mem[mStart.Int64():mStart.Int64()+bytesLen.Int64()+1], ethutil.BigToBytes(val, 256)) case oCALL: // Pop return size and offset retSize, retOffset := stack.Popn() @@ -93,20 +110,25 @@ func (vm *Vm) RunClosure(closure *Closure, state *State, vars RuntimeVars) []byt gas, value := stack.Popn() // Closure addr addr := stack.Pop() - - contract := state.GetContract(addr.Bytes()) - closure := NewClosure(closure, contract, state, gas, value) - ret := vm.RunClosure(closure, state, vars) + // 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, nil) // Ensure that memory is large enough to hold the returned data + // If it isn't resize the memory slice so that it may hold the value totSize := new(big.Int).Add(retOffset, retSize) lenSize := big.NewInt(int64(len(mem))) - // Resize the current memory slice so that the return value may fit if totSize.Cmp(lenSize) > 0 { + // Calculate the diff between the sizes diff := new(big.Int).Sub(totSize, lenSize) + // Create a new empty slice and append it newSlice := make([]byte, diff.Int64()+1) mem = append(mem, newSlice...) } + // Copy over the returned values to the memory given the offset and size copy(mem[retOffset.Int64():retOffset.Int64()+retSize.Int64()+1], ret) case oRETURN: size, offset := stack.Popn() diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 4e72c9249..654ddb566 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -117,8 +117,10 @@ func TestRun3(t *testing.T) { script := Compile([]string{ "PUSH", "300", + "PUSH", "0", "MSTORE", "PUSH", "300", + "PUSH", "31", "MSTORE", "PUSH", "62", "PUSH", "0", @@ -147,8 +149,7 @@ func TestRun3(t *testing.T) { executer.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) callerClosure := NewClosure(executer, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) - vm := &Vm{} - vm.RunClosure(callerClosure, state, RuntimeVars{ + vm := NewVm(state, RuntimeVars{ address: callerAddr, blockNumber: 1, sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), @@ -159,4 +160,5 @@ func TestRun3(t *testing.T) { txValue: big.NewInt(10000), txData: nil, }) + callerClosure.Call(vm, nil) } From f3d27bf5d878120346f8cdd0744e7f1f8e1ee631 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 22:51:20 +0100 Subject: [PATCH 12/20] Rewrote opcodes again --- ethchain/stack.go | 301 ++++++++++++++++++-------------------- ethchain/state_manager.go | 3 +- ethchain/vm.go | 41 ++---- ethutil/parsing.go | 117 ++++++++------- 4 files changed, 217 insertions(+), 245 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index bfb19614e..429c31d08 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -2,7 +2,7 @@ package ethchain import ( "fmt" - "github.com/ethereum/eth-go/ethutil" + _ "github.com/ethereum/eth-go/ethutil" "math/big" ) @@ -10,116 +10,136 @@ type OpCode int // Op codes const ( - oSTOP = 0x00 - oADD = 0x01 - oMUL = 0x02 - oSUB = 0x03 - oDIV = 0x04 - oSDIV = 0x05 - oMOD = 0x06 - oSMOD = 0x07 - oEXP = 0x08 - oNEG = 0x09 - oLT = 0x0a - oLE = 0x0b - oGT = 0x0c - oGE = 0x0d - oEQ = 0x0e - oNOT = 0x0f - oMYADDRESS = 0x10 - oTXSENDER = 0x11 - oTXVALUE = 0x12 - oTXDATAN = 0x13 - oTXDATA = 0x14 - oBLK_PREVHASH = 0x15 - oBLK_COINBASE = 0x16 - oBLK_TIMESTAMP = 0x17 - oBLK_NUMBER = 0x18 - oBLK_DIFFICULTY = 0x19 - oBLK_NONCE = 0x1a - oBASEFEE = 0x1b - oSHA256 = 0x20 - oRIPEMD160 = 0x21 - oECMUL = 0x22 - oECADD = 0x23 - oECSIGN = 0x24 - oECRECOVER = 0x25 - oECVALID = 0x26 - oSHA3 = 0x27 - oPUSH = 0x30 - oPOP = 0x31 - oDUP = 0x32 - oSWAP = 0x33 - oMLOAD = 0x34 - oMSTORE = 0x35 - oSLOAD = 0x36 - oSSTORE = 0x37 - oJMP = 0x38 - oJMPI = 0x39 - oIND = 0x3a - oEXTRO = 0x3b - oBALANCE = 0x3c - oMKTX = 0x3d - oSUICIDE = 0x3f + // 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 - // TODO FIX OPCODES - oCALL = 0x40 - oRETURN = 0x41 + // 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 + oRETURNDATASIZE = 0x37 + oTXGASPRICE = 0x38 + + // 0x40 range - block operations + oPREVHASH = 0x40 + oPREVNONCE = 0x41 + oCOINBASE = 0x42 + oTIMESTAMP = 0x43 + oNUMBER = 0x44 + oDIFFICULTY = 0x45 + oGASLIMIT = 0x46 + + // 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 + oMEMSIZE = 0x5c + + // 0x60 range - closures + oCREATE = 0x60 + oCALL = 0x61 + oRETURN = 0x62 ) // Since the opcodes aren't all in order we can't use a regular slice var opCodeToString = map[OpCode]string{ - oSTOP: "STOP", - oADD: "ADD", - oMUL: "MUL", - oSUB: "SUB", - oDIV: "DIV", - oSDIV: "SDIV", - oMOD: "MOD", - oSMOD: "SMOD", - oEXP: "EXP", - oNEG: "NEG", - oLT: "LT", - oLE: "LE", - oGT: "GT", - oGE: "GE", - oEQ: "EQ", - oNOT: "NOT", - oMYADDRESS: "MYADDRESS", - oTXSENDER: "TXSENDER", - oTXVALUE: "TXVALUE", - oTXDATAN: "TXDATAN", - oTXDATA: "TXDATA", - oBLK_PREVHASH: "BLK_PREVHASH", - oBLK_COINBASE: "BLK_COINBASE", - oBLK_TIMESTAMP: "BLK_TIMESTAMP", - oBLK_NUMBER: "BLK_NUMBER", - oBLK_DIFFICULTY: "BLK_DIFFICULTY", - oBASEFEE: "BASEFEE", - oSHA256: "SHA256", - oRIPEMD160: "RIPEMD160", - oECMUL: "ECMUL", - oECADD: "ECADD", - oECSIGN: "ECSIGN", - oECRECOVER: "ECRECOVER", - oECVALID: "ECVALID", - oSHA3: "SHA3", - oPUSH: "PUSH", - oPOP: "POP", - oDUP: "DUP", - oSWAP: "SWAP", - oMLOAD: "MLOAD", - oMSTORE: "MSTORE", - oSLOAD: "SLOAD", - oSSTORE: "SSTORE", - oJMP: "JMP", - oJMPI: "JMPI", - oIND: "IND", - oEXTRO: "EXTRO", - oBALANCE: "BALANCE", - oMKTX: "MKTX", - oSUICIDE: "SUICIDE", + // 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", + oRETURNDATASIZE: "RETURNDATASIZE", + oTXGASPRICE: "TXGASPRICE", + + // 0x40 range - block operations + oPREVHASH: "PREVHASH", + oPREVNONCE: "PREVNONCE", + 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", + oMEMSIZE: "MEMSIZE", + + // 0x60 range - closures + oCREATE: "CREATE", oCALL: "CALL", oRETURN: "RETURN", } @@ -189,61 +209,26 @@ func (st *Stack) Print() { fmt.Println("#############") } -////////////// TODO this will eventually become the main stack once the big ints are removed from the VM -type ValueStack struct { - data []*ethutil.Value +type Memory struct { + store []byte } -func NewValueStack() *ValueStack { - return &ValueStack{} -} - -func (st *ValueStack) Pop() *ethutil.Value { - s := len(st.data) - - str := st.data[s-1] - st.data = st.data[:s-1] - - return str -} - -func (st *ValueStack) Popn() (*ethutil.Value, *ethutil.Value) { - s := len(st.data) - - ints := st.data[s-2:] - st.data = st.data[:s-2] - - return ints[0], ints[1] -} - -func (st *ValueStack) Peek() *ethutil.Value { - s := len(st.data) - - str := st.data[s-1] - - return str -} - -func (st *ValueStack) Peekn() (*ethutil.Value, *ethutil.Value) { - s := len(st.data) - - ints := st.data[s-2:] - - return ints[0], ints[1] -} - -func (st *ValueStack) Push(d *ethutil.Value) { - st.data = append(st.data, d) -} - -func (st *ValueStack) Print() { - fmt.Println("### STACK ###") - if len(st.data) > 0 { - for i, val := range st.data { - fmt.Printf("%-3d %v\n", i, val) +func (m *Memory) Set(offset, size int64, value []byte) { + totSize := offset + size + lenSize := int64(len(m.store)) + if totSize > lenSize { + // Calculate the diff between the sizes + diff := totSize - lenSize + if diff > 0 { + // Create a new empty slice and append it + newSlice := make([]byte, diff+1) + // Resize slice + m.store = append(m.store, newSlice...) } - } else { - fmt.Println("-- empty --") } - fmt.Println("#############") + copy(m.store[offset:offset+size+1], value) +} + +func (m *Memory) Get(offset, size int64) []byte { + return m.store[offset : offset+size] } diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index e67f0f680..14686b217 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -306,8 +306,8 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo }() */ + /*TODO to be fixed and replaced by the new vm vm := &Vm{} - //vm.Process(contract, block.state, RuntimeVars{ vm.Process(contract, sm.procState, RuntimeVars{ address: tx.Hash()[12:], blockNumber: block.BlockInfo().Number, @@ -319,4 +319,5 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo txValue: tx.Value, txData: tx.Data, }) + */ } diff --git a/ethchain/vm.go b/ethchain/vm.go index 2fa78a748..6479409f8 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -1,12 +1,12 @@ package ethchain import ( - "bytes" + _ "bytes" "fmt" "github.com/ethereum/eth-go/ethutil" - "github.com/obscuren/secp256k1-go" + _ "github.com/obscuren/secp256k1-go" "log" - "math" + _ "math" "math/big" ) @@ -45,7 +45,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } // Memory for the current closure - var mem []byte + mem := &Memory{} // New stack (should this be shared?) stack := NewStack() // Instruction pointer @@ -86,19 +86,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { case oMSTORE: // Store the value at stack top-1 in to memory at location stack top // Pop value of the stack val, mStart := stack.Popn() - // Ensure that memory is large enough to hold the data - // If it isn't resize the memory slice so that it may hold the value - bytesLen := big.NewInt(32) - totSize := new(big.Int).Add(mStart, bytesLen) - lenSize := big.NewInt(int64(len(mem))) - if totSize.Cmp(lenSize) > 0 { - // Calculate the diff between the sizes - diff := new(big.Int).Sub(totSize, lenSize) - // Create a new empty slice and append it - newSlice := make([]byte, diff.Int64()+1) - mem = append(mem, newSlice...) - } - copy(mem[mStart.Int64():mStart.Int64()+bytesLen.Int64()+1], ethutil.BigToBytes(val, 256)) + mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) case oCALL: // Pop return size and offset retSize, retOffset := stack.Popn() @@ -116,23 +104,10 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { closure := NewClosure(closure, contract, vm.state, gas, value) // Executer the closure and get the return value (if any) ret := closure.Call(vm, nil) - - // Ensure that memory is large enough to hold the returned data - // If it isn't resize the memory slice so that it may hold the value - totSize := new(big.Int).Add(retOffset, retSize) - lenSize := big.NewInt(int64(len(mem))) - if totSize.Cmp(lenSize) > 0 { - // Calculate the diff between the sizes - diff := new(big.Int).Sub(totSize, lenSize) - // Create a new empty slice and append it - newSlice := make([]byte, diff.Int64()+1) - mem = append(mem, newSlice...) - } - // Copy over the returned values to the memory given the offset and size - copy(mem[retOffset.Int64():retOffset.Int64()+retSize.Int64()+1], ret) + mem.Set(retOffset.Int64(), retSize.Int64(), ret) case oRETURN: size, offset := stack.Popn() - ret := mem[offset.Int64() : offset.Int64()+size.Int64()+1] + ret := mem.Get(offset.Int64(), size.Int64()) return closure.Return(ret) } @@ -141,6 +116,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } } +/* // Old VM code func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) { vm.mem = make(map[string]*big.Int) @@ -507,6 +483,7 @@ out: state.UpdateContract(addr, contract) } +*/ 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) diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 459cdc284..9ff2827a0 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -7,61 +7,70 @@ import ( // Op codes var OpCodes = map[string]byte{ - "STOP": 0x00, - "ADD": 0x01, - "MUL": 0x02, - "SUB": 0x03, - "DIV": 0x04, - "SDIV": 0x05, - "MOD": 0x06, - "SMOD": 0x07, - "EXP": 0x08, - "NEG": 0x09, - "LT": 0x0a, - "LE": 0x0b, - "GT": 0x0c, - "GE": 0x0d, - "EQ": 0x0e, - "NOT": 0x0f, - "MYADDRESS": 0x10, - "TXSENDER": 0x11, - "TXVALUE": 0x12, - "TXDATAN": 0x13, - "TXDATA": 0x14, - "BLK_PREVHASH": 0x15, - "BLK_COINBASE": 0x16, - "BLK_TIMESTAMP": 0x17, - "BLK_NUMBER": 0x18, - "BLK_DIFFICULTY": 0x19, - "BLK_NONCE": 0x1a, - "BASEFEE": 0x1b, - "SHA256": 0x20, - "RIPEMD160": 0x21, - "ECMUL": 0x22, - "ECADD": 0x23, - "ECSIGN": 0x24, - "ECRECOVER": 0x25, - "ECVALID": 0x26, - "SHA3": 0x27, - "PUSH": 0x30, - "POP": 0x31, - "DUP": 0x32, - "SWAP": 0x33, - "MLOAD": 0x34, - "MSTORE": 0x35, - "SLOAD": 0x36, - "SSTORE": 0x37, - "JMP": 0x38, - "JMPI": 0x39, - "IND": 0x3a, - "EXTRO": 0x3b, - "BALANCE": 0x3c, - "MKTX": 0x3d, - "SUICIDE": 0x3f, + // 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, - // TODO FIX OPCODES - "CALL": 0x40, - "RETURN": 0x41, + // 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, + "CALLDATA": 0x35, + "CALLDATASIZE": 0x36, + "RETURNDATASIZE": 0x37, + "TXGASPRICE": 0x38, + + // 0x40 range - block operations + "PREVHASH": 0x40, + "PREVNONCE": 0x41, + "COINBASE": 0x42, + "TIMESTAMP": 0x43, + "NUMBER": 0x44, + "DIFFICULTY": 0x45, + "GASLIMIT": 0x46, + + // 0x50 range - 'storage' and execution + "PUSH": 0x50, + "POP": 0x51, + "DUP": 0x52, + "SWAP": 0x53, + "MLOAD": 0x54, + "MSTORE": 0x55, + "MSTORE8": 0x56, + "SLOAD": 0x57, + "SSTORE": 0x58, + "JUMP": 0x59, + "JUMPI": 0x5a, + "PC": 0x5b, + "MEMSIZE": 0x5c, + + // 0x60 range - closures + "CREATE": 0x60, + "CALL": 0x61, + "RETURN": 0x62, } func IsOpCode(s string) bool { From 7705b23f248156878d00c301fbbadafedaf7e3d2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 23:17:53 +0100 Subject: [PATCH 13/20] Removed caller from tx and added "callership" to account. Transactions can no longer serve as callers. Accounts are now the initial callee of closures. Transactions now serve as transport to call closures. --- ethchain/address.go | 24 ++++++++++++++++-------- ethchain/block.go | 2 +- ethchain/closure.go | 4 ++-- ethchain/state.go | 4 ++-- ethchain/transaction.go | 9 --------- ethchain/vm.go | 4 ++++ ethchain/vm_test.go | 13 ++++++------- 7 files changed, 31 insertions(+), 29 deletions(-) diff --git a/ethchain/address.go b/ethchain/address.go index f1f27a1a5..9c6acbe08 100644 --- a/ethchain/address.go +++ b/ethchain/address.go @@ -6,19 +6,20 @@ import ( ) type Account struct { - Amount *big.Int - Nonce uint64 + Address []byte + Amount *big.Int + Nonce uint64 } -func NewAccount(amount *big.Int) *Account { - return &Account{Amount: amount, Nonce: 0} +func NewAccount(address []byte, amount *big.Int) *Account { + return &Account{address, amount, 0} } -func NewAccountFromData(data []byte) *Account { - address := &Account{} - address.RlpDecode(data) +func NewAccountFromData(address, data []byte) *Account { + account := &Account{Address: address} + account.RlpDecode(data) - return address + return account } func (a *Account) AddFee(fee *big.Int) { @@ -29,6 +30,13 @@ func (a *Account) AddFunds(funds *big.Int) { a.Amount.Add(a.Amount, funds) } +// 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}) } diff --git a/ethchain/block.go b/ethchain/block.go index 20af73ba2..1f63c2c9e 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -142,7 +142,7 @@ 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([]byte(data)) + ether := NewAccountFromData(block.Coinbase, []byte(data)) base = new(big.Int) ether.Amount = base.Add(ether.Amount, fee) diff --git a/ethchain/closure.go b/ethchain/closure.go index 204fbce06..9453ce22c 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -26,7 +26,7 @@ type Closure struct { gas *big.Int val *big.Int - args []byte + Args []byte } // Create a new closure for the given data items @@ -45,7 +45,7 @@ func (c *Closure) GetMem(x int64) *ethutil.Value { } func (c *Closure) Call(vm *Vm, args []byte) []byte { - c.args = args + c.Args = args return vm.RunClosure(c) } diff --git a/ethchain/state.go b/ethchain/state.go index b84d60c6c..b6750d62d 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -86,9 +86,9 @@ func (s *State) UpdateContract(addr []byte, contract *Contract) { func (s *State) GetAccount(addr []byte) (account *Account) { data := s.trie.Get(string(addr)) if data == "" { - account = NewAccount(big.NewInt(0)) + account = NewAccount(addr, big.NewInt(0)) } else { - account = NewAccountFromData([]byte(data)) + account = NewAccountFromData(addr, []byte(data)) } return diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 07e7ea970..57df9cdc4 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -29,15 +29,6 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { return &tx } -// Implements Callee -func (tx *Transaction) ReturnGas(value *big.Int, state *State) { - // Return the value back to the sender - sender := tx.Sender() - account := state.GetAccount(sender) - account.AddFunds(value) - state.UpdateAccount(sender, account) -} - // XXX Deprecated func NewTransactionFromData(data []byte) *Transaction { return NewTransactionFromBytes(data) diff --git a/ethchain/vm.go b/ethchain/vm.go index 6479409f8..3d85e2c09 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -87,6 +87,10 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // Pop value of the stack val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) + + case oCALLDATA: + offset := stack.Pop() + mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args) case oCALL: // Pop return size and offset retSize, retOffset := stack.Popn() diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 654ddb566..30c8a110e 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -133,10 +133,10 @@ func TestRun3(t *testing.T) { state.UpdateContract(addr, contract) callerScript := Compile([]string{ - "PUSH", "62", // REND - "PUSH", "0", // RSTART - "PUSH", "22", // MEND - "PUSH", "15", // MSTART + "PUSH", "62", // ret size + "PUSH", "0", // ret offset + "PUSH", "32", // arg size + "PUSH", "63", // arg offset "PUSH", "1000", /// Gas "PUSH", "0", /// value "PUSH", string(addr), // Sender @@ -144,10 +144,9 @@ func TestRun3(t *testing.T) { }) callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) callerAddr := callerTx.Hash()[12:] - executer := NewTransaction(ContractAddr, ethutil.Big("10000"), nil) - executer.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) - callerClosure := NewClosure(executer, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) + account := NewAccount(ContractAddr, big.NewInt(10000000)) + callerClosure := NewClosure(account, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ address: callerAddr, From f567f89b994bf28f908410223084a6702d05d156 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 23:38:16 +0100 Subject: [PATCH 14/20] Added address to account and contract Contract and account now both have an address field or method for the sake of simplicity. --- ethchain/closure.go | 1 + ethchain/contract.go | 38 +++++++++++++++++++++++++------------- ethchain/state.go | 7 ++++--- ethchain/vm.go | 7 +++---- ethchain/vm_test.go | 11 +++++------ 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 9453ce22c..0eef866d0 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -15,6 +15,7 @@ type ClosureBody interface { Callee ethutil.RlpEncodable GetMem(int64) *ethutil.Value + Address() []byte } // Basic inline closure object which implement the 'closure' interface diff --git a/ethchain/contract.go b/ethchain/contract.go index 10c5e2df6..93d2b68ba 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -9,26 +9,22 @@ type Contract struct { Amount *big.Int Nonce uint64 //state *ethutil.Trie - state *State + state *State + address []byte } -func NewContract(Amount *big.Int, root []byte) *Contract { - contract := &Contract{Amount: Amount, Nonce: 0} +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 (c *Contract) RlpEncode() []byte { - return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root}) -} +func NewContractFromBytes(address, data []byte) *Contract { + contract := &Contract{address: address} + contract.RlpDecode(data) -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())) + return contract } func (c *Contract) Addr(addr []byte) *ethutil.Value { @@ -54,13 +50,29 @@ 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(value, []byte("")) + contract := NewContract(addr, value, []byte("")) state.trie.Update(string(addr), string(contract.RlpEncode())) for i, val := range tx.Data { if len(val) > 0 { diff --git a/ethchain/state.go b/ethchain/state.go index b6750d62d..c9b35da21 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -63,8 +63,7 @@ func (s *State) GetContract(addr []byte) *Contract { } // build contract - contract := &Contract{} - contract.RlpDecode([]byte(data)) + contract := NewContractFromBytes(addr, []byte(data)) // Check if there's a cached state for this contract cachedState := s.states[string(addr)] @@ -78,7 +77,9 @@ func (s *State) GetContract(addr []byte) *Contract { return contract } -func (s *State) UpdateContract(addr []byte, contract *Contract) { +func (s *State) UpdateContract(contract *Contract) { + addr := contract.Address() + s.states[string(addr)] = contract.state s.trie.Update(string(addr), string(contract.RlpEncode())) } diff --git a/ethchain/vm.go b/ethchain/vm.go index 3d85e2c09..ba19231cc 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -23,14 +23,12 @@ type Vm struct { } type RuntimeVars struct { - address []byte + origin []byte blockNumber uint64 - sender []byte prevHash []byte coinbase []byte time int64 diff *big.Int - txValue *big.Int txData []string } @@ -108,6 +106,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { closure := NewClosure(closure, contract, vm.state, gas, value) // Executer the closure and get the return value (if any) ret := closure.Call(vm, nil) + mem.Set(retOffset.Int64(), retSize.Int64(), ret) case oRETURN: size, offset := stack.Popn() @@ -501,7 +500,7 @@ func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, tx := NewTransaction(addr, value, dataItems) if tx.IsContract() { contract := MakeContract(tx, state) - state.UpdateContract(tx.Hash()[12:], contract) + state.UpdateContract(contract) } else { account := state.GetAccount(tx.Recipient) account.Amount.Add(account.Amount, tx.Value) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 30c8a110e..1dca5cfb7 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -130,7 +130,7 @@ func TestRun3(t *testing.T) { addr := tx.Hash()[12:] fmt.Printf("addr contract %x\n", addr) contract := MakeContract(tx, state) - state.UpdateContract(addr, contract) + state.UpdateContract(contract) callerScript := Compile([]string{ "PUSH", "62", // ret size @@ -143,21 +143,20 @@ func TestRun3(t *testing.T) { "CALL", }) callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) - callerAddr := callerTx.Hash()[12:] + // Contract addr as test address account := NewAccount(ContractAddr, big.NewInt(10000000)) callerClosure := NewClosure(account, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ - address: callerAddr, + origin: account.Address, blockNumber: 1, - sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), time: 1, diff: big.NewInt(256), - txValue: big.NewInt(10000), - txData: nil, + // XXX Tx data? Could be just an argument to the closure instead + txData: nil, }) callerClosure.Call(vm, nil) } From 9cf8ce9ef82bfb37fea92bbea6a8d326af00adc8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 00:04:31 +0100 Subject: [PATCH 15/20] New tx methods and added new vm to state manager --- ethchain/state_manager.go | 16 +++++++--------- ethchain/transaction.go | 21 +++++++++++++++------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 14686b217..50c777349 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -305,19 +305,17 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo } }() */ - - /*TODO to be fixed and replaced by the new vm - vm := &Vm{} - vm.Process(contract, sm.procState, RuntimeVars{ - address: tx.Hash()[12:], + 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, - sender: tx.Sender(), prevHash: block.PrevHash, coinbase: block.Coinbase, time: block.Time, diff: block.Difficulty, - txValue: tx.Value, - txData: tx.Data, + // XXX Tx data? Could be just an argument to the closure instead + txData: nil, }) - */ + closure.Call(vm, nil) } diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 57df9cdc4..3b07c81d4 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -13,22 +13,31 @@ type Transaction struct { Nonce uint64 Recipient []byte Value *big.Int + Gas *big.Int + Gasprice *big.Int Data []string - Memory []int v byte r, s []byte } func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { - tx := Transaction{Recipient: to, Value: value} - tx.Nonce = 0 - - // Serialize the data - tx.Data = data + 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} +} + +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 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) From fa1db8d2dcbc12fd9b343e6572c541d92fe7cb55 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 11:54:36 +0100 Subject: [PATCH 16/20] Implemented closure arguments --- ethchain/stack.go | 28 +++++-- ethchain/vm.go | 184 +++++++++++++++++++++++++++++++++++++++++--- ethchain/vm_test.go | 29 +++++-- ethutil/parsing.go | 6 +- 4 files changed, 225 insertions(+), 22 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index 429c31d08..c75d02dda 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -68,12 +68,16 @@ const ( oJUMP = 0x59 oJUMPI = 0x5a oPC = 0x5b - oMEMSIZE = 0x5c + 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 @@ -136,12 +140,16 @@ var opCodeToString = map[OpCode]string{ oJUMP: "JUMP", oJUMPI: "JUMPI", oPC: "PC", - oMEMSIZE: "MEMSIZE", + oMSIZE: "MSIZE", // 0x60 range - closures oCREATE: "CREATE", oCALL: "CALL", oRETURN: "RETURN", + + // 0x70 range - other + oLOG: "LOG", + oSUICIDE: "SUICIDE", } func (o OpCode) String() string { @@ -215,20 +223,30 @@ type Memory struct { func (m *Memory) Set(offset, size int64, value []byte) { totSize := offset + size - lenSize := int64(len(m.store)) + lenSize := int64(len(m.store) - 1) if totSize > lenSize { // Calculate the diff between the sizes diff := totSize - lenSize if diff > 0 { // Create a new empty slice and append it - newSlice := make([]byte, diff+1) + newSlice := make([]byte, diff-1) // Resize slice m.store = append(m.store, newSlice...) } } - copy(m.store[offset:offset+size+1], value) + copy(m.store[offset:offset+size], value) } func (m *Memory) Get(offset, size int64) []byte { return m.store[offset : offset+size] } + +func (m *Memory) Print() { + fmt.Println("### MEM ###") + if len(m.store) > 0 { + fmt.Println(m.store) + } else { + fmt.Println("-- empty --") + } + fmt.Println("###########") +} diff --git a/ethchain/vm.go b/ethchain/vm.go index ba19231cc..3d2ee4c86 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -2,7 +2,7 @@ package ethchain import ( _ "bytes" - "fmt" + _ "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" "log" @@ -36,6 +36,8 @@ func NewVm(state *State, vars RuntimeVars) *Vm { return &Vm{vars: vars, state: state} } +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.GetGas().Cmp(big.NewInt(0)) <= 0 { @@ -48,9 +50,10 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { stack := NewStack() // Instruction pointer pc := int64(0) - // Current address - //addr := vars.address + // 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") @@ -75,27 +78,171 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } switch op { + case oLOG: + stack.Print() + mem.Print() case oSTOP: // Stop the closure return closure.Return(nil) + + // 0x20 range + case oADD: + 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: + 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: + 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: + x, y := stack.Popn() + // floor(x / y) + base.Div(x, y) + // Pop result back on the stack + stack.Push(base) + case oSDIV: + x, y := stack.Popn() + // n > 2**255 + if x.Cmp(Pow256) > 0 { + x.Sub(Pow256, x) + } + if y.Cmp(Pow256) > 0 { + y.Sub(Pow256, y) + } + z := new(big.Int) + z.Div(x, y) + if z.Cmp(Pow256) > 0 { + z.Sub(Pow256, z) + } + // Push result on to the stack + stack.Push(z) + case oMOD: + x, y := stack.Popn() + base.Mod(x, y) + stack.Push(base) + case oSMOD: + x, y := stack.Popn() + // n > 2**255 + if x.Cmp(Pow256) > 0 { + x.Sub(Pow256, x) + } + if y.Cmp(Pow256) > 0 { + y.Sub(Pow256, y) + } + z := new(big.Int) + z.Mod(x, y) + if z.Cmp(Pow256) > 0 { + z.Sub(Pow256, z) + } + // Push result on to the stack + stack.Push(z) + case oEXP: + x, y := stack.Popn() + base.Exp(x, y, Pow256) + + stack.Push(base) + case oNEG: + base.Sub(Pow256, stack.Pop()) + stack.Push(base) + case oLT: + x, y := stack.Popn() + // x < y + if x.Cmp(y) < 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case oGT: + x, y := stack.Popn() + // x > y + if x.Cmp(y) > 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case oNOT: + x, y := stack.Popn() + // x != y + if x.Cmp(y) != 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + + // 0x10 range + case oAND: + case oOR: + case oXOR: + case oBYTE: + + // 0x20 range + case oSHA3: + + // 0x30 range + case oADDRESS: + case oBALANCE: + case oORIGIN: + case oCALLER: + case oCALLVALUE: + case oCALLDATA: + offset := stack.Pop() + mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args) + case oCALLDATASIZE: + case oRETURNDATASIZE: + case oTXGASPRICE: + + // 0x40 range + case oPREVHASH: + case oPREVNONCE: + case oCOINBASE: + case oTIMESTAMP: + case oNUMBER: + case oDIFFICULTY: + case oGASLIMIT: + + // 0x50 range case oPUSH: // Push PC+1 on to the stack pc++ val := closure.GetMem(pc).BigInt() stack.Push(val) + case oPOP: + case oDUP: + case oSWAP: + case oMLOAD: + 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 // Pop value of the stack val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) + case oMSTORE8: + case oSLOAD: + case oSSTORE: + case oJUMP: + case oJUMPI: + case oPC: + case oMSIZE: - case oCALLDATA: - offset := stack.Pop() - mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args) + // 0x60 range case oCALL: // Pop return size and offset retSize, retOffset := stack.Popn() // Pop input size and offset inSize, inOffset := stack.Popn() - // TODO remove me. - fmt.Sprintln(inSize, inOffset) + // 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 @@ -105,7 +252,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // 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, nil) + ret := closure.Call(vm, args) mem.Set(retOffset.Int64(), retSize.Int64(), ret) case oRETURN: @@ -113,6 +260,25 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { ret := mem.Get(offset.Int64(), size.Int64()) return closure.Return(ret) + 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) } pc++ diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 1dca5cfb7..ce8c7a4de 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -1,6 +1,7 @@ package ethchain import ( + "bytes" "fmt" "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethutil" @@ -119,11 +120,13 @@ func TestRun3(t *testing.T) { "PUSH", "300", "PUSH", "0", "MSTORE", - "PUSH", "300", - "PUSH", "31", - "MSTORE", - "PUSH", "62", + + "PUSH", "32", + "CALLDATA", + + "PUSH", "64", "PUSH", "0", + "LOG", "RETURN", }) tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) @@ -133,14 +136,21 @@ func TestRun3(t *testing.T) { state.UpdateContract(contract) callerScript := Compile([]string{ - "PUSH", "62", // ret size + "PUSH", "1337", // Argument + "PUSH", "65", // argument mem offset + "MSTORE", + "PUSH", "64", // ret size "PUSH", "0", // ret offset + "PUSH", "32", // arg size - "PUSH", "63", // arg offset + "PUSH", "65", // arg offset "PUSH", "1000", /// Gas "PUSH", "0", /// value "PUSH", string(addr), // Sender "CALL", + "PUSH", "64", + "PUSH", "0", + "RETURN", }) callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) @@ -158,5 +168,10 @@ func TestRun3(t *testing.T) { // XXX Tx data? Could be just an argument to the closure instead txData: nil, }) - callerClosure.Call(vm, nil) + ret := callerClosure.Call(vm, nil) + + exp := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 57} + if bytes.Compare(ret, exp) != 0 { + t.Errorf("expected return value to be %v, got %v", exp, ret) + } } diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 9ff2827a0..b2e9d9fee 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -65,12 +65,16 @@ var OpCodes = map[string]byte{ "JUMP": 0x59, "JUMPI": 0x5a, "PC": 0x5b, - "MEMSIZE": 0x5c, + "MSIZE": 0x5c, // 0x60 range - closures "CREATE": 0x60, "CALL": 0x61, "RETURN": 0x62, + + // 0x70 range - other + "LOG": 0x70, + "SUICIDE": 0x7f, } func IsOpCode(s string) bool { From 2ea4c632d1673b762c1af11582364d9faa08c413 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 14:47:55 +0100 Subject: [PATCH 17/20] Closure return, arguments fixed. Added proper tests --- ethchain/address.go | 10 +++++--- ethchain/closure.go | 24 ++++++++++++------ ethchain/stack.go | 52 +++++++++++++++++++-------------------- ethchain/state_manager.go | 2 +- ethchain/vm.go | 33 +++++++++++++++++++++---- ethchain/vm_test.go | 3 +-- ethutil/parsing.go | 28 ++++++++++----------- 7 files changed, 92 insertions(+), 60 deletions(-) diff --git a/ethchain/address.go b/ethchain/address.go index 9c6acbe08..0b3ef7c05 100644 --- a/ethchain/address.go +++ b/ethchain/address.go @@ -6,7 +6,7 @@ import ( ) type Account struct { - Address []byte + address []byte Amount *big.Int Nonce uint64 } @@ -16,7 +16,7 @@ func NewAccount(address []byte, amount *big.Int) *Account { } func NewAccountFromData(address, data []byte) *Account { - account := &Account{Address: address} + account := &Account{address: address} account.RlpDecode(data) return account @@ -30,11 +30,15 @@ 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) + state.UpdateAccount(a.address, a) } func (a *Account) RlpEncode() []byte { diff --git a/ethchain/closure.go b/ethchain/closure.go index 0eef866d0..f8e692f61 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -9,13 +9,13 @@ import ( type Callee interface { ReturnGas(*big.Int, *State) + Address() []byte } type ClosureBody interface { Callee ethutil.RlpEncodable GetMem(int64) *ethutil.Value - Address() []byte } // Basic inline closure object which implement the 'closure' interface @@ -24,8 +24,8 @@ type Closure struct { object ClosureBody State *State - gas *big.Int - val *big.Int + Gas *big.Int + Value *big.Int Args []byte } @@ -45,6 +45,10 @@ func (c *Closure) GetMem(x int64) *ethutil.Value { return m } +func (c *Closure) Address() []byte { + return c.object.Address() +} + func (c *Closure) Call(vm *Vm, args []byte) []byte { c.Args = args @@ -56,9 +60,9 @@ 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.State) } else { - c.object.ReturnGas(c.gas, c.State) + 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 } @@ -69,9 +73,13 @@ func (c *Closure) Return(ret []byte) []byte { // Implement the Callee interface func (c *Closure) ReturnGas(gas *big.Int, state *State) { // Return the gas to the closure - c.gas.Add(c.gas, gas) + c.Gas.Add(c.Gas, gas) } -func (c *Closure) GetGas() *big.Int { - return c.gas +func (c *Closure) Object() ClosureBody { + return c.object +} + +func (c *Closure) Callee() Callee { + return c.callee } diff --git a/ethchain/stack.go b/ethchain/stack.go index c75d02dda..b64b759fd 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -36,24 +36,22 @@ const ( oSHA3 = 0x20 // 0x30 range - closure state - oADDRESS = 0x30 - oBALANCE = 0x31 - oORIGIN = 0x32 - oCALLER = 0x33 - oCALLVALUE = 0x34 - oCALLDATA = 0x35 - oCALLDATASIZE = 0x36 - oRETURNDATASIZE = 0x37 - oTXGASPRICE = 0x38 + oADDRESS = 0x30 + oBALANCE = 0x31 + oORIGIN = 0x32 + oCALLER = 0x33 + oCALLVALUE = 0x34 + oCALLDATA = 0x35 + oCALLDATASIZE = 0x36 + oGASPRICE = 0x37 // 0x40 range - block operations oPREVHASH = 0x40 - oPREVNONCE = 0x41 - oCOINBASE = 0x42 - oTIMESTAMP = 0x43 - oNUMBER = 0x44 - oDIFFICULTY = 0x45 - oGASLIMIT = 0x46 + oCOINBASE = 0x41 + oTIMESTAMP = 0x42 + oNUMBER = 0x43 + oDIFFICULTY = 0x44 + oGASLIMIT = 0x45 // 0x50 range - 'storage' and execution oPUSH = 0x50 @@ -108,19 +106,17 @@ var opCodeToString = map[OpCode]string{ oSHA3: "SHA3", // 0x30 range - closure state - oADDRESS: "ADDRESS", - oBALANCE: "BALANCE", - oORIGIN: "ORIGIN", - oCALLER: "CALLER", - oCALLVALUE: "CALLVALUE", - oCALLDATA: "CALLDATA", - oCALLDATASIZE: "CALLDATASIZE", - oRETURNDATASIZE: "RETURNDATASIZE", - oTXGASPRICE: "TXGASPRICE", + oADDRESS: "ADDRESS", + oBALANCE: "BALANCE", + oORIGIN: "ORIGIN", + oCALLER: "CALLER", + oCALLVALUE: "CALLVALUE", + oCALLDATA: "CALLDATA", + oCALLDATASIZE: "CALLDATASIZE", + oGASPRICE: "TXGASPRICE", // 0x40 range - block operations oPREVHASH: "PREVHASH", - oPREVNONCE: "PREVNONCE", oCOINBASE: "COINBASE", oTIMESTAMP: "TIMESTAMP", oNUMBER: "NUMBER", @@ -244,7 +240,11 @@ func (m *Memory) Get(offset, size int64) []byte { func (m *Memory) Print() { fmt.Println("### MEM ###") if len(m.store) > 0 { - fmt.Println(m.store) + addr := 0 + for i := 0; i+32 < len(m.store); i += 32 { + fmt.Printf("%03d %v\n", addr, m.store[i:i+32]) + addr++ + } } else { fmt.Println("-- empty --") } diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 50c777349..3b5507740 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -308,7 +308,7 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo caller := sm.procState.GetAccount(tx.Sender()) closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ - origin: caller.Address, + origin: caller.Address(), blockNumber: block.BlockInfo().Number, prevHash: block.PrevHash, coinbase: block.Coinbase, diff --git a/ethchain/vm.go b/ethchain/vm.go index 3d2ee4c86..8b5bb93c0 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -40,7 +40,7 @@ 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.GetGas().Cmp(big.NewInt(0)) <= 0 { + if closure.Gas.Cmp(big.NewInt(0)) <= 0 { // TODO Do something } @@ -73,7 +73,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { fee := new(big.Int) fee.Add(fee, big.NewInt(1000)) - if closure.GetGas().Cmp(fee) < 0 { + if closure.Gas.Cmp(fee) < 0 { return closure.Return(nil) } @@ -192,25 +192,37 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // 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)) 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 oCALLDATASIZE: - case oRETURNDATASIZE: - case oTXGASPRICE: + stack.Push(big.NewInt(int64(len(closure.Args)))) + case oGASPRICE: + // TODO // 0x40 range case oPREVHASH: - case oPREVNONCE: + stack.Push(ethutil.BigD(vm.vars.prevHash)) case oCOINBASE: + stack.Push(ethutil.BigD(vm.vars.coinbase)) case oTIMESTAMP: + stack.Push(big.NewInt(vm.vars.time)) case oNUMBER: + stack.Push(big.NewInt(int64(vm.vars.blockNumber))) case oDIFFICULTY: + stack.Push(vm.vars.diff) case oGASLIMIT: + // TODO // 0x50 range case oPUSH: // Push PC+1 on to the stack @@ -218,8 +230,13 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { val := closure.GetMem(pc).BigInt() stack.Push(val) case oPOP: + stack.Pop() case oDUP: + stack.Push(stack.Peek()) case oSWAP: + x, y := stack.Popn() + stack.Push(y) + stack.Push(x) case oMLOAD: offset := stack.Pop() stack.Push(ethutil.BigD(mem.Get(offset.Int64(), 32))) @@ -228,7 +245,13 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) case oMSTORE8: + val, mStart := stack.Popn() + base.And(val, new(big.Int).SetInt64(0xff)) + mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) case oSLOAD: + loc := stack.Pop() + val := closure.GetMem(loc.Int64()) + stack.Push(val.BigInt()) case oSSTORE: case oJUMP: case oJUMPI: diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index ce8c7a4de..16cbf51b7 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -126,7 +126,6 @@ func TestRun3(t *testing.T) { "PUSH", "64", "PUSH", "0", - "LOG", "RETURN", }) tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) @@ -159,7 +158,7 @@ func TestRun3(t *testing.T) { callerClosure := NewClosure(account, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ - origin: account.Address, + origin: account.Address(), blockNumber: 1, prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), diff --git a/ethutil/parsing.go b/ethutil/parsing.go index b2e9d9fee..f24402623 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -33,24 +33,22 @@ var OpCodes = map[string]byte{ "SHA3": 0x20, // 0x30 range - closure state - "ADDRESS": 0x30, - "BALANCE": 0x31, - "ORIGIN": 0x32, - "CALLER": 0x33, - "CALLVALUE": 0x34, - "CALLDATA": 0x35, - "CALLDATASIZE": 0x36, - "RETURNDATASIZE": 0x37, - "TXGASPRICE": 0x38, + "ADDRESS": 0x30, + "BALANCE": 0x31, + "ORIGIN": 0x32, + "CALLER": 0x33, + "CALLVALUE": 0x34, + "CALLDATA": 0x35, + "CALLDATASIZE": 0x36, + "GASPRICE": 0x38, // 0x40 range - block operations "PREVHASH": 0x40, - "PREVNONCE": 0x41, - "COINBASE": 0x42, - "TIMESTAMP": 0x43, - "NUMBER": 0x44, - "DIFFICULTY": 0x45, - "GASLIMIT": 0x46, + "COINBASE": 0x41, + "TIMESTAMP": 0x42, + "NUMBER": 0x43, + "DIFFICULTY": 0x44, + "GASLIMIT": 0x45, // 0x50 range - 'storage' and execution "PUSH": 0x50, From 9a9e252cabdc6283d7f4e523860f0e4addf62152 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 15:27:18 +0100 Subject: [PATCH 18/20] Changes 'compiler' to work with any type --- ethchain/vm_test.go | 28 ++++++++++++-------------- ethutil/parsing.go | 48 +++++++++++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 16cbf51b7..047531e09 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -2,7 +2,6 @@ package ethchain import ( "bytes" - "fmt" "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethutil" "math/big" @@ -130,27 +129,26 @@ func TestRun3(t *testing.T) { }) tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) addr := tx.Hash()[12:] - fmt.Printf("addr contract %x\n", addr) contract := MakeContract(tx, state) state.UpdateContract(contract) - callerScript := Compile([]string{ - "PUSH", "1337", // Argument - "PUSH", "65", // argument mem offset + callerScript := ethutil.Compile( + "PUSH", 1337, // Argument + "PUSH", 65, // argument mem offset "MSTORE", - "PUSH", "64", // ret size - "PUSH", "0", // ret offset + "PUSH", 64, // ret size + "PUSH", 0, // ret offset - "PUSH", "32", // arg size - "PUSH", "65", // arg offset - "PUSH", "1000", /// Gas - "PUSH", "0", /// value - "PUSH", string(addr), // Sender + "PUSH", 32, // arg size + "PUSH", 65, // arg offset + "PUSH", 1000, /// Gas + "PUSH", 0, /// value + "PUSH", addr, // Sender "CALL", - "PUSH", "64", - "PUSH", "0", + "PUSH", 64, + "PUSH", 0, "RETURN", - }) + ) callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) // Contract addr as test address diff --git a/ethutil/parsing.go b/ethutil/parsing.go index f24402623..8929f0829 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -84,20 +84,30 @@ func IsOpCode(s string) bool { return false } -func CompileInstr(s string) ([]byte, error) { - isOp := IsOpCode(s) - if isOp { - return []byte{OpCodes[s]}, nil +func CompileInstr(s interface{}) ([]byte, error) { + switch s.(type) { + case string: + str := s.(string) + isOp := IsOpCode(str) + if isOp { + return []byte{OpCodes[str]}, nil + } + + num := new(big.Int) + _, success := num.SetString(str, 0) + // Assume regular bytes during compilation + if !success { + num.SetBytes([]byte(str)) + } + + return num.Bytes(), nil + case int: + return big.NewInt(int64(s.(int))).Bytes(), nil + case []byte: + return BigD(s.([]byte)).Bytes(), nil } - num := new(big.Int) - _, success := num.SetString(s, 0) - // Assume regular bytes during compilation - if !success { - num.SetBytes([]byte(s)) - } - - return num.Bytes(), nil + return nil, nil } func Instr(instr string) (int, []string, error) { @@ -118,3 +128,17 @@ func Instr(instr string) (int, []string, error) { return op, args[1:7], nil } + +// Script compilation functions +// Compiles strings to machine code +func Compile(instructions ...interface{}) (script []string) { + script = make([]string, len(instructions)) + + for i, val := range instructions { + instr, _ := CompileInstr(val) + + script[i] = string(instr) + } + + return +} From 01c1bce9c5dfa2b2bcdf934afec3f206823f895f Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 18:22:47 +0100 Subject: [PATCH 19/20] Removed regular ints from the virtual machine and closures --- ethchain/closure.go | 9 +++++++-- ethchain/contract.go | 9 +++++++-- ethchain/stack.go | 4 ++++ ethchain/vm.go | 21 +++++++++++++++------ ethutil/common.go | 6 ++++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index f8e692f61..2e809aa9d 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -15,7 +15,8 @@ type Callee interface { type ClosureBody interface { Callee ethutil.RlpEncodable - GetMem(int64) *ethutil.Value + GetMem(*big.Int) *ethutil.Value + SetMem(*big.Int, *ethutil.Value) } // Basic inline closure object which implement the 'closure' interface @@ -36,7 +37,7 @@ func NewClosure(callee Callee, object ClosureBody, state *State, gas, val *big.I } // Retuns the x element in data slice -func (c *Closure) GetMem(x int64) *ethutil.Value { +func (c *Closure) GetMem(x *big.Int) *ethutil.Value { m := c.object.GetMem(x) if m == nil { return ethutil.EmptyValue() @@ -45,6 +46,10 @@ func (c *Closure) GetMem(x int64) *ethutil.Value { return m } +func (c *Closure) SetMem(x *big.Int, val *ethutil.Value) { + c.object.SetMem(x, val) +} + func (c *Closure) Address() []byte { return c.object.Address() } diff --git a/ethchain/contract.go b/ethchain/contract.go index 93d2b68ba..f7ae01753 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -39,12 +39,17 @@ func (c *Contract) State() *State { return c.state } -func (c *Contract) GetMem(num int64) *ethutil.Value { - nb := ethutil.BigToBytes(big.NewInt(num), 256) +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) diff --git a/ethchain/stack.go b/ethchain/stack.go index b64b759fd..3c2899e62 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -237,6 +237,10 @@ func (m *Memory) Get(offset, size int64) []byte { return m.store[offset : offset+size] } +func (m *Memory) Len() int { + return len(m.store) +} + func (m *Memory) Print() { fmt.Println("### MEM ###") if len(m.store) > 0 { diff --git a/ethchain/vm.go b/ethchain/vm.go index 8b5bb93c0..bc3a9edaf 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -49,7 +49,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // New stack (should this be shared?) stack := NewStack() // Instruction pointer - pc := int64(0) + pc := big.NewInt(0) // Current step count step := 0 // The base for all big integer arithmetic @@ -226,7 +226,8 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // 0x50 range case oPUSH: // Push PC+1 on to the stack - pc++ + pc.Add(pc, ethutil.Big1) + val := closure.GetMem(pc).BigInt() stack.Push(val) case oPOP: @@ -250,14 +251,22 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) case oSLOAD: loc := stack.Pop() - val := closure.GetMem(loc.Int64()) + val := closure.GetMem(loc) stack.Push(val.BigInt()) case oSSTORE: + val, loc := stack.Popn() + closure.SetMem(loc, ethutil.NewValue(val)) case oJUMP: + pc = stack.Pop() case oJUMPI: + pos, cond := stack.Popn() + if cond.Cmp(big.NewInt(0)) > 0 { + pc = pos + } case oPC: + stack.Push(pc) case oMSIZE: - + stack.Push(big.NewInt(int64(mem.Len()))) // 0x60 range case oCALL: // Pop return size and offset @@ -304,7 +313,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { ethutil.Config.Log.Debugln("Invalid opcode", op) } - pc++ + pc.Add(pc, ethutil.Big1) } } @@ -682,7 +691,7 @@ func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, j := int64(0) dataItems := make([]string, int(length.Uint64())) for i := from.Int64(); i < length.Int64(); i++ { - dataItems[j] = contract.GetMem(j).Str() + dataItems[j] = contract.GetMem(big.NewInt(j)).Str() j++ } diff --git a/ethutil/common.go b/ethutil/common.go index 07df6bb13..f15b10e6d 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -33,3 +33,9 @@ func CurrencyToString(num *big.Int) string { return fmt.Sprintf("%v Wei", num) } + +var ( + Big1 = big.NewInt(1) + Big0 = big.NewInt(0) + Big256 = big.NewInt(0xff) +) From 6a86c517c4f4b372cad0ae1d92e926a482eac5ba Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 22 Mar 2014 11:47:27 +0100 Subject: [PATCH 20/20] Removed old VM code --- README.md | 2 +- ethchain/state.go | 4 + ethchain/vm.go | 369 ---------------------------------------------- 3 files changed, 5 insertions(+), 370 deletions(-) diff --git a/README.md b/README.md index 3553a5e35..0f0a33edb 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". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 3.5". 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/state.go b/ethchain/state.go index c9b35da21..1860647f2 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -149,6 +149,10 @@ 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) { diff --git a/ethchain/vm.go b/ethchain/vm.go index bc3a9edaf..126592b25 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -317,375 +317,6 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } } -/* -// Old VM code -func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) { - vm.mem = make(map[string]*big.Int) - vm.stack = NewStack() - - addr := vars.address // tx.Hash()[12:] - // Instruction pointer - pc := int64(0) - - if contract == nil { - fmt.Println("Contract not found") - return - } - - Pow256 := ethutil.BigPow(2, 256) - - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("# op\n") - } - - stepcount := 0 - totalFee := new(big.Int) - -out: - for { - stepcount++ - // The base big int for all calculations. Use this for any results. - base := new(big.Int) - val := contract.GetMem(pc) - //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb) - op := OpCode(val.Uint()) - - var fee *big.Int = new(big.Int) - var fee2 *big.Int = new(big.Int) - if stepcount > 16 { - fee.Add(fee, StepFee) - } - - // Calculate the fees - switch op { - case oSSTORE: - y, x := vm.stack.Peekn() - val := contract.Addr(ethutil.BigToBytes(x, 256)) - if val.IsEmpty() && len(y.Bytes()) > 0 { - fee2.Add(DataFee, StoreFee) - } else { - fee2.Sub(DataFee, StoreFee) - } - case oSLOAD: - fee.Add(fee, StoreFee) - case oEXTRO, oBALANCE: - fee.Add(fee, ExtroFee) - case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID: - fee.Add(fee, CryptoFee) - case oMKTX: - fee.Add(fee, ContractFee) - } - - tf := new(big.Int).Add(fee, fee2) - if contract.Amount.Cmp(tf) < 0 { - fmt.Println("Insufficient fees to continue running the contract", tf, contract.Amount) - break - } - // Add the fee to the total fee. It's subtracted when we're done looping - totalFee.Add(totalFee, tf) - - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) - } - - switch op { - case oSTOP: - fmt.Println("") - break out - case oADD: - x, y := vm.stack.Popn() - // (x + y) % 2 ** 256 - base.Add(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - vm.stack.Push(base) - case oSUB: - x, y := vm.stack.Popn() - // (x - y) % 2 ** 256 - base.Sub(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - vm.stack.Push(base) - case oMUL: - x, y := vm.stack.Popn() - // (x * y) % 2 ** 256 - base.Mul(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - vm.stack.Push(base) - case oDIV: - x, y := vm.stack.Popn() - // floor(x / y) - base.Div(x, y) - // Pop result back on the stack - vm.stack.Push(base) - case oSDIV: - x, y := vm.stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Div(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) - } - // Push result on to the stack - vm.stack.Push(z) - case oMOD: - x, y := vm.stack.Popn() - base.Mod(x, y) - vm.stack.Push(base) - case oSMOD: - x, y := vm.stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Mod(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) - } - // Push result on to the stack - vm.stack.Push(z) - case oEXP: - x, y := vm.stack.Popn() - base.Exp(x, y, Pow256) - - vm.stack.Push(base) - case oNEG: - base.Sub(Pow256, vm.stack.Pop()) - vm.stack.Push(base) - case oLT: - x, y := vm.stack.Popn() - // x < y - if x.Cmp(y) < 0 { - vm.stack.Push(ethutil.BigTrue) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oLE: - x, y := vm.stack.Popn() - // x <= y - if x.Cmp(y) < 1 { - vm.stack.Push(ethutil.BigTrue) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oGT: - x, y := vm.stack.Popn() - // x > y - if x.Cmp(y) > 0 { - vm.stack.Push(ethutil.BigTrue) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oGE: - x, y := vm.stack.Popn() - // x >= y - if x.Cmp(y) > -1 { - vm.stack.Push(ethutil.BigTrue) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oNOT: - x, y := vm.stack.Popn() - // x != y - if x.Cmp(y) != 0 { - vm.stack.Push(ethutil.BigTrue) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oMYADDRESS: - vm.stack.Push(ethutil.BigD(addr)) - case oTXSENDER: - vm.stack.Push(ethutil.BigD(vars.sender)) - case oTXVALUE: - vm.stack.Push(vars.txValue) - case oTXDATAN: - vm.stack.Push(big.NewInt(int64(len(vars.txData)))) - case oTXDATA: - v := vm.stack.Pop() - // v >= len(data) - if v.Cmp(big.NewInt(int64(len(vars.txData)))) >= 0 { - vm.stack.Push(ethutil.Big("0")) - } else { - vm.stack.Push(ethutil.Big(vars.txData[v.Uint64()])) - } - case oBLK_PREVHASH: - vm.stack.Push(ethutil.BigD(vars.prevHash)) - case oBLK_COINBASE: - vm.stack.Push(ethutil.BigD(vars.coinbase)) - case oBLK_TIMESTAMP: - vm.stack.Push(big.NewInt(vars.time)) - case oBLK_NUMBER: - vm.stack.Push(big.NewInt(int64(vars.blockNumber))) - case oBLK_DIFFICULTY: - vm.stack.Push(vars.diff) - case oBASEFEE: - // e = 10^21 - e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0)) - d := new(big.Rat) - d.SetInt(vars.diff) - c := new(big.Rat) - c.SetFloat64(0.5) - // d = diff / 0.5 - d.Quo(d, c) - // base = floor(d) - base.Div(d.Num(), d.Denom()) - - x := new(big.Int) - x.Div(e, base) - - // x = floor(10^21 / floor(diff^0.5)) - vm.stack.Push(x) - case oSHA256, oSHA3, oRIPEMD160: - // This is probably save - // ceil(pop / 32) - length := int(math.Ceil(float64(vm.stack.Pop().Uint64()) / 32.0)) - // New buffer which will contain the concatenated popped items - data := new(bytes.Buffer) - for i := 0; i < length; i++ { - // Encode the number to bytes and have it 32bytes long - num := ethutil.NumberToBytes(vm.stack.Pop().Bytes(), 256) - data.WriteString(string(num)) - } - - if op == oSHA256 { - vm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes()))) - } else if op == oSHA3 { - vm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes()))) - } else { - vm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes()))) - } - case oECMUL: - y := vm.stack.Pop() - x := vm.stack.Pop() - //n := vm.stack.Pop() - - //if ethutil.Big(x).Cmp(ethutil.Big(y)) { - data := new(bytes.Buffer) - data.WriteString(x.String()) - data.WriteString(y.String()) - if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 { - // TODO - } else { - // Invalid, push infinity - vm.stack.Push(ethutil.Big("0")) - vm.stack.Push(ethutil.Big("0")) - } - //} else { - // // Invalid, push infinity - // vm.stack.Push("0") - // vm.stack.Push("0") - //} - - case oECADD: - case oECSIGN: - case oECRECOVER: - case oECVALID: - case oPUSH: - pc++ - vm.stack.Push(contract.GetMem(pc).BigInt()) - case oPOP: - // Pop current value of the stack - vm.stack.Pop() - case oDUP: - // Dup top stack - x := vm.stack.Pop() - vm.stack.Push(x) - vm.stack.Push(x) - case oSWAP: - // Swap two top most values - x, y := vm.stack.Popn() - vm.stack.Push(y) - vm.stack.Push(x) - case oMLOAD: - x := vm.stack.Pop() - vm.stack.Push(vm.mem[x.String()]) - case oMSTORE: - x, y := vm.stack.Popn() - vm.mem[x.String()] = y - case oSLOAD: - // Load the value in storage and push it on the stack - x := vm.stack.Pop() - // decode the object as a big integer - decoder := contract.Addr(x.Bytes()) - if !decoder.IsNil() { - vm.stack.Push(decoder.BigInt()) - } else { - vm.stack.Push(ethutil.BigFalse) - } - case oSSTORE: - // Store Y at index X - y, x := vm.stack.Popn() - addr := ethutil.BigToBytes(x, 256) - fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr)) - contract.SetAddr(addr, y) - //contract.State().Update(string(idx), string(y)) - case oJMP: - x := vm.stack.Pop().Int64() - // Set pc to x - 1 (minus one so the incrementing at the end won't effect it) - pc = x - pc-- - case oJMPI: - x := vm.stack.Pop() - // Set pc to x if it's non zero - if x.Cmp(ethutil.BigFalse) != 0 { - pc = x.Int64() - pc-- - } - case oIND: - vm.stack.Push(big.NewInt(int64(pc))) - case oEXTRO: - memAddr := vm.stack.Pop() - contractAddr := vm.stack.Pop().Bytes() - - // Push the contract's memory on to the stack - vm.stack.Push(contractMemory(state, contractAddr, memAddr)) - case oBALANCE: - // Pushes the balance of the popped value on to the stack - account := state.GetAccount(vm.stack.Pop().Bytes()) - vm.stack.Push(account.Amount) - case oMKTX: - addr, value := vm.stack.Popn() - from, length := vm.stack.Popn() - - makeInlineTx(addr.Bytes(), value, from, length, contract, state) - case oSUICIDE: - recAddr := vm.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: - fmt.Printf("Invalid OPCODE: %x\n", op) - } - ethutil.Config.Log.Debugln("") - //vm.stack.Print() - pc++ - } - - state.UpdateContract(addr, contract) -} -*/ - 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)