Merge branch 'master' into miner

This commit is contained in:
Maran 2014-03-24 10:24:39 +01:00
commit 97786d03d5
18 changed files with 796 additions and 521 deletions

View File

@ -6,7 +6,7 @@ Ethereum
Ethereum Go Development package (C) Jeffrey Wilcke Ethereum Go Development package (C) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof 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 Ethereum Go is split up in several sub packages Please refer to each
individual package for more information. individual package for more information.

View File

@ -6,23 +6,39 @@ import (
) )
type Account struct { type Account struct {
address []byte
Amount *big.Int Amount *big.Int
Nonce uint64 Nonce uint64
} }
func NewAccount(amount *big.Int) *Account { func NewAccount(address []byte, amount *big.Int) *Account {
return &Account{Amount: amount, Nonce: 0} return &Account{address, amount, 0}
} }
func NewAccountFromData(data []byte) *Account { func NewAccountFromData(address, data []byte) *Account {
address := &Account{} account := &Account{address: address}
address.RlpDecode(data) account.RlpDecode(data)
return address return account
} }
func (a *Account) AddFee(fee *big.Int) { 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) 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)
} }
func (a *Account) RlpEncode() []byte { func (a *Account) RlpEncode() []byte {

View File

@ -142,7 +142,7 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
data := block.state.trie.Get(string(block.Coinbase)) data := block.state.trie.Get(string(block.Coinbase))
// Get the ether (Coinbase) and add the fee (gief fee to miner) // 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) base = new(big.Int)
ether.Amount = base.Add(ether.Amount, fee) ether.Amount = base.Add(ether.Amount, fee)

View File

@ -1,5 +1,6 @@
package ethchain package ethchain
/*
import ( import (
_ "fmt" _ "fmt"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethdb"
@ -14,9 +15,10 @@ func TestVm(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ethutil.Config.Db = db ethutil.Config.Db = db
bm := NewBlockManager(nil) bm := NewStateManager(nil)
block := bm.bc.genesisBlock block := bm.bc.genesisBlock
bm.Prepare(block.State(), block.State())
script := Compile([]string{ script := Compile([]string{
"PUSH", "PUSH",
"1", "1",
@ -31,3 +33,4 @@ func TestVm(t *testing.T) {
tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
bm.ApplyTransactions(block, []*Transaction{tx2}) bm.ApplyTransactions(block, []*Transaction{tx2})
} }
*/

90
ethchain/closure.go Normal file
View File

@ -0,0 +1,90 @@
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)
Address() []byte
}
type ClosureBody interface {
Callee
ethutil.RlpEncodable
GetMem(*big.Int) *ethutil.Value
SetMem(*big.Int, *ethutil.Value)
}
// Basic inline closure object which implement the 'closure' interface
type Closure struct {
callee Callee
object ClosureBody
State *State
Gas *big.Int
Value *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, nil}
}
// Retuns the x element in data slice
func (c *Closure) GetMem(x *big.Int) *ethutil.Value {
m := c.object.GetMem(x)
if m == nil {
return ethutil.EmptyValue()
}
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()
}
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)
} 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) Object() ClosureBody {
return c.object
}
func (c *Closure) Callee() Callee {
return c.callee
}

View File

@ -10,25 +10,21 @@ type Contract struct {
Nonce uint64 Nonce uint64
//state *ethutil.Trie //state *ethutil.Trie
state *State state *State
address []byte
} }
func NewContract(Amount *big.Int, root []byte) *Contract { func NewContract(address []byte, Amount *big.Int, root []byte) *Contract {
contract := &Contract{Amount: Amount, Nonce: 0} contract := &Contract{address: address, Amount: Amount, Nonce: 0}
contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root)))
return contract return contract
} }
func (c *Contract) RlpEncode() []byte { func NewContractFromBytes(address, data []byte) *Contract {
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root}) contract := &Contract{address: address}
} contract.RlpDecode(data)
func (c *Contract) RlpDecode(data []byte) { return contract
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 (c *Contract) Addr(addr []byte) *ethutil.Value { func (c *Contract) Addr(addr []byte) *ethutil.Value {
@ -43,19 +39,45 @@ func (c *Contract) State() *State {
return c.state return c.state
} }
func (c *Contract) GetMem(num int) *ethutil.Value { func (c *Contract) GetMem(num *big.Int) *ethutil.Value {
nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256) nb := ethutil.BigToBytes(num, 256)
return c.Addr(nb) 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)
}
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 { func MakeContract(tx *Transaction, state *State) *Contract {
// Create contract if there's no recipient // Create contract if there's no recipient
if tx.IsContract() { if tx.IsContract() {
addr := tx.Hash()[12:] addr := tx.Hash()[12:]
value := tx.Value value := tx.Value
contract := NewContract(value, []byte("")) contract := NewContract(addr, value, []byte(""))
state.trie.Update(string(addr), string(contract.RlpEncode())) state.trie.Update(string(addr), string(contract.RlpEncode()))
for i, val := range tx.Data { for i, val := range tx.Data {
if len(val) > 0 { if len(val) > 0 {

View File

@ -2,6 +2,7 @@ package ethchain
import ( import (
"fmt" "fmt"
_ "github.com/ethereum/eth-go/ethutil"
"math/big" "math/big"
) )
@ -9,6 +10,7 @@ type OpCode int
// Op codes // Op codes
const ( const (
// 0x0 range - arithmetic ops
oSTOP = 0x00 oSTOP = 0x00
oADD = 0x01 oADD = 0x01
oMUL = 0x02 oMUL = 0x02
@ -20,50 +22,65 @@ const (
oEXP = 0x08 oEXP = 0x08
oNEG = 0x09 oNEG = 0x09
oLT = 0x0a oLT = 0x0a
oLE = 0x0b oGT = 0x0b
oGT = 0x0c oEQ = 0x0c
oGE = 0x0d oNOT = 0x0d
oEQ = 0x0e
oNOT = 0x0f // 0x10 range - bit ops
oMYADDRESS = 0x10 oAND = 0x10
oTXSENDER = 0x11 oOR = 0x11
oTXVALUE = 0x12 oXOR = 0x12
oTXDATAN = 0x13 oBYTE = 0x13
oTXDATA = 0x14
oBLK_PREVHASH = 0x15 // 0x20 range - crypto
oBLK_COINBASE = 0x16 oSHA3 = 0x20
oBLK_TIMESTAMP = 0x17
oBLK_NUMBER = 0x18 // 0x30 range - closure state
oBLK_DIFFICULTY = 0x19 oADDRESS = 0x30
oBLK_NONCE = 0x1a oBALANCE = 0x31
oBASEFEE = 0x1b oORIGIN = 0x32
oSHA256 = 0x20 oCALLER = 0x33
oRIPEMD160 = 0x21 oCALLVALUE = 0x34
oECMUL = 0x22 oCALLDATA = 0x35
oECADD = 0x23 oCALLDATASIZE = 0x36
oECSIGN = 0x24 oGASPRICE = 0x37
oECRECOVER = 0x25
oECVALID = 0x26 // 0x40 range - block operations
oSHA3 = 0x27 oPREVHASH = 0x40
oPUSH = 0x30 oCOINBASE = 0x41
oPOP = 0x31 oTIMESTAMP = 0x42
oDUP = 0x32 oNUMBER = 0x43
oSWAP = 0x33 oDIFFICULTY = 0x44
oMLOAD = 0x34 oGASLIMIT = 0x45
oMSTORE = 0x35
oSLOAD = 0x36 // 0x50 range - 'storage' and execution
oSSTORE = 0x37 oPUSH = 0x50
oJMP = 0x38 oPOP = 0x51
oJMPI = 0x39 oDUP = 0x52
oIND = 0x3a oSWAP = 0x53
oEXTRO = 0x3b oMLOAD = 0x54
oBALANCE = 0x3c oMSTORE = 0x55
oMKTX = 0x3d oMSTORE8 = 0x56
oSUICIDE = 0x3f oSLOAD = 0x57
oSSTORE = 0x58
oJUMP = 0x59
oJUMPI = 0x5a
oPC = 0x5b
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 // Since the opcodes aren't all in order we can't use a regular slice
var opCodeToString = map[OpCode]string{ var opCodeToString = map[OpCode]string{
// 0x0 range - arithmetic ops
oSTOP: "STOP", oSTOP: "STOP",
oADD: "ADD", oADD: "ADD",
oMUL: "MUL", oMUL: "MUL",
@ -75,44 +92,59 @@ var opCodeToString = map[OpCode]string{
oEXP: "EXP", oEXP: "EXP",
oNEG: "NEG", oNEG: "NEG",
oLT: "LT", oLT: "LT",
oLE: "LE",
oGT: "GT", oGT: "GT",
oGE: "GE",
oEQ: "EQ", oEQ: "EQ",
oNOT: "NOT", oNOT: "NOT",
oMYADDRESS: "MYADDRESS",
oTXSENDER: "TXSENDER", // 0x10 range - bit ops
oTXVALUE: "TXVALUE", oAND: "AND",
oTXDATAN: "TXDATAN", oOR: "OR",
oTXDATA: "TXDATA", oXOR: "XOR",
oBLK_PREVHASH: "BLK_PREVHASH", oBYTE: "BYTE",
oBLK_COINBASE: "BLK_COINBASE",
oBLK_TIMESTAMP: "BLK_TIMESTAMP", // 0x20 range - crypto
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", oSHA3: "SHA3",
// 0x30 range - closure state
oADDRESS: "ADDRESS",
oBALANCE: "BALANCE",
oORIGIN: "ORIGIN",
oCALLER: "CALLER",
oCALLVALUE: "CALLVALUE",
oCALLDATA: "CALLDATA",
oCALLDATASIZE: "CALLDATASIZE",
oGASPRICE: "TXGASPRICE",
// 0x40 range - block operations
oPREVHASH: "PREVHASH",
oCOINBASE: "COINBASE",
oTIMESTAMP: "TIMESTAMP",
oNUMBER: "NUMBER",
oDIFFICULTY: "DIFFICULTY",
oGASLIMIT: "GASLIMIT",
// 0x50 range - 'storage' and execution
oPUSH: "PUSH", oPUSH: "PUSH",
oPOP: "POP", oPOP: "POP",
oDUP: "DUP", oDUP: "DUP",
oSWAP: "SWAP", oSWAP: "SWAP",
oMLOAD: "MLOAD", oMLOAD: "MLOAD",
oMSTORE: "MSTORE", oMSTORE: "MSTORE",
oMSTORE8: "MSTORE8",
oSLOAD: "SLOAD", oSLOAD: "SLOAD",
oSSTORE: "SSTORE", oSSTORE: "SSTORE",
oJMP: "JMP", oJUMP: "JUMP",
oJMPI: "JMPI", oJUMPI: "JUMPI",
oIND: "IND", oPC: "PC",
oEXTRO: "EXTRO", oMSIZE: "MSIZE",
oBALANCE: "BALANCE",
oMKTX: "MKTX", // 0x60 range - closures
oCREATE: "CREATE",
oCALL: "CALL",
oRETURN: "RETURN",
// 0x70 range - other
oLOG: "LOG",
oSUICIDE: "SUICIDE", oSUICIDE: "SUICIDE",
} }
@ -141,35 +173,27 @@ func NewStack() *Stack {
} }
func (st *Stack) Pop() *big.Int { func (st *Stack) Pop() *big.Int {
s := len(st.data) str := st.data[0]
st.data = st.data[1:]
str := st.data[s-1]
st.data = st.data[:s-1]
return str return str
} }
func (st *Stack) Popn() (*big.Int, *big.Int) { func (st *Stack) Popn() (*big.Int, *big.Int) {
s := len(st.data) ints := st.data[:2]
st.data = st.data[2:]
ints := st.data[s-2:]
st.data = st.data[:s-2]
return ints[0], ints[1] return ints[0], ints[1]
} }
func (st *Stack) Peek() *big.Int { func (st *Stack) Peek() *big.Int {
s := len(st.data) str := st.data[0]
str := st.data[s-1]
return str return str
} }
func (st *Stack) Peekn() (*big.Int, *big.Int) { func (st *Stack) Peekn() (*big.Int, *big.Int) {
s := len(st.data) ints := st.data[:2]
ints := st.data[s-2:]
return ints[0], ints[1] return ints[0], ints[1]
} }
@ -188,3 +212,45 @@ func (st *Stack) Print() {
} }
fmt.Println("#############") fmt.Println("#############")
} }
type Memory struct {
store []byte
}
func (m *Memory) Set(offset, size int64, value []byte) {
totSize := offset + size
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)
// Resize slice
m.store = append(m.store, newSlice...)
}
}
copy(m.store[offset:offset+size], value)
}
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 {
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 --")
}
fmt.Println("###########")
}

View File

@ -63,8 +63,7 @@ func (s *State) GetContract(addr []byte) *Contract {
} }
// build contract // build contract
contract := &Contract{} contract := NewContractFromBytes(addr, []byte(data))
contract.RlpDecode([]byte(data))
// Check if there's a cached state for this contract // Check if there's a cached state for this contract
cachedState := s.states[string(addr)] cachedState := s.states[string(addr)]
@ -78,27 +77,19 @@ func (s *State) GetContract(addr []byte) *Contract {
return 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())) 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) { func (s *State) GetAccount(addr []byte) (account *Account) {
data := s.trie.Get(string(addr)) data := s.trie.Get(string(addr))
if data == "" { if data == "" {
account = NewAccount(big.NewInt(0)) account = NewAccount(addr, big.NewInt(0))
} else { } else {
account = NewAccountFromData([]byte(data)) account = NewAccountFromData(addr, []byte(data))
} }
return return
@ -153,3 +144,35 @@ func (s *State) Get(key []byte) (*ethutil.Value, ObjType) {
return val, typ return val, typ
} }
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) {
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
}

View File

@ -308,18 +308,17 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo
} }
}() }()
*/ */
caller := sm.procState.GetAccount(tx.Sender())
vm := &Vm{} closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value)
//vm.Process(contract, block.state, RuntimeVars{ vm := NewVm(sm.procState, RuntimeVars{
vm.Process(contract, sm.procState, RuntimeVars{ origin: caller.Address(),
address: tx.Hash()[12:],
blockNumber: block.BlockInfo().Number, blockNumber: block.BlockInfo().Number,
sender: tx.Sender(),
prevHash: block.PrevHash, prevHash: block.PrevHash,
coinbase: block.Coinbase, coinbase: block.Coinbase,
time: block.Time, time: block.Time,
diff: block.Difficulty, diff: block.Difficulty,
txValue: tx.Value, // XXX Tx data? Could be just an argument to the closure instead
txData: tx.Data, txData: nil,
}) })
closure.Call(vm, nil)
} }

