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 {
Amount *big.Int address []byte
Nonce uint64 Amount *big.Int
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
}
...@@ -9,26 +9,22 @@ type Contract struct { ...@@ -9,26 +9,22 @@ type Contract struct {
Amount *big.Int Amount *big.Int
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,111 +10,142 @@ type OpCode int ...@@ -9,111 +10,142 @@ type OpCode int
// Op codes // Op codes
const ( const (
oSTOP = 0x00 // 0x0 range - arithmetic ops
oADD = 0x01 oSTOP = 0x00
oMUL = 0x02 oADD = 0x01
oSUB = 0x03 oMUL = 0x02
oDIV = 0x04 oSUB = 0x03
oSDIV = 0x05 oDIV = 0x04
oMOD = 0x06 oSDIV = 0x05
oSMOD = 0x07 oMOD = 0x06
oEXP = 0x08 oSMOD = 0x07
oNEG = 0x09 oEXP = 0x08
oLT = 0x0a oNEG = 0x09
oLE = 0x0b oLT = 0x0a
oGT = 0x0c oGT = 0x0b
oGE = 0x0d oEQ = 0x0c
oEQ = 0x0e oNOT = 0x0d
oNOT = 0x0f
oMYADDRESS = 0x10 // 0x10 range - bit ops
oTXSENDER = 0x11 oAND = 0x10
oTXVALUE = 0x12 oOR = 0x11
oTXDATAN = 0x13 oXOR = 0x12
oTXDATA = 0x14 oBYTE = 0x13
oBLK_PREVHASH = 0x15
oBLK_COINBASE = 0x16 // 0x20 range - crypto
oBLK_TIMESTAMP = 0x17 oSHA3 = 0x20
oBLK_NUMBER = 0x18
oBLK_DIFFICULTY = 0x19 // 0x30 range - closure state
oBLK_NONCE = 0x1a oADDRESS = 0x30
oBASEFEE = 0x1b oBALANCE = 0x31
oSHA256 = 0x20 oORIGIN = 0x32
oRIPEMD160 = 0x21 oCALLER = 0x33
oECMUL = 0x22 oCALLVALUE = 0x34
oECADD = 0x23 oCALLDATA = 0x35
oECSIGN = 0x24 oCALLDATASIZE = 0x36
oECRECOVER = 0x25 oGASPRICE = 0x37
oECVALID = 0x26
oSHA3 = 0x27 // 0x40 range - block operations
oPUSH = 0x30 oPREVHASH = 0x40
oPOP = 0x31 oCOINBASE = 0x41
oDUP = 0x32 oTIMESTAMP = 0x42
oSWAP = 0x33 oNUMBER = 0x43
oMLOAD = 0x34 oDIFFICULTY = 0x44
oMSTORE = 0x35 oGASLIMIT = 0x45
oSLOAD = 0x36
oSSTORE = 0x37 // 0x50 range - 'storage' and execution
oJMP = 0x38 oPUSH = 0x50
oJMPI = 0x39 oPOP = 0x51
oIND = 0x3a oDUP = 0x52
oEXTRO = 0x3b oSWAP = 0x53
oBALANCE = 0x3c oMLOAD = 0x54
oMKTX = 0x3d oMSTORE = 0x55
oSUICIDE = 0x3f oMSTORE8 = 0x56
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{
oSTOP: "STOP", // 0x0 range - arithmetic ops
oADD: "ADD", oSTOP: "STOP",
oMUL: "MUL", oADD: "ADD",
oSUB: "SUB", oMUL: "MUL",
oDIV: "DIV", oSUB: "SUB",
oSDIV: "SDIV", oDIV: "DIV",
oMOD: "MOD", oSDIV: "SDIV",
oSMOD: "SMOD", oMOD: "MOD",
oEXP: "EXP", oSMOD: "SMOD",
oNEG: "NEG", oEXP: "EXP",
oLT: "LT", oNEG: "NEG",
oLE: "LE", oLT: "LT",
oGT: "GT", oGT: "GT",
oGE: "GE", oEQ: "EQ",
oEQ: "EQ", oNOT: "NOT",
oNOT: "NOT",
oMYADDRESS: "MYADDRESS", // 0x10 range - bit ops
oTXSENDER: "TXSENDER", oAND: "AND",
oTXVALUE: "TXVALUE", oOR: "OR",
oTXDATAN: "TXDATAN", oXOR: "XOR",
oTXDATA: "TXDATA", oBYTE: "BYTE",
oBLK_PREVHASH: "BLK_PREVHASH",
oBLK_COINBASE: "BLK_COINBASE", // 0x20 range - crypto
oBLK_TIMESTAMP: "BLK_TIMESTAMP", oSHA3: "SHA3",
oBLK_NUMBER: "BLK_NUMBER",
oBLK_DIFFICULTY: "BLK_DIFFICULTY", // 0x30 range - closure state
oBASEFEE: "BASEFEE", oADDRESS: "ADDRESS",
oSHA256: "SHA256", oBALANCE: "BALANCE",
oRIPEMD160: "RIPEMD160", oORIGIN: "ORIGIN",
oECMUL: "ECMUL", oCALLER: "CALLER",
oECADD: "ECADD", oCALLVALUE: "CALLVALUE",
oECSIGN: "ECSIGN", oCALLDATA: "CALLDATA",
oECRECOVER: "ECRECOVER", oCALLDATASIZE: "CALLDATASIZE",
oECVALID: "ECVALID", oGASPRICE: "TXGASPRICE",
oSHA3: "SHA3",
oPUSH: "PUSH", // 0x40 range - block operations
oPOP: "POP", oPREVHASH: "PREVHASH",
oDUP: "DUP", oCOINBASE: "COINBASE",
oSWAP: "SWAP", oTIMESTAMP: "TIMESTAMP",
oMLOAD: "MLOAD", oNUMBER: "NUMBER",
oMSTORE: "MSTORE", oDIFFICULTY: "DIFFICULTY",
oSLOAD: "SLOAD", oGASLIMIT: "GASLIMIT",
oSSTORE: "SSTORE",
oJMP: "JMP", // 0x50 range - 'storage' and execution
oJMPI: "JMPI", oPUSH: "PUSH",
oIND: "IND", oPOP: "POP",
oEXTRO: "EXTRO", oDUP: "DUP",
oBALANCE: "BALANCE", oSWAP: "SWAP",
oMKTX: "MKTX", oMLOAD: "MLOAD",
oSUICIDE: "SUICIDE", oMSTORE: "MSTORE",
oMSTORE8: "MSTORE8",
oSLOAD: "SLOAD",
oSSTORE: "SSTORE",
oJUMP: "JUMP",
oJUMPI: "JUMPI",
oPC: "PC",
oMSIZE: "MSIZE",
// 0x60 range - closures
oCREATE: "CREATE",
oCALL: "CALL",
oRETURN: "RETURN",
// 0x70 range - other
oLOG: "LOG",
oSUICIDE: "SUICIDE",
} }
func (o OpCode) String() string { func (o OpCode) String() string {
...@@ -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
...@@ -19,12 +20,13 @@ const ( ...@@ -19,12 +20,13 @@ const (
type config struct { type config struct {
Db Database Db Database
Log *Logger Log *Logger
ExecPath string ExecPath string
Debug bool Debug bool
Ver string Ver string
Pubkey []byte ClientString string
Seed bool Pubkey []byte
Seed bool
} }
var Config *config var Config *config
...@@ -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,57 +7,72 @@ import ( ...@@ -7,57 +7,72 @@ import (
// Op codes // Op codes
var OpCodes = map[string]byte{ var OpCodes = map[string]byte{
"STOP": 0x00, // 0x0 range - arithmetic ops
"ADD": 0x01, "STOP": 0x00,
"MUL": 0x02, "ADD": 0x01,
"SUB": 0x03, "MUL": 0x02,
"DIV": 0x04, "SUB": 0x03,
"SDIV": 0x05, "DIV": 0x04,
"MOD": 0x06, "SDIV": 0x05,
"SMOD": 0x07, "MOD": 0x06,
"EXP": 0x08, "SMOD": 0x07,
"NEG": 0x09, "EXP": 0x08,
"LT": 0x0a, "NEG": 0x09,
"LE": 0x0b, "LT": 0x0a,
"GT": 0x0c, "GT": 0x0b,
"GE": 0x0d, "EQ": 0x0c,
"EQ": 0x0e, "NOT": 0x0d,
"NOT": 0x0f,
"MYADDRESS": 0x10, // 0x10 range - bit ops
"TXSENDER": 0x11, "AND": 0x10,
"TXVALUE": 0x12, "OR": 0x11,
"TXDATAN": 0x13, "XOR": 0x12,
"TXDATA": 0x14, "BYTE": 0x13,
"BLK_PREVHASH": 0x15,
"BLK_COINBASE": 0x16, // 0x20 range - crypto
"BLK_TIMESTAMP": 0x17, "SHA3": 0x20,
"BLK_NUMBER": 0x18,
"BLK_DIFFICULTY": 0x19, // 0x30 range - closure state
"BLK_NONCE": 0x1a, "ADDRESS": 0x30,
"BASEFEE": 0x1b, "BALANCE": 0x31,
"SHA256": 0x20, "ORIGIN": 0x32,
"RIPEMD160": 0x21, "CALLER": 0x33,
"ECMUL": 0x22, "CALLVALUE": 0x34,
"ECADD": 0x23, "CALLDATA": 0x35,
"ECSIGN": 0x24, "CALLDATASIZE": 0x36,
"ECRECOVER": 0x25, "GASPRICE": 0x38,
"ECVALID": 0x26,
"SHA3": 0x27, // 0x40 range - block operations
"PUSH": 0x30, "PREVHASH": 0x40,
"POP": 0x31, "COINBASE": 0x41,
"DUP": 0x32, "TIMESTAMP": 0x42,
"SWAP": 0x33, "NUMBER": 0x43,
"MLOAD": 0x34, "DIFFICULTY": 0x44,
"MSTORE": 0x35, "GASLIMIT": 0x45,
"SLOAD": 0x36,
"SSTORE": 0x37, // 0x50 range - 'storage' and execution
"JMP": 0x38, "PUSH": 0x50,
"JMPI": 0x39, "POP": 0x51,
"IND": 0x3a, "DUP": 0x52,
"EXTRO": 0x3b, "SWAP": 0x53,
"BALANCE": 0x3c, "MLOAD": 0x54,
"MKTX": 0x3d, "MSTORE": 0x55,
"SUICIDE": 0x3f, "MSTORE8": 0x56,
"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) {
if isOp { case string:
return []byte{OpCodes[s]}, nil str := s.(string)
} isOp := IsOpCode(str)
if isOp {
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
case int:
return big.NewInt(int64(s.(int))).Bytes(), nil
case []byte:
return BigD(s.([]byte)).Bytes(), nil
}
return num.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