From 38ea6a6d5dffb7573b29541c2a6687145bdcc495 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 17:26:07 +0100 Subject: Closures and vm based on closures Status: Work in progress --- ethchain/vm.go | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 5 deletions(-) (limited to 'ethchain/vm.go') 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++ } -- cgit v1.2.3 From c68ff9886bdd59294bc2bf0a6b8bf9b83645cc9a Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 19:50:53 +0100 Subject: Fixed MSTORE and added some more commets --- ethchain/vm.go | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) (limited to 'ethchain/vm.go') 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() -- cgit v1.2.3 From f3d27bf5d878120346f8cdd0744e7f1f8e1ee631 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 22:51:20 +0100 Subject: Rewrote opcodes again --- ethchain/vm.go | 41 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) (limited to 'ethchain/vm.go') 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) -- cgit v1.2.3 From 7705b23f248156878d00c301fbbadafedaf7e3d2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 23:17:53 +0100 Subject: 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/vm.go | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'ethchain/vm.go') 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() -- cgit v1.2.3 From f567f89b994bf28f908410223084a6702d05d156 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 20 Mar 2014 23:38:16 +0100 Subject: Added address to account and contract Contract and account now both have an address field or method for the sake of simplicity. --- ethchain/vm.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'ethchain/vm.go') 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) -- cgit v1.2.3 From fa1db8d2dcbc12fd9b343e6572c541d92fe7cb55 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 11:54:36 +0100 Subject: Implemented closure arguments --- ethchain/vm.go | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 175 insertions(+), 9 deletions(-) (limited to 'ethchain/vm.go') 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++ -- cgit v1.2.3 From 2ea4c632d1673b762c1af11582364d9faa08c413 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 14:47:55 +0100 Subject: Closure return, arguments fixed. Added proper tests --- ethchain/vm.go | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'ethchain/vm.go') 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: -- cgit v1.2.3 From 01c1bce9c5dfa2b2bcdf934afec3f206823f895f Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 21 Mar 2014 18:22:47 +0100 Subject: Removed regular ints from the virtual machine and closures --- ethchain/vm.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'ethchain/vm.go') 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++ } -- cgit v1.2.3 From 6a86c517c4f4b372cad0ae1d92e926a482eac5ba Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 22 Mar 2014 11:47:27 +0100 Subject: Removed old VM code --- ethchain/vm.go | 369 --------------------------------------------------------- 1 file changed, 369 deletions(-) (limited to 'ethchain/vm.go') 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) -- cgit v1.2.3