Commit 97786d03 authored by Maran's avatar Maran

Merge branch 'master' into miner

parents 274d5cc9 6a86c517
...@@ -6,7 +6,7 @@ Ethereum ...@@ -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.
......
...@@ -6,23 +6,39 @@ import ( ...@@ -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 {
......
...@@ -142,7 +142,7 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { ...@@ -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)
......
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) { ...@@ -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) { ...@@ -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})
} }
*/
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
}
...@@ -10,25 +10,21 @@ type Contract struct { ...@@ -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 { ...@@ -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 {
......
...@@ -2,6 +2,7 @@ package ethchain ...@@ -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 ...@@ -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 ( ...@@ -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{ ...@@ -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 { ...@@ -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() { ...@@ -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("###########")
}
...@@ -63,8 +63,7 @@ func (s *State) GetContract(addr []byte) *Contract { ...@@ -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 { ...@@ -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) {
s.trie.Update(string(addr), string(contract.RlpEncode())) addr := contract.Address()
}
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 s.states[string(addr)] = contract.state
s.trie.Update(string(addr), string(contract.RlpEncode()))
} }
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) { ...@@ -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
}
...@@ -308,18 +308,17 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo ...@@ -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)
} }
...@@ -13,22 +13,31 @@ type Transaction struct { ...@@ -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)
......
This diff is collapsed.
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) { ...@@ -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)
}
}
...@@ -33,3 +33,9 @@ func CurrencyToString(num *big.Int) string { ...@@ -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)
)
...@@ -6,6 +6,7 @@ import ( ...@@ -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 { ...@@ -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 { ...@@ -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 (
......
...@@ -7,6 +7,7 @@ import ( ...@@ -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{ ...@@ -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 { ...@@ -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) { ...@@ -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
}
...@@ -9,6 +9,10 @@ import ( ...@@ -9,6 +9,10 @@ import (
"math/big" "math/big"
) )
type RlpEncodable interface {
RlpEncode() []byte
}
type RlpEncoder struct { type RlpEncoder struct {
rlpData []byte rlpData []byte
} }
......
package ethutil package ethutil
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
) )
......
...@@ -7,7 +7,6 @@ import ( ...@@ -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 { ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment