aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ethchain/closure.go25
-rw-r--r--ethchain/contract.go33
-rw-r--r--ethchain/stack.go3
-rw-r--r--ethchain/state_manager.go2
-rw-r--r--ethchain/transaction.go66
-rw-r--r--ethchain/transaction_test.go53
-rw-r--r--ethchain/vm.go63
-rw-r--r--ethchain/vm_test.go45
-rw-r--r--ethutil/parsing.go42
-rw-r--r--ethutil/rlp.go7
10 files changed, 176 insertions, 163 deletions
diff --git a/ethchain/closure.go b/ethchain/closure.go
index 2e809aa9d..8e57a0d03 100644
--- a/ethchain/closure.go
+++ b/ethchain/closure.go
@@ -12,7 +12,7 @@ type Callee interface {
Address() []byte
}
-type ClosureBody interface {
+type Reference interface {
Callee
ethutil.RlpEncodable
GetMem(*big.Int) *ethutil.Value
@@ -22,7 +22,8 @@ type ClosureBody interface {
// Basic inline closure object which implement the 'closure' interface
type Closure struct {
callee Callee
- object ClosureBody
+ object Reference
+ Script []byte
State *State
Gas *big.Int
@@ -32,8 +33,8 @@ type Closure struct {
}
// 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, nil}
+func NewClosure(callee Callee, object Reference, script []byte, state *State, gas, val *big.Int) *Closure {
+ return &Closure{callee, object, script, state, gas, val, nil}
}
// Retuns the x element in data slice
@@ -46,6 +47,20 @@ func (c *Closure) GetMem(x *big.Int) *ethutil.Value {
return m
}
+func (c *Closure) Get(x *big.Int) *ethutil.Value {
+ return c.Gets(x, big.NewInt(1))
+}
+
+func (c *Closure) Gets(x, y *big.Int) *ethutil.Value {
+ if x.Int64() > int64(len(c.Script)) || y.Int64() > int64(len(c.Script)) {
+ return ethutil.NewValue(0)
+ }
+
+ partial := c.Script[x.Int64() : x.Int64()+y.Int64()]
+
+ return ethutil.NewValue(partial)
+}
+
func (c *Closure) SetMem(x *big.Int, val *ethutil.Value) {
c.object.SetMem(x, val)
}
@@ -81,7 +96,7 @@ func (c *Closure) ReturnGas(gas *big.Int, state *State) {
c.Gas.Add(c.Gas, gas)
}
-func (c *Closure) Object() ClosureBody {
+func (c *Closure) Object() Reference {
return c.object
}
diff --git a/ethchain/contract.go b/ethchain/contract.go
index f7ae01753..e99e413f7 100644
--- a/ethchain/contract.go
+++ b/ethchain/contract.go
@@ -9,8 +9,10 @@ type Contract struct {
Amount *big.Int
Nonce uint64
//state *ethutil.Trie
- state *State
- address []byte
+ state *State
+ address []byte
+ script []byte
+ initScript []byte
}
func NewContract(address []byte, Amount *big.Int, root []byte) *Contract {
@@ -45,6 +47,14 @@ func (c *Contract) GetMem(num *big.Int) *ethutil.Value {
return c.Addr(nb)
}
+func (c *Contract) GetInstr(pc *big.Int) *ethutil.Value {
+ if int64(len(c.script)-1) < pc.Int64() {
+ return ethutil.NewValue(0)
+ }
+
+ return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]})
+}
+
func (c *Contract) SetMem(num *big.Int, val *ethutil.Value) {
addr := ethutil.BigToBytes(num, 256)
c.state.trie.Update(string(addr), string(val.Encode()))
@@ -60,7 +70,7 @@ func (c *Contract) Address() []byte {
}
func (c *Contract) RlpEncode() []byte {
- return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root})
+ return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root, c.script, c.initScript})
}
func (c *Contract) RlpDecode(data []byte) {
@@ -69,6 +79,8 @@ func (c *Contract) RlpDecode(data []byte) {
c.Amount = decoder.Get(0).BigInt()
c.Nonce = decoder.Get(1).Uint()
c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
+ c.script = decoder.Get(3).Bytes()
+ c.initScript = decoder.Get(4).Bytes()
}
func MakeContract(tx *Transaction, state *State) *Contract {
@@ -79,12 +91,17 @@ func MakeContract(tx *Transaction, state *State) *Contract {
value := tx.Value
contract := NewContract(addr, value, []byte(""))
state.trie.Update(string(addr), string(contract.RlpEncode()))
- for i, val := range tx.Data {
- if len(val) > 0 {
- bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
- contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val)))
+ contract.script = tx.Data
+ contract.initScript = tx.Init
+
+ /*
+ for i, val := range tx.Data {
+ if len(val) > 0 {
+ bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
+ contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val)))
+ }
}
- }
+ */
state.trie.Update(string(addr), string(contract.RlpEncode()))
return contract
diff --git a/ethchain/stack.go b/ethchain/stack.go
index e3fc4b684..d475f2f8e 100644
--- a/ethchain/stack.go
+++ b/ethchain/stack.go
@@ -55,6 +55,7 @@ const (
// 0x50 range - 'storage' and execution
oPUSH = 0x50
+ oPUSH20 = 0x80
oPOP = 0x51
oDUP = 0x52
oSWAP = 0x53
@@ -250,7 +251,7 @@ func (m *Memory) Print() {
if len(m.store) > 0 {
addr := 0
for i := 0; i+32 <= len(m.store); i += 32 {
- fmt.Printf("%03d %v\n", addr, m.store[i:i+32])
+ fmt.Printf("%03d: % x\n", addr, m.store[i:i+32])
addr++
}
} else {
diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go
index 0f8ef19a7..3043c3d15 100644
--- a/ethchain/state_manager.go
+++ b/ethchain/state_manager.go
@@ -316,7 +316,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)
+ closure := NewClosure(caller, contract, contract.script, sm.procState, tx.Gas, tx.Value)
vm := NewVm(sm.procState, RuntimeVars{
origin: caller.Address(),
blockNumber: block.BlockInfo().Number,
diff --git a/ethchain/transaction.go b/ethchain/transaction.go
index 506e3c159..b359c9151 100644
--- a/ethchain/transaction.go
+++ b/ethchain/transaction.go
@@ -14,7 +14,8 @@ type Transaction struct {
Value *big.Int
Gas *big.Int
Gasprice *big.Int
- Data []string
+ Data []byte
+ Init []byte
v byte
r, s []byte
@@ -22,11 +23,11 @@ type Transaction struct {
contractCreation bool
}
-func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction {
+func NewContractCreationTx(value, gasprice *big.Int, data []byte) *Transaction {
return &Transaction{Value: value, Gasprice: gasprice, Data: data, contractCreation: true}
}
-func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction {
+func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []byte) *Transaction {
return &Transaction{Recipient: to, Value: value, Gasprice: gasprice, Gas: gas, Data: data}
}
@@ -45,19 +46,12 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
}
func (tx *Transaction) Hash() []byte {
- data := make([]interface{}, len(tx.Data))
- for i, val := range tx.Data {
- data[i] = val
+ data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice, tx.Gas, tx.Recipient, string(tx.Data)}
+ if tx.contractCreation {
+ data = append(data, string(tx.Init))
}
- preEnc := []interface{}{
- tx.Nonce,
- tx.Recipient,
- tx.Value,
- data,
- }
-
- return ethutil.Sha3Bin(ethutil.Encode(preEnc))
+ return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
}
func (tx *Transaction) IsContract() bool {
@@ -110,15 +104,17 @@ func (tx *Transaction) Sign(privk []byte) error {
return nil
}
+// [ NONCE, VALUE, GASPRICE, GAS, TO, DATA, V, R, S ]
+// [ NONCE, VALUE, GASPRICE, GAS, 0, CODE, INIT, V, R, S ]
func (tx *Transaction) RlpData() interface{} {
- data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice}
+ data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice, tx.Gas, tx.Recipient, tx.Data}
- if !tx.contractCreation {
- data = append(data, tx.Recipient, tx.Gas)
+ if tx.contractCreation {
+ data = append(data, tx.Init)
}
- d := ethutil.NewSliceValue(tx.Data).Slice()
+ //d := ethutil.NewSliceValue(tx.Data).Slice()
- return append(data, d, tx.v, tx.r, tx.s)
+ return append(data, tx.v, tx.r, tx.s)
}
func (tx *Transaction) RlpValue() *ethutil.Value {
@@ -137,31 +133,19 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.Nonce = decoder.Get(0).Uint()
tx.Value = decoder.Get(1).BigInt()
tx.Gasprice = decoder.Get(2).BigInt()
+ tx.Gas = decoder.Get(3).BigInt()
+ tx.Recipient = decoder.Get(4).Bytes()
+ tx.Data = decoder.Get(5).Bytes()
- // If the 4th item is a list(slice) this tx
- // is a contract creation tx
- if decoder.Get(3).IsList() {
- d := decoder.Get(3)
- tx.Data = make([]string, d.Len())
- for i := 0; i < d.Len(); i++ {
- tx.Data[i] = d.Get(i).Str()
- }
-
- tx.v = byte(decoder.Get(4).Uint())
- tx.r = decoder.Get(5).Bytes()
- tx.s = decoder.Get(6).Bytes()
-
+ // If the list is of length 10 it's a contract creation tx
+ if decoder.Len() == 10 {
tx.contractCreation = true
- } else {
- tx.Recipient = decoder.Get(3).Bytes()
- tx.Gas = decoder.Get(4).BigInt()
-
- d := decoder.Get(5)
- tx.Data = make([]string, d.Len())
- for i := 0; i < d.Len(); i++ {
- tx.Data[i] = d.Get(i).Str()
- }
+ tx.Init = decoder.Get(6).Bytes()
+ tx.v = byte(decoder.Get(7).Uint())
+ tx.r = decoder.Get(8).Bytes()
+ tx.s = decoder.Get(9).Bytes()
+ } else {
tx.v = byte(decoder.Get(6).Uint())
tx.r = decoder.Get(7).Bytes()
tx.s = decoder.Get(8).Bytes()
diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go
index a49768aea..3603fd8a7 100644
--- a/ethchain/transaction_test.go
+++ b/ethchain/transaction_test.go
@@ -1,54 +1 @@
package ethchain
-
-import (
- "encoding/hex"
- "math/big"
- "testing"
-)
-
-func TestAddressRetrieval(t *testing.T) {
- // TODO
- // 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
- key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
-
- tx := &Transaction{
- Nonce: 0,
- Recipient: ZeroHash160,
- Value: big.NewInt(0),
- Data: nil,
- }
- //fmt.Printf("rlp %x\n", tx.RlpEncode())
- //fmt.Printf("sha rlp %x\n", tx.Hash())
-
- tx.Sign(key)
-
- //fmt.Printf("hex tx key %x\n", tx.PublicKey())
- //fmt.Printf("seder %x\n", tx.Sender())
-}
-
-func TestAddressRetrieval2(t *testing.T) {
- // TODO
- // 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
- key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
- addr, _ := hex.DecodeString("944400f4b88ac9589a0f17ed4671da26bddb668b")
- tx := &Transaction{
- Nonce: 0,
- Recipient: addr,
- Value: big.NewInt(1000),
- Data: nil,
- }
- tx.Sign(key)
- //data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
- //tx := NewTransactionFromData(data)
- /*
- fmt.Println(tx.RlpValue())
-
- fmt.Printf("rlp %x\n", tx.RlpEncode())
- fmt.Printf("sha rlp %x\n", tx.Hash())
-
- //tx.Sign(key)
-
- fmt.Printf("hex tx key %x\n", tx.PublicKey())
- fmt.Printf("seder %x\n", tx.Sender())
- */
-}
diff --git a/ethchain/vm.go b/ethchain/vm.go
index 98aaa603a..dd99ee790 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"
_ "math"
@@ -72,7 +72,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
for {
step++
// Get the memory location of pc
- val := closure.GetMem(pc)
+ val := closure.Get(pc)
// Get the opcode (it must be an opcode!)
op := OpCode(val.Uint())
if ethutil.Config.Debug {
@@ -233,13 +233,37 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// 0x10 range
case oAND:
+ x, y := stack.Popn()
+ if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+
case oOR:
+ x, y := stack.Popn()
+ if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
case oXOR:
+ x, y := stack.Popn()
+ stack.Push(base.Xor(x, y))
case oBYTE:
+ val, th := stack.Popn()
+ if th.Cmp(big.NewInt(32)) < 0 {
+ stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64()))
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
// 0x20 range
case oSHA3:
+ size, offset := stack.Popn()
+ data := mem.Get(offset.Int64(), size.Int64())
+ stack.Push(ethutil.BigD(data))
// 0x30 range
case oADDRESS:
stack.Push(ethutil.BigD(closure.Object().Address()))
@@ -277,9 +301,23 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// 0x50 range
case oPUSH: // Push PC+1 on to the stack
pc.Add(pc, ethutil.Big1)
+ data := closure.Gets(pc, big.NewInt(32))
+ val := ethutil.BigD(data.Bytes())
+
+ // Push value to stack
+ stack.Push(val)
+
+ pc.Add(pc, big.NewInt(31))
+ case oPUSH20:
+ pc.Add(pc, ethutil.Big1)
+ data := closure.Gets(pc, big.NewInt(20))
+ val := ethutil.BigD(data.Bytes())
- val := closure.GetMem(pc).BigInt()
+ // Push value to stack
stack.Push(val)
+
+ pc.Add(pc, big.NewInt(19))
+
case oPOP:
stack.Pop()
case oDUP:
@@ -319,21 +357,20 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
stack.Push(big.NewInt(int64(mem.Len())))
// 0x60 range
case oCALL:
- // Pop return size and offset
- retSize, retOffset := stack.Popn()
+ // Closure addr
+ addr := stack.Pop()
+ // Pop gas and value of the stack.
+ gas, value := stack.Popn()
// Pop input size and offset
inSize, inOffset := stack.Popn()
- fmt.Println(inSize, inOffset)
+ // Pop return size and offset
+ retSize, retOffset := stack.Popn()
// 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
- addr := stack.Pop()
// 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)
+ closure := NewClosure(closure, contract, contract.script, vm.state, gas, value)
// Executer the closure and get the return value (if any)
ret := closure.Call(vm, args)
@@ -361,7 +398,9 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
break out
*/
default:
- ethutil.Config.Log.Debugln("Invalid opcode", op)
+ ethutil.Config.Log.Debugf("Invalid opcode %x\n", op)
+
+ return closure.Return(nil)
}
pc.Add(pc, ethutil.Big1)
diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go
index 85ec4c693..4075dfbc6 100644
--- a/ethchain/vm_test.go
+++ b/ethchain/vm_test.go
@@ -84,27 +84,41 @@ func TestRun4(t *testing.T) {
asm, err := mutan.Compile(strings.NewReader(`
int32 a = 10
- int32 b = 10
- if a == b {
- int32 c = 10
- if c == 10 {
- int32 d = 1000
- int32 e = 10
- }
+ int32 b = 20
+ if a > b {
+ int32 c = this.caller()
}
+ exit()
+ `), false)
+ script := ethutil.Assemble(asm...)
+ tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script)
+ addr := tx.Hash()[12:]
+ contract := MakeContract(tx, state)
+ state.UpdateContract(contract)
+ fmt.Printf("%x\n", addr)
- store[0] = 20
- store[a] = 20
- store[b] = this.caller()
+ asm, err = mutan.Compile(strings.NewReader(`
+ // Check if there's any cash in the initial store
+ if store[1000] == 0 {
+ store[1000] = 10^20
+ }
+
+ store[1001] = this.value() * 20
+ store[this.origin()] = store[this.origin()] + 1000
- int8[10] ret
- int8[10] arg
- call(1234, 0, 100000000, arg, ret)
+ if store[1001] > 20 {
+ store[1001] = 10^50
+ }
+
+ int8 ret = 0
+ int8 arg = 10
+ store[1002] = "a46df28529eb8aa8b8c025b0b413c5f4b688352f"
+ call(store[1002], 0, 100000000, arg, ret)
`), false)
if err != nil {
fmt.Println(err)
}
- //asm = append(asm, "LOG")
+ asm = append(asm, "LOG")
fmt.Println(asm)
callerScript := ethutil.Assemble(asm...)
@@ -112,7 +126,8 @@ func TestRun4(t *testing.T) {
// Contract addr as test address
account := NewAccount(ContractAddr, big.NewInt(10000000))
- callerClosure := NewClosure(account, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int))
+ c := MakeContract(callerTx, state)
+ callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), new(big.Int))
vm := NewVm(state, RuntimeVars{
origin: account.Address(),
diff --git a/ethutil/parsing.go b/ethutil/parsing.go
index 16ed2d06d..278414982 100644
--- a/ethutil/parsing.go
+++ b/ethutil/parsing.go
@@ -1,8 +1,8 @@
package ethutil
import (
+ _ "fmt"
"math/big"
- "strconv"
)
// Op codes
@@ -51,7 +51,10 @@ var OpCodes = map[string]byte{
"GASLIMIT": 0x45,
// 0x50 range - 'storage' and execution
- "PUSH": 0x50,
+ "PUSH": 0x50,
+
+ "PUSH20": 0x80,
+
"POP": 0x51,
"DUP": 0x52,
"SWAP": 0x53,
@@ -98,11 +101,16 @@ func CompileInstr(s interface{}) ([]byte, error) {
// Assume regular bytes during compilation
if !success {
num.SetBytes([]byte(str))
+ } else {
+ // tmp fix for 32 bytes
+ n := BigToBytes(num, 256)
+ return n, nil
}
return num.Bytes(), nil
case int:
- return big.NewInt(int64(s.(int))).Bytes(), nil
+ num := BigToBytes(big.NewInt(int64(s.(int))), 256)
+ return num, nil
case []byte:
return BigD(s.([]byte)).Bytes(), nil
}
@@ -110,34 +118,16 @@ func CompileInstr(s interface{}) ([]byte, error) {
return nil, nil
}
-func Instr(instr string) (int, []string, error) {
-
- base := new(big.Int)
- base.SetString(instr, 0)
-
- args := make([]string, 7)
- for i := 0; i < 7; i++ {
- // int(int(val) / int(math.Pow(256,float64(i)))) % 256
- exp := BigPow(256, i)
- num := new(big.Int)
- num.Div(base, exp)
-
- args[i] = num.Mod(num, big.NewInt(256)).String()
- }
- op, _ := strconv.Atoi(args[0])
-
- return op, args[1:7], nil
-}
-
// Script compilation functions
// Compiles strings to machine code
-func Assemble(instructions ...interface{}) (script []string) {
- script = make([]string, len(instructions))
+func Assemble(instructions ...interface{}) (script []byte) {
+ //script = make([]string, len(instructions))
- for i, val := range instructions {
+ for _, val := range instructions {
instr, _ := CompileInstr(val)
- script[i] = string(instr)
+ //script[i] = string(instr)
+ script = append(script, instr...)
}
return
diff --git a/ethutil/rlp.go b/ethutil/rlp.go
index e6c75696e..d95ace425 100644
--- a/ethutil/rlp.go
+++ b/ethutil/rlp.go
@@ -186,7 +186,12 @@ func Encode(object interface{}) []byte {
case byte:
buff.Write(Encode(big.NewInt(int64(t))))
case *big.Int:
- buff.Write(Encode(t.Bytes()))
+ // Not sure how this is possible while we check for
+ if t == nil {
+ buff.WriteByte(0xc0)
+ } else {
+ buff.Write(Encode(t.Bytes()))
+ }
case []byte:
if len(t) == 1 && t[0] <= 0x7f {
buff.Write(t)