View File

@ -13,22 +13,31 @@ type Transaction struct {
Nonce uint64 Nonce uint64
Recipient []byte Recipient []byte
Value *big.Int Value *big.Int
Gas *big.Int
Gasprice *big.Int
Data []string Data []string
Memory []int
v byte v byte
r, s []byte r, s []byte
} }
func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
tx := Transaction{Recipient: to, Value: value} tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data}
tx.Nonce = 0
// Serialize the data
tx.Data = data
return &tx 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 // XXX Deprecated
func NewTransactionFromData(data []byte) *Transaction { func NewTransactionFromData(data []byte) *Transaction {
return NewTransactionFromBytes(data) return NewTransactionFromBytes(data)

View File

@ -1,12 +1,12 @@
package ethchain package ethchain
import ( import (
"bytes" _ "bytes"
"fmt" _ "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go" _ "github.com/obscuren/secp256k1-go"
"log" "log"
"math" _ "math"
"math/big" "math/big"
) )
@ -18,122 +18,102 @@ type Vm struct {
mem map[string]*big.Int mem map[string]*big.Int
vars RuntimeVars vars RuntimeVars
state *State
} }
type RuntimeVars struct { type RuntimeVars struct {
address []byte origin []byte
blockNumber uint64 blockNumber uint64
sender []byte
prevHash []byte prevHash []byte
coinbase []byte coinbase []byte
time int64 time int64
diff *big.Int diff *big.Int
txValue *big.Int
txData []string txData []string
} }
func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) { func NewVm(state *State, vars RuntimeVars) *Vm {
vm.mem = make(map[string]*big.Int) return &Vm{vars: vars, state: state}
vm.stack = NewStack() }
addr := vars.address // tx.Hash()[12:] var Pow256 = ethutil.BigPow(2, 256)
// Instruction pointer
pc := 0
if contract == nil { func (vm *Vm) RunClosure(closure *Closure) []byte {
fmt.Println("Contract not found") // If the amount of gas supplied is less equal to 0
return if closure.Gas.Cmp(big.NewInt(0)) <= 0 {
// TODO Do something
} }
Pow256 := ethutil.BigPow(2, 256) // Memory for the current closure
mem := &Memory{}
// New stack (should this be shared?)
stack := NewStack()
// Instruction pointer
pc := big.NewInt(0)
// Current step count
step := 0
// The base for all big integer arithmetic
base := new(big.Int)
if ethutil.Config.Debug { if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("# op\n") ethutil.Config.Log.Debugf("# op\n")
} }
stepcount := 0
totalFee := new(big.Int)
out:
for { for {
stepcount++ step++
// The base big int for all calculations. Use this for any results. // Get the memory location of pc
base := new(big.Int) val := closure.GetMem(pc)
val := contract.GetMem(pc) // Get the opcode (it must be an opcode!)
//fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb)
op := OpCode(val.Uint()) 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 { if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) 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.Gas.Cmp(fee) < 0 {
return closure.Return(nil)
}
switch op { switch op {
case oSTOP: case oLOG:
fmt.Println("") stack.Print()
break out mem.Print()
case oSTOP: // Stop the closure
return closure.Return(nil)
// 0x20 range
case oADD: case oADD:
x, y := vm.stack.Popn() x, y := stack.Popn()
// (x + y) % 2 ** 256 // (x + y) % 2 ** 256
base.Add(x, y) base.Add(x, y)
base.Mod(base, Pow256) base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base) stack.Push(base)
case oSUB: case oSUB:
x, y := vm.stack.Popn() x, y := stack.Popn()
// (x - y) % 2 ** 256 // (x - y) % 2 ** 256
base.Sub(x, y) base.Sub(x, y)
base.Mod(base, Pow256) base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base) stack.Push(base)
case oMUL: case oMUL:
x, y := vm.stack.Popn() x, y := stack.Popn()
// (x * y) % 2 ** 256 // (x * y) % 2 ** 256
base.Mul(x, y) base.Mul(x, y)
base.Mod(base, Pow256) base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base) stack.Push(base)
case oDIV: case oDIV:
x, y := vm.stack.Popn() x, y := stack.Popn()
// floor(x / y) // floor(x / y)
base.Div(x, y) base.Div(x, y)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base) stack.Push(base)
case oSDIV: case oSDIV:
x, y := vm.stack.Popn() x, y := stack.Popn()
// n > 2**255 // n > 2**255
if x.Cmp(Pow256) > 0 { if x.Cmp(Pow256) > 0 {
x.Sub(Pow256, x) x.Sub(Pow256, x)
@ -147,13 +127,13 @@ out:
z.Sub(Pow256, z) z.Sub(Pow256, z)
} }
// Push result on to the stack // Push result on to the stack
vm.stack.Push(z) stack.Push(z)
case oMOD: case oMOD:
x, y := vm.stack.Popn() x, y := stack.Popn()
base.Mod(x, y) base.Mod(x, y)
vm.stack.Push(base) stack.Push(base)
case oSMOD: case oSMOD:
x, y := vm.stack.Popn() x, y := stack.Popn()
// n > 2**255 // n > 2**255
if x.Cmp(Pow256) > 0 { if x.Cmp(Pow256) > 0 {
x.Sub(Pow256, x) x.Sub(Pow256, x)
@ -167,213 +147,154 @@ out:
z.Sub(Pow256, z) z.Sub(Pow256, z)
} }
// Push result on to the stack // Push result on to the stack
vm.stack.Push(z) stack.Push(z)
case oEXP: case oEXP:
x, y := vm.stack.Popn() x, y := stack.Popn()
base.Exp(x, y, Pow256) base.Exp(x, y, Pow256)
vm.stack.Push(base) stack.Push(base)
case oNEG: case oNEG:
base.Sub(Pow256, vm.stack.Pop()) base.Sub(Pow256, stack.Pop())
vm.stack.Push(base) stack.Push(base)
case oLT: case oLT:
x, y := vm.stack.Popn() x, y := stack.Popn()
// x < y // x < y
if x.Cmp(y) < 0 { if x.Cmp(y) < 0 {
vm.stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
} else { } else {
vm.stack.Push(ethutil.BigFalse) 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: case oGT:
x, y := vm.stack.Popn() x, y := stack.Popn()
// x > y // x > y
if x.Cmp(y) > 0 { if x.Cmp(y) > 0 {
vm.stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
} else { } else {
vm.stack.Push(ethutil.BigFalse) 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: case oNOT:
x, y := vm.stack.Popn() x, y := stack.Popn()
// x != y // x != y
if x.Cmp(y) != 0 { if x.Cmp(y) != 0 {
vm.stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
} else { } else {
vm.stack.Push(ethutil.BigFalse) 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 { // 0x10 range
vm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes()))) case oAND:
} else if op == oSHA3 { case oOR:
vm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes()))) case oXOR:
} else { case oBYTE:
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)) { // 0x20 range
data := new(bytes.Buffer) case oSHA3:
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: // 0x30 range
case oECSIGN: case oADDRESS:
case oECRECOVER: stack.Push(ethutil.BigD(closure.Object().Address()))
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 := int(vm.stack.Pop().Uint64())
// 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 = int(x.Uint64())
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: case oBALANCE:
// Pushes the balance of the popped value on to the stack stack.Push(closure.Value)
account := state.GetAccount(vm.stack.Pop().Bytes()) case oORIGIN:
vm.stack.Push(account.Amount) stack.Push(ethutil.BigD(vm.vars.origin))
case oMKTX: case oCALLER:
addr, value := vm.stack.Popn() stack.Push(ethutil.BigD(closure.Callee().Address()))
from, length := vm.stack.Popn() 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:
stack.Push(big.NewInt(int64(len(closure.Args))))
case oGASPRICE:
// TODO
makeInlineTx(addr.Bytes(), value, from, length, contract, state) // 0x40 range
case oPREVHASH:
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
pc.Add(pc, ethutil.Big1)
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)))
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:
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)
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
retSize, retOffset := stack.Popn()
// Pop input size and offset
inSize, inOffset := 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)
// Executer the closure and get the return value (if any)
ret := closure.Call(vm, args)
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
case oRETURN:
size, offset := stack.Popn()
ret := mem.Get(offset.Int64(), size.Int64())
return closure.Return(ret)
case oSUICIDE: case oSUICIDE:
recAddr := vm.stack.Pop().Bytes() /*
recAddr := stack.Pop().Bytes()
// Purge all memory // Purge all memory
deletedMemory := contract.state.Purge() deletedMemory := contract.state.Purge()
// Add refunds to the pop'ed address // Add refunds to the pop'ed address
@ -387,30 +308,28 @@ out:
ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr) ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr)
break out break out
*/
default: default:
fmt.Printf("Invalid OPCODE: %x\n", op) ethutil.Config.Log.Debugln("Invalid opcode", op)
}
ethutil.Config.Log.Debugln("")
//vm.stack.Print()
pc++
} }
state.UpdateContract(addr, contract) pc.Add(pc, ethutil.Big1)
}
} }
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) { 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) 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())) 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() dataItems[j] = contract.GetMem(big.NewInt(j)).Str()
j++ j++
} }
tx := NewTransaction(addr, value, dataItems) tx := NewTransaction(addr, value, dataItems)
if tx.IsContract() { if tx.IsContract() {
contract := MakeContract(tx, state) contract := MakeContract(tx, state)
state.UpdateContract(tx.Hash()[12:], contract) state.UpdateContract(contract)
} else { } else {
account := state.GetAccount(tx.Recipient) account := state.GetAccount(tx.Recipient)
account.Amount.Add(account.Amount, tx.Value) account.Amount.Add(account.Amount, tx.Value)

View File

@ -1,13 +1,15 @@
package ethchain package ethchain
import ( import (
"fmt" "bytes"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big" "math/big"
"testing" "testing"
) )
/*
func TestRun(t *testing.T) { func TestRun(t *testing.T) {
InitFees() InitFees()
@ -104,3 +106,69 @@ func TestRun2(t *testing.T) {
txData: tx.Data, 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",
"PUSH", "0",
"MSTORE",
"PUSH", "32",
"CALLDATA",
"PUSH", "64",
"PUSH", "0",
"RETURN",
})
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
addr := tx.Hash()[12:]
contract := MakeContract(tx, state)
state.UpdateContract(contract)
callerScript := ethutil.Compile(
"PUSH", 1337, // Argument
"PUSH", 65, // argument mem offset
"MSTORE",
"PUSH", 64, // ret size
"PUSH", 0, // ret offset
"PUSH", 32, // arg size
"PUSH", 65, // arg offset
"PUSH", 1000, /// Gas
"PUSH", 0, /// value
"PUSH", addr, // Sender
"CALL",
"PUSH", 64,
"PUSH", 0,
"RETURN",
)
callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript)
// 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{
origin: account.Address(),
blockNumber: 1,
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
time: 1,
diff: big.NewInt(256),
// XXX Tx data? Could be just an argument to the closure instead
txData: 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)
}
}

View File

@ -33,3 +33,9 @@ func CurrencyToString(num *big.Int) string {
return fmt.Sprintf("%v Wei", num) return fmt.Sprintf("%v Wei", num)
} }
var (
Big1 = big.NewInt(1)
Big0 = big.NewInt(0)
Big256 = big.NewInt(0xff)
)

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"os/user" "os/user"
"path" "path"
"runtime"
) )
type LogType byte type LogType byte
@ -23,6 +24,7 @@ type config struct {
ExecPath string ExecPath string
Debug bool Debug bool
Ver string Ver string
ClientString string
Pubkey []byte Pubkey []byte
Seed bool Seed bool
} }
@ -48,11 +50,16 @@ func ReadConfig(base string) *config {
Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"} Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"}
Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
Config.SetClientString("/Ethereum(G)")
} }
return Config return Config
} }
func (c *config) SetClientString(str string) {
Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS)
}
type LoggerType byte type LoggerType byte
const ( const (

View File

@ -7,6 +7,7 @@ import (
// Op codes // Op codes
var OpCodes = map[string]byte{ var OpCodes = map[string]byte{
// 0x0 range - arithmetic ops
"STOP": 0x00, "STOP": 0x00,
"ADD": 0x01, "ADD": 0x01,
"MUL": 0x02, "MUL": 0x02,
@ -18,46 +19,60 @@ var OpCodes = map[string]byte{
"EXP": 0x08, "EXP": 0x08,
"NEG": 0x09, "NEG": 0x09,
"LT": 0x0a, "LT": 0x0a,
"LE": 0x0b, "GT": 0x0b,
"GT": 0x0c, "EQ": 0x0c,
"GE": 0x0d, "NOT": 0x0d,
"EQ": 0x0e,
"NOT": 0x0f, // 0x10 range - bit ops
"MYADDRESS": 0x10, "AND": 0x10,
"TXSENDER": 0x11, "OR": 0x11,
"TXVALUE": 0x12, "XOR": 0x12,
"TXDATAN": 0x13, "BYTE": 0x13,
"TXDATA": 0x14,
"BLK_PREVHASH": 0x15, // 0x20 range - crypto
"BLK_COINBASE": 0x16, "SHA3": 0x20,
"BLK_TIMESTAMP": 0x17,
"BLK_NUMBER": 0x18, // 0x30 range - closure state
"BLK_DIFFICULTY": 0x19, "ADDRESS": 0x30,
"BLK_NONCE": 0x1a, "BALANCE": 0x31,
"BASEFEE": 0x1b, "ORIGIN": 0x32,
"SHA256": 0x20, "CALLER": 0x33,
"RIPEMD160": 0x21, "CALLVALUE": 0x34,
"ECMUL": 0x22, "CALLDATA": 0x35,
"ECADD": 0x23, "CALLDATASIZE": 0x36,
"ECSIGN": 0x24, "GASPRICE": 0x38,
"ECRECOVER": 0x25,
"ECVALID": 0x26, // 0x40 range - block operations
"SHA3": 0x27, "PREVHASH": 0x40,
"PUSH": 0x30, "COINBASE": 0x41,
"POP": 0x31, "TIMESTAMP": 0x42,
"DUP": 0x32, "NUMBER": 0x43,
"SWAP": 0x33, "DIFFICULTY": 0x44,
"MLOAD": 0x34, "GASLIMIT": 0x45,
"MSTORE": 0x35,
"SLOAD": 0x36, // 0x50 range - 'storage' and execution
"SSTORE": 0x37, "PUSH": 0x50,
"JMP": 0x38, "POP": 0x51,
"JMPI": 0x39, "DUP": 0x52,
"IND": 0x3a, "SWAP": 0x53,
"EXTRO": 0x3b, "MLOAD": 0x54,
"BALANCE": 0x3c, "MSTORE": 0x55,
"MKTX": 0x3d, "MSTORE8": 0x56,
"SUICIDE": 0x3f, "SLOAD": 0x57,
"SSTORE": 0x58,
"JUMP": 0x59,
"JUMPI": 0x5a,
"PC": 0x5b,
"MSIZE": 0x5c,
// 0x60 range - closures
"CREATE": 0x60,
"CALL": 0x61,
"RETURN": 0x62,
// 0x70 range - other
"LOG": 0x70,
"SUICIDE": 0x7f,
} }
func IsOpCode(s string) bool { func IsOpCode(s string) bool {
@ -69,16 +84,30 @@ func IsOpCode(s string) bool {
return false return false
} }
func CompileInstr(s string) ([]byte, error) { func CompileInstr(s interface{}) ([]byte, error) {
isOp := IsOpCode(s) switch s.(type) {
case string:
str := s.(string)
isOp := IsOpCode(str)
if isOp { if isOp {
return []byte{OpCodes[s]}, nil return []byte{OpCodes[str]}, nil
} }
num := new(big.Int) num := new(big.Int)
num.SetString(s, 0) _, success := num.SetString(str, 0)
// Assume regular bytes during compilation
if !success {
num.SetBytes([]byte(str))
}
return num.Bytes(), nil return num.Bytes(), nil
case int:
return big.NewInt(int64(s.(int))).Bytes(), nil
case []byte:
return BigD(s.([]byte)).Bytes(), nil
}
return nil, nil
} }
func Instr(instr string) (int, []string, error) { func Instr(instr string) (int, []string, error) {
@ -99,3 +128,17 @@ func Instr(instr string) (int, []string, error) {
return op, args[1:7], nil 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
}

View File

@ -9,6 +9,10 @@ import (
"math/big" "math/big"
) )
type RlpEncodable interface {
RlpEncode() []byte
}
type RlpEncoder struct { type RlpEncoder struct {
rlpData []byte rlpData []byte
} }

View File

@ -1,6 +1,7 @@
package ethutil package ethutil
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
) )

View File

@ -7,7 +7,6 @@ import (
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"net" "net"
"runtime"
"strconv" "strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
@ -160,7 +159,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
connected: 0, connected: 0,
disconnect: 0, disconnect: 0,
caps: caps, 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 // Set up the connection in another goroutine so we don't block the main thread