Merge branch 'master' into miner
This commit is contained in:
commit
97786d03d5
@ -6,7 +6,7 @@ Ethereum
|
||||
Ethereum Go Development package (C) Jeffrey Wilcke
|
||||
|
||||
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
|
||||
individual package for more information.
|
||||
|
@ -6,23 +6,39 @@ import (
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
address []byte
|
||||
Amount *big.Int
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
func NewAccount(amount *big.Int) *Account {
|
||||
return &Account{Amount: amount, Nonce: 0}
|
||||
func NewAccount(address []byte, amount *big.Int) *Account {
|
||||
return &Account{address, amount, 0}
|
||||
}
|
||||
|
||||
func NewAccountFromData(data []byte) *Account {
|
||||
address := &Account{}
|
||||
address.RlpDecode(data)
|
||||
func NewAccountFromData(address, data []byte) *Account {
|
||||
account := &Account{address: address}
|
||||
account.RlpDecode(data)
|
||||
|
||||
return address
|
||||
return account
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -142,7 +142,7 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
|
||||
data := block.state.trie.Get(string(block.Coinbase))
|
||||
|
||||
// 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)
|
||||
ether.Amount = base.Add(ether.Amount, fee)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ethchain
|
||||
|
||||
/*
|
||||
import (
|
||||
_ "fmt"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
@ -14,9 +15,10 @@ func TestVm(t *testing.T) {
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
ethutil.Config.Db = db
|
||||
bm := NewBlockManager(nil)
|
||||
bm := NewStateManager(nil)
|
||||
|
||||
block := bm.bc.genesisBlock
|
||||
bm.Prepare(block.State(), block.State())
|
||||
script := Compile([]string{
|
||||
"PUSH",
|
||||
"1",
|
||||
@ -31,3 +33,4 @@ func TestVm(t *testing.T) {
|
||||
tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
|
||||
bm.ApplyTransactions(block, []*Transaction{tx2})
|
||||
}
|
||||
*/
|
||||
|
90
ethchain/closure.go
Normal file
90
ethchain/closure.go
Normal 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
|
||||
}
|
@ -10,25 +10,21 @@ type Contract struct {
|
||||
Nonce uint64
|
||||
//state *ethutil.Trie
|
||||
state *State
|
||||
address []byte
|
||||
}
|
||||
|
||||
func NewContract(Amount *big.Int, root []byte) *Contract {
|
||||
contract := &Contract{Amount: Amount, Nonce: 0}
|
||||
func NewContract(address []byte, Amount *big.Int, root []byte) *Contract {
|
||||
contract := &Contract{address: address, Amount: Amount, Nonce: 0}
|
||||
contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root)))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
func (c *Contract) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root})
|
||||
}
|
||||
func NewContractFromBytes(address, data []byte) *Contract {
|
||||
contract := &Contract{address: address}
|
||||
contract.RlpDecode(data)
|
||||
|
||||
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()))
|
||||
return contract
|
||||
}
|
||||
|
||||
func (c *Contract) Addr(addr []byte) *ethutil.Value {
|
||||
@ -43,19 +39,45 @@ func (c *Contract) State() *State {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *Contract) GetMem(num int) *ethutil.Value {
|
||||
nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
|
||||
func (c *Contract) GetMem(num *big.Int) *ethutil.Value {
|
||||
nb := ethutil.BigToBytes(num, 256)
|
||||
|
||||
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 {
|
||||
// Create contract if there's no recipient
|
||||
if tx.IsContract() {
|
||||
addr := tx.Hash()[12:]
|
||||
|
||||
value := tx.Value
|
||||
contract := NewContract(value, []byte(""))
|
||||
contract := NewContract(addr, value, []byte(""))
|
||||
state.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||
for i, val := range tx.Data {
|
||||
if len(val) > 0 {
|
||||
|
@ -2,6 +2,7 @@ package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
@ -9,6 +10,7 @@ type OpCode int
|
||||
|
||||
// Op codes
|
||||
const (
|
||||
// 0x0 range - arithmetic ops
|
||||
oSTOP = 0x00
|
||||
oADD = 0x01
|
||||
oMUL = 0x02
|
||||
@ -20,50 +22,65 @@ const (
|
||||
oEXP = 0x08
|
||||
oNEG = 0x09
|
||||
oLT = 0x0a
|
||||
oLE = 0x0b
|
||||
oGT = 0x0c
|
||||
oGE = 0x0d
|
||||
oEQ = 0x0e
|
||||
oNOT = 0x0f
|
||||
oMYADDRESS = 0x10
|
||||
oTXSENDER = 0x11
|
||||
oTXVALUE = 0x12
|
||||
oTXDATAN = 0x13
|
||||
oTXDATA = 0x14
|
||||
oBLK_PREVHASH = 0x15
|
||||
oBLK_COINBASE = 0x16
|
||||
oBLK_TIMESTAMP = 0x17
|
||||
oBLK_NUMBER = 0x18
|
||||
oBLK_DIFFICULTY = 0x19
|
||||
oBLK_NONCE = 0x1a
|
||||
oBASEFEE = 0x1b
|
||||
oSHA256 = 0x20
|
||||
oRIPEMD160 = 0x21
|
||||
oECMUL = 0x22
|
||||
oECADD = 0x23
|
||||
oECSIGN = 0x24
|
||||
oECRECOVER = 0x25
|
||||
oECVALID = 0x26
|
||||
oSHA3 = 0x27
|
||||
oPUSH = 0x30
|
||||
oPOP = 0x31
|
||||
oDUP = 0x32
|
||||
oSWAP = 0x33
|
||||
oMLOAD = 0x34
|
||||
oMSTORE = 0x35
|
||||
oSLOAD = 0x36
|
||||
oSSTORE = 0x37
|
||||
oJMP = 0x38
|
||||
oJMPI = 0x39
|
||||
oIND = 0x3a
|
||||
oEXTRO = 0x3b
|
||||
oBALANCE = 0x3c
|
||||
oMKTX = 0x3d
|
||||
oSUICIDE = 0x3f
|
||||
oGT = 0x0b
|
||||
oEQ = 0x0c
|
||||
oNOT = 0x0d
|
||||
|
||||
// 0x10 range - bit ops
|
||||
oAND = 0x10
|
||||
oOR = 0x11
|
||||
oXOR = 0x12
|
||||
oBYTE = 0x13
|
||||
|
||||
// 0x20 range - crypto
|
||||
oSHA3 = 0x20
|
||||
|
||||
// 0x30 range - closure state
|
||||
oADDRESS = 0x30
|
||||
oBALANCE = 0x31
|
||||
oORIGIN = 0x32
|
||||
oCALLER = 0x33
|
||||
oCALLVALUE = 0x34
|
||||
oCALLDATA = 0x35
|
||||
oCALLDATASIZE = 0x36
|
||||
oGASPRICE = 0x37
|
||||
|
||||
// 0x40 range - block operations
|
||||
oPREVHASH = 0x40
|
||||
oCOINBASE = 0x41
|
||||
oTIMESTAMP = 0x42
|
||||
oNUMBER = 0x43
|
||||
oDIFFICULTY = 0x44
|
||||
oGASLIMIT = 0x45
|
||||
|
||||
// 0x50 range - 'storage' and execution
|
||||
oPUSH = 0x50
|
||||
oPOP = 0x51
|
||||
oDUP = 0x52
|
||||
oSWAP = 0x53
|
||||
oMLOAD = 0x54
|
||||
oMSTORE = 0x55
|
||||
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
|
||||
var opCodeToString = map[OpCode]string{
|
||||
// 0x0 range - arithmetic ops
|
||||
oSTOP: "STOP",
|
||||
oADD: "ADD",
|
||||
oMUL: "MUL",
|
||||
@ -75,44 +92,59 @@ var opCodeToString = map[OpCode]string{
|
||||
oEXP: "EXP",
|
||||
oNEG: "NEG",
|
||||
oLT: "LT",
|
||||
oLE: "LE",
|
||||
oGT: "GT",
|
||||
oGE: "GE",
|
||||
oEQ: "EQ",
|
||||
oNOT: "NOT",
|
||||
oMYADDRESS: "MYADDRESS",
|
||||
oTXSENDER: "TXSENDER",
|
||||
oTXVALUE: "TXVALUE",
|
||||
oTXDATAN: "TXDATAN",
|
||||
oTXDATA: "TXDATA",
|
||||
oBLK_PREVHASH: "BLK_PREVHASH",
|
||||
oBLK_COINBASE: "BLK_COINBASE",
|
||||
oBLK_TIMESTAMP: "BLK_TIMESTAMP",
|
||||
oBLK_NUMBER: "BLK_NUMBER",
|
||||
oBLK_DIFFICULTY: "BLK_DIFFICULTY",
|
||||
oBASEFEE: "BASEFEE",
|
||||
oSHA256: "SHA256",
|
||||
oRIPEMD160: "RIPEMD160",
|
||||
oECMUL: "ECMUL",
|
||||
oECADD: "ECADD",
|
||||
oECSIGN: "ECSIGN",
|
||||
oECRECOVER: "ECRECOVER",
|
||||
oECVALID: "ECVALID",
|
||||
|
||||
// 0x10 range - bit ops
|
||||
oAND: "AND",
|
||||
oOR: "OR",
|
||||
oXOR: "XOR",
|
||||
oBYTE: "BYTE",
|
||||
|
||||
// 0x20 range - crypto
|
||||
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",
|
||||
oPOP: "POP",
|
||||
oDUP: "DUP",
|
||||
oSWAP: "SWAP",
|
||||
oMLOAD: "MLOAD",
|
||||
oMSTORE: "MSTORE",
|
||||
oMSTORE8: "MSTORE8",
|
||||
oSLOAD: "SLOAD",
|
||||
oSSTORE: "SSTORE",
|
||||
oJMP: "JMP",
|
||||
oJMPI: "JMPI",
|
||||
oIND: "IND",
|
||||
oEXTRO: "EXTRO",
|
||||
oBALANCE: "BALANCE",
|
||||
oMKTX: "MKTX",
|
||||
oJUMP: "JUMP",
|
||||
oJUMPI: "JUMPI",
|
||||
oPC: "PC",
|
||||
oMSIZE: "MSIZE",
|
||||
|
||||
// 0x60 range - closures
|
||||
oCREATE: "CREATE",
|
||||
oCALL: "CALL",
|
||||
oRETURN: "RETURN",
|
||||
|
||||
// 0x70 range - other
|
||||
oLOG: "LOG",
|
||||
oSUICIDE: "SUICIDE",
|
||||
}
|
||||
|
||||
@ -141,35 +173,27 @@ func NewStack() *Stack {
|
||||
}
|
||||
|
||||
func (st *Stack) Pop() *big.Int {
|
||||
s := len(st.data)
|
||||
|
||||
str := st.data[s-1]
|
||||
st.data = st.data[:s-1]
|
||||
str := st.data[0]
|
||||
st.data = st.data[1:]
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (st *Stack) Popn() (*big.Int, *big.Int) {
|
||||
s := len(st.data)
|
||||
|
||||
ints := st.data[s-2:]
|
||||
st.data = st.data[:s-2]
|
||||
ints := st.data[:2]
|
||||
st.data = st.data[2:]
|
||||
|
||||
return ints[0], ints[1]
|
||||
}
|
||||
|
||||
func (st *Stack) Peek() *big.Int {
|
||||
s := len(st.data)
|
||||
|
||||
str := st.data[s-1]
|
||||
str := st.data[0]
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (st *Stack) Peekn() (*big.Int, *big.Int) {
|
||||
s := len(st.data)
|
||||
|
||||
ints := st.data[s-2:]
|
||||
ints := st.data[:2]
|
||||
|
||||
return ints[0], ints[1]
|
||||
}
|
||||
@ -188,3 +212,45 @@ func (st *Stack) Print() {
|
||||
}
|
||||
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 {
|
||||
}
|
||||
|
||||
// build contract
|
||||
contract := &Contract{}
|
||||
contract.RlpDecode([]byte(data))
|
||||
contract := NewContractFromBytes(addr, []byte(data))
|
||||
|
||||
// Check if there's a cached state for this contract
|
||||
cachedState := s.states[string(addr)]
|
||||
@ -78,27 +77,19 @@ func (s *State) GetContract(addr []byte) *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()))
|
||||
}
|
||||
|
||||
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) {
|
||||
data := s.trie.Get(string(addr))
|
||||
if data == "" {
|
||||
account = NewAccount(big.NewInt(0))
|
||||
account = NewAccount(addr, big.NewInt(0))
|
||||
} else {
|
||||
account = NewAccountFromData([]byte(data))
|
||||
account = NewAccountFromData(addr, []byte(data))
|
||||
}
|
||||
|
||||
return
|
||||
@ -153,3 +144,35 @@ func (s *State) Get(key []byte) (*ethutil.Value, ObjType) {
|
||||
|
||||
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
|
||||
}
|
||||
}()
|
||||
*/
|
||||
|
||||
vm := &Vm{}
|
||||
//vm.Process(contract, block.state, RuntimeVars{
|
||||
vm.Process(contract, sm.procState, RuntimeVars{
|
||||
address: tx.Hash()[12:],
|
||||
caller := sm.procState.GetAccount(tx.Sender())
|
||||
closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value)
|
||||
vm := NewVm(sm.procState, RuntimeVars{
|
||||
origin: caller.Address(),
|
||||
blockNumber: block.BlockInfo().Number,
|
||||
sender: tx.Sender(),
|
||||
prevHash: block.PrevHash,
|
||||
coinbase: block.Coinbase,
|
||||
time: block.Time,
|
||||
diff: block.Difficulty,
|
||||
txValue: tx.Value,
|
||||
txData: tx.Data,
|
||||
// XXX Tx data? Could be just an argument to the closure instead
|
||||
txData: nil,
|
||||
})
|
||||
closure.Call(vm, nil)
|
||||
}
|
||||
|
@ -13,22 +13,31 @@ type Transaction struct {
|
||||
Nonce uint64
|
||||
Recipient []byte
|
||||
Value *big.Int
|
||||
Gas *big.Int
|
||||
Gasprice *big.Int
|
||||
Data []string
|
||||
Memory []int
|
||||
v byte
|
||||
r, s []byte
|
||||
}
|
||||
|
||||
func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
|
||||
tx := Transaction{Recipient: to, Value: value}
|
||||
tx.Nonce = 0
|
||||
|
||||
// Serialize the data
|
||||
tx.Data = data
|
||||
tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data}
|
||||
|
||||
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
|
||||
func NewTransactionFromData(data []byte) *Transaction {
|
||||
return NewTransactionFromBytes(data)
|
||||
|
457
ethchain/vm.go
457
ethchain/vm.go
@ -1,12 +1,12 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
_ "bytes"
|
||||
_ "fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
_ "github.com/obscuren/secp256k1-go"
|
||||
"log"
|
||||
"math"
|
||||
_ "math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
@ -18,122 +18,102 @@ type Vm struct {
|
||||
mem map[string]*big.Int
|
||||
|
||||
vars RuntimeVars
|
||||
|
||||
state *State
|
||||
}
|
||||
|
||||
type RuntimeVars struct {
|
||||
address []byte
|
||||
origin []byte
|
||||
blockNumber uint64
|
||||
sender []byte
|
||||
prevHash []byte
|
||||
coinbase []byte
|
||||
time int64
|
||||
diff *big.Int
|
||||
txValue *big.Int
|
||||
txData []string
|
||||
}
|
||||
|
||||
func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) {
|
||||
vm.mem = make(map[string]*big.Int)
|
||||
vm.stack = NewStack()
|
||||
func NewVm(state *State, vars RuntimeVars) *Vm {
|
||||
return &Vm{vars: vars, state: state}
|
||||
}
|
||||
|
||||
addr := vars.address // tx.Hash()[12:]
|
||||
// Instruction pointer
|
||||
pc := 0
|
||||
var Pow256 = ethutil.BigPow(2, 256)
|
||||
|
||||
if contract == nil {
|
||||
fmt.Println("Contract not found")
|
||||
return
|
||||
func (vm *Vm) RunClosure(closure *Closure) []byte {
|
||||
// If the amount of gas supplied is less equal to 0
|
||||
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 {
|
||||
ethutil.Config.Log.Debugf("# op\n")
|
||||
}
|
||||
|
||||
stepcount := 0
|
||||
totalFee := new(big.Int)
|
||||
|
||||
out:
|
||||
for {
|
||||
stepcount++
|
||||
// The base big int for all calculations. Use this for any results.
|
||||
base := new(big.Int)
|
||||
val := contract.GetMem(pc)
|
||||
//fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb)
|
||||
step++
|
||||
// Get the memory location of pc
|
||||
val := closure.GetMem(pc)
|
||||
// Get the opcode (it must be an opcode!)
|
||||
op := OpCode(val.Uint())
|
||||
|
||||
var fee *big.Int = new(big.Int)
|
||||
var fee2 *big.Int = new(big.Int)
|
||||
if stepcount > 16 {
|
||||
fee.Add(fee, StepFee)
|
||||
}
|
||||
|
||||
// Calculate the fees
|
||||
switch op {
|
||||
case oSSTORE:
|
||||
y, x := vm.stack.Peekn()
|
||||
val := contract.Addr(ethutil.BigToBytes(x, 256))
|
||||
if val.IsEmpty() && len(y.Bytes()) > 0 {
|
||||
fee2.Add(DataFee, StoreFee)
|
||||
} else {
|
||||
fee2.Sub(DataFee, StoreFee)
|
||||
}
|
||||
case oSLOAD:
|
||||
fee.Add(fee, StoreFee)
|
||||
case oEXTRO, oBALANCE:
|
||||
fee.Add(fee, ExtroFee)
|
||||
case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID:
|
||||
fee.Add(fee, CryptoFee)
|
||||
case oMKTX:
|
||||
fee.Add(fee, ContractFee)
|
||||
}
|
||||
|
||||
tf := new(big.Int).Add(fee, fee2)
|
||||
if contract.Amount.Cmp(tf) < 0 {
|
||||
fmt.Println("Insufficient fees to continue running the contract", tf, contract.Amount)
|
||||
break
|
||||
}
|
||||
// Add the fee to the total fee. It's subtracted when we're done looping
|
||||
totalFee.Add(totalFee, tf)
|
||||
|
||||
if ethutil.Config.Debug {
|
||||
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
|
||||
}
|
||||
|
||||
// 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 {
|
||||
case oSTOP:
|
||||
fmt.Println("")
|
||||
break out
|
||||
case oLOG:
|
||||
stack.Print()
|
||||
mem.Print()
|
||||
case oSTOP: // Stop the closure
|
||||
return closure.Return(nil)
|
||||
|
||||
// 0x20 range
|
||||
case oADD:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// (x + y) % 2 ** 256
|
||||
base.Add(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
vm.stack.Push(base)
|
||||
stack.Push(base)
|
||||
case oSUB:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// (x - y) % 2 ** 256
|
||||
base.Sub(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
vm.stack.Push(base)
|
||||
stack.Push(base)
|
||||
case oMUL:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// (x * y) % 2 ** 256
|
||||
base.Mul(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
vm.stack.Push(base)
|
||||
stack.Push(base)
|
||||
case oDIV:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// floor(x / y)
|
||||
base.Div(x, y)
|
||||
// Pop result back on the stack
|
||||
vm.stack.Push(base)
|
||||
stack.Push(base)
|
||||
case oSDIV:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
x.Sub(Pow256, x)
|
||||
@ -147,13 +127,13 @@ out:
|
||||
z.Sub(Pow256, z)
|
||||
}
|
||||
// Push result on to the stack
|
||||
vm.stack.Push(z)
|
||||
stack.Push(z)
|
||||
case oMOD:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
base.Mod(x, y)
|
||||
vm.stack.Push(base)
|
||||
stack.Push(base)
|
||||
case oSMOD:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
x.Sub(Pow256, x)
|
||||
@ -167,213 +147,154 @@ out:
|
||||
z.Sub(Pow256, z)
|
||||
}
|
||||
// Push result on to the stack
|
||||
vm.stack.Push(z)
|
||||
stack.Push(z)
|
||||
case oEXP:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
base.Exp(x, y, Pow256)
|
||||
|
||||
vm.stack.Push(base)
|
||||
stack.Push(base)
|
||||
case oNEG:
|
||||
base.Sub(Pow256, vm.stack.Pop())
|
||||
vm.stack.Push(base)
|
||||
base.Sub(Pow256, stack.Pop())
|
||||
stack.Push(base)
|
||||
case oLT:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// x < y
|
||||
if x.Cmp(y) < 0 {
|
||||
vm.stack.Push(ethutil.BigTrue)
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
vm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oLE:
|
||||
x, y := vm.stack.Popn()
|
||||
// x <= y
|
||||
if x.Cmp(y) < 1 {
|
||||
vm.stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
vm.stack.Push(ethutil.BigFalse)
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oGT:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// x > y
|
||||
if x.Cmp(y) > 0 {
|
||||
vm.stack.Push(ethutil.BigTrue)
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
vm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oGE:
|
||||
x, y := vm.stack.Popn()
|
||||
// x >= y
|
||||
if x.Cmp(y) > -1 {
|
||||
vm.stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
vm.stack.Push(ethutil.BigFalse)
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oNOT:
|
||||
x, y := vm.stack.Popn()
|
||||
x, y := stack.Popn()
|
||||
// x != y
|
||||
if x.Cmp(y) != 0 {
|
||||
vm.stack.Push(ethutil.BigTrue)
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
vm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oMYADDRESS:
|
||||
vm.stack.Push(ethutil.BigD(addr))
|
||||
case oTXSENDER:
|
||||
vm.stack.Push(ethutil.BigD(vars.sender))
|
||||
case oTXVALUE:
|
||||
vm.stack.Push(vars.txValue)
|
||||
case oTXDATAN:
|
||||
vm.stack.Push(big.NewInt(int64(len(vars.txData))))
|
||||
case oTXDATA:
|
||||
v := vm.stack.Pop()
|
||||
// v >= len(data)
|
||||
if v.Cmp(big.NewInt(int64(len(vars.txData)))) >= 0 {
|
||||
vm.stack.Push(ethutil.Big("0"))
|
||||
} else {
|
||||
vm.stack.Push(ethutil.Big(vars.txData[v.Uint64()]))
|
||||
}
|
||||
case oBLK_PREVHASH:
|
||||
vm.stack.Push(ethutil.BigD(vars.prevHash))
|
||||
case oBLK_COINBASE:
|
||||
vm.stack.Push(ethutil.BigD(vars.coinbase))
|
||||
case oBLK_TIMESTAMP:
|
||||
vm.stack.Push(big.NewInt(vars.time))
|
||||
case oBLK_NUMBER:
|
||||
vm.stack.Push(big.NewInt(int64(vars.blockNumber)))
|
||||
case oBLK_DIFFICULTY:
|
||||
vm.stack.Push(vars.diff)
|
||||
case oBASEFEE:
|
||||
// e = 10^21
|
||||
e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0))
|
||||
d := new(big.Rat)
|
||||
d.SetInt(vars.diff)
|
||||
c := new(big.Rat)
|
||||
c.SetFloat64(0.5)
|
||||
// d = diff / 0.5
|
||||
d.Quo(d, c)
|
||||
// base = floor(d)
|
||||
base.Div(d.Num(), d.Denom())
|
||||
|
||||
x := new(big.Int)
|
||||
x.Div(e, base)
|
||||
|
||||
// x = floor(10^21 / floor(diff^0.5))
|
||||
vm.stack.Push(x)
|
||||
case oSHA256, oSHA3, oRIPEMD160:
|
||||
// This is probably save
|
||||
// ceil(pop / 32)
|
||||
length := int(math.Ceil(float64(vm.stack.Pop().Uint64()) / 32.0))
|
||||
// New buffer which will contain the concatenated popped items
|
||||
data := new(bytes.Buffer)
|
||||
for i := 0; i < length; i++ {
|
||||
// Encode the number to bytes and have it 32bytes long
|
||||
num := ethutil.NumberToBytes(vm.stack.Pop().Bytes(), 256)
|
||||
data.WriteString(string(num))
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
if op == oSHA256 {
|
||||
vm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
|
||||
} else if op == oSHA3 {
|
||||
vm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
|
||||
} else {
|
||||
vm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
|
||||
}
|
||||
case oECMUL:
|
||||
y := vm.stack.Pop()
|
||||
x := vm.stack.Pop()
|
||||
//n := vm.stack.Pop()
|
||||
// 0x10 range
|
||||
case oAND:
|
||||
case oOR:
|
||||
case oXOR:
|
||||
case oBYTE:
|
||||
|
||||
//if ethutil.Big(x).Cmp(ethutil.Big(y)) {
|
||||
data := new(bytes.Buffer)
|
||||
data.WriteString(x.String())
|
||||
data.WriteString(y.String())
|
||||
if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 {
|
||||
// TODO
|
||||
} else {
|
||||
// Invalid, push infinity
|
||||
vm.stack.Push(ethutil.Big("0"))
|
||||
vm.stack.Push(ethutil.Big("0"))
|
||||
}
|
||||
//} else {
|
||||
// // Invalid, push infinity
|
||||
// vm.stack.Push("0")
|
||||
// vm.stack.Push("0")
|
||||
//}
|
||||
// 0x20 range
|
||||
case oSHA3:
|
||||
|
||||
case oECADD:
|
||||
case oECSIGN:
|
||||
case oECRECOVER:
|
||||
case oECVALID:
|
||||
case oPUSH:
|
||||
pc++
|
||||
vm.stack.Push(contract.GetMem(pc).BigInt())
|
||||
case oPOP:
|
||||
// Pop current value of the stack
|
||||
vm.stack.Pop()
|
||||
case oDUP:
|
||||
// Dup top stack
|
||||
x := vm.stack.Pop()
|
||||
vm.stack.Push(x)
|
||||
vm.stack.Push(x)
|
||||
case oSWAP:
|
||||
// Swap two top most values
|
||||
x, y := vm.stack.Popn()
|
||||
vm.stack.Push(y)
|
||||
vm.stack.Push(x)
|
||||
case oMLOAD:
|
||||
x := vm.stack.Pop()
|
||||
vm.stack.Push(vm.mem[x.String()])
|
||||
case oMSTORE:
|
||||
x, y := vm.stack.Popn()
|
||||
vm.mem[x.String()] = y
|
||||
case oSLOAD:
|
||||
// Load the value in storage and push it on the stack
|
||||
x := vm.stack.Pop()
|
||||
// decode the object as a big integer
|
||||
decoder := contract.Addr(x.Bytes())
|
||||
if !decoder.IsNil() {
|
||||
vm.stack.Push(decoder.BigInt())
|
||||
} else {
|
||||
vm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oSSTORE:
|
||||
// Store Y at index X
|
||||
y, x := vm.stack.Popn()
|
||||
addr := ethutil.BigToBytes(x, 256)
|
||||
fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr))
|
||||
contract.SetAddr(addr, y)
|
||||
//contract.State().Update(string(idx), string(y))
|
||||
case oJMP:
|
||||
x := 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))
|
||||
// 0x30 range
|
||||
case oADDRESS:
|
||||
stack.Push(ethutil.BigD(closure.Object().Address()))
|
||||
case oBALANCE:
|
||||
// Pushes the balance of the popped value on to the stack
|
||||
account := state.GetAccount(vm.stack.Pop().Bytes())
|
||||
vm.stack.Push(account.Amount)
|
||||
case oMKTX:
|
||||
addr, value := vm.stack.Popn()
|
||||
from, length := vm.stack.Popn()
|
||||
stack.Push(closure.Value)
|
||||
case oORIGIN:
|
||||
stack.Push(ethutil.BigD(vm.vars.origin))
|
||||
case oCALLER:
|
||||
stack.Push(ethutil.BigD(closure.Callee().Address()))
|
||||
case oCALLVALUE:
|
||||
// FIXME: Original value of the call, not the current value
|
||||
stack.Push(closure.Value)
|
||||
case oCALLDATA:
|
||||
offset := stack.Pop()
|
||||
mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args)
|
||||
case oCALLDATASIZE:
|
||||
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:
|
||||
recAddr := vm.stack.Pop().Bytes()
|
||||
/*
|
||||
recAddr := stack.Pop().Bytes()
|
||||
// Purge all memory
|
||||
deletedMemory := contract.state.Purge()
|
||||
// Add refunds to the pop'ed address
|
||||
@ -387,30 +308,28 @@ out:
|
||||
|
||||
ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr)
|
||||
break out
|
||||
*/
|
||||
default:
|
||||
fmt.Printf("Invalid OPCODE: %x\n", op)
|
||||
}
|
||||
ethutil.Config.Log.Debugln("")
|
||||
//vm.stack.Print()
|
||||
pc++
|
||||
ethutil.Config.Log.Debugln("Invalid opcode", op)
|
||||
}
|
||||
|
||||
state.UpdateContract(addr, contract)
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
}
|
||||
}
|
||||
|
||||
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
|
||||
ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length)
|
||||
j := 0
|
||||
j := int64(0)
|
||||
dataItems := make([]string, int(length.Uint64()))
|
||||
for i := from.Uint64(); i < length.Uint64(); i++ {
|
||||
dataItems[j] = contract.GetMem(j).Str()
|
||||
for i := from.Int64(); i < length.Int64(); i++ {
|
||||
dataItems[j] = contract.GetMem(big.NewInt(j)).Str()
|
||||
j++
|
||||
}
|
||||
|
||||
tx := NewTransaction(addr, value, dataItems)
|
||||
if tx.IsContract() {
|
||||
contract := MakeContract(tx, state)
|
||||
state.UpdateContract(tx.Hash()[12:], contract)
|
||||
state.UpdateContract(contract)
|
||||
} else {
|
||||
account := state.GetAccount(tx.Recipient)
|
||||
account.Amount.Add(account.Amount, tx.Value)
|
||||
|
@ -1,13 +1,15 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
InitFees()
|
||||
|
||||
@ -104,3 +106,69 @@ func TestRun2(t *testing.T) {
|
||||
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 {
|
||||
|
||||
return fmt.Sprintf("%v Wei", num)
|
||||
}
|
||||
|
||||
var (
|
||||
Big1 = big.NewInt(1)
|
||||
Big0 = big.NewInt(0)
|
||||
Big256 = big.NewInt(0xff)
|
||||
)
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type LogType byte
|
||||
@ -23,6 +24,7 @@ type config struct {
|
||||
ExecPath string
|
||||
Debug bool
|
||||
Ver string
|
||||
ClientString string
|
||||
Pubkey []byte
|
||||
Seed bool
|
||||
}
|
||||
@ -48,11 +50,16 @@ func ReadConfig(base string) *config {
|
||||
|
||||
Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"}
|
||||
Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
|
||||
Config.SetClientString("/Ethereum(G)")
|
||||
}
|
||||
|
||||
return Config
|
||||
}
|
||||
|
||||
func (c *config) SetClientString(str string) {
|
||||
Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS)
|
||||
}
|
||||
|
||||
type LoggerType byte
|
||||
|
||||
const (
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
// Op codes
|
||||
var OpCodes = map[string]byte{
|
||||
// 0x0 range - arithmetic ops
|
||||
"STOP": 0x00,
|
||||
"ADD": 0x01,
|
||||
"MUL": 0x02,
|
||||
@ -18,46 +19,60 @@ var OpCodes = map[string]byte{
|
||||
"EXP": 0x08,
|
||||
"NEG": 0x09,
|
||||
"LT": 0x0a,
|
||||
"LE": 0x0b,
|
||||
"GT": 0x0c,
|
||||
"GE": 0x0d,
|
||||
"EQ": 0x0e,
|
||||
"NOT": 0x0f,
|
||||
"MYADDRESS": 0x10,
|
||||
"TXSENDER": 0x11,
|
||||
"TXVALUE": 0x12,
|
||||
"TXDATAN": 0x13,
|
||||
"TXDATA": 0x14,
|
||||
"BLK_PREVHASH": 0x15,
|
||||
"BLK_COINBASE": 0x16,
|
||||
"BLK_TIMESTAMP": 0x17,
|
||||
"BLK_NUMBER": 0x18,
|
||||
"BLK_DIFFICULTY": 0x19,
|
||||
"BLK_NONCE": 0x1a,
|
||||
"BASEFEE": 0x1b,
|
||||
"SHA256": 0x20,
|
||||
"RIPEMD160": 0x21,
|
||||
"ECMUL": 0x22,
|
||||
"ECADD": 0x23,
|
||||
"ECSIGN": 0x24,
|
||||
"ECRECOVER": 0x25,
|
||||
"ECVALID": 0x26,
|
||||
"SHA3": 0x27,
|
||||
"PUSH": 0x30,
|
||||
"POP": 0x31,
|
||||
"DUP": 0x32,
|
||||
"SWAP": 0x33,
|
||||
"MLOAD": 0x34,
|
||||
"MSTORE": 0x35,
|
||||
"SLOAD": 0x36,
|
||||
"SSTORE": 0x37,
|
||||
"JMP": 0x38,
|
||||
"JMPI": 0x39,
|
||||
"IND": 0x3a,
|
||||
"EXTRO": 0x3b,
|
||||
"BALANCE": 0x3c,
|
||||
"MKTX": 0x3d,
|
||||
"SUICIDE": 0x3f,
|
||||
"GT": 0x0b,
|
||||
"EQ": 0x0c,
|
||||
"NOT": 0x0d,
|
||||
|
||||
// 0x10 range - bit ops
|
||||
"AND": 0x10,
|
||||
"OR": 0x11,
|
||||
"XOR": 0x12,
|
||||
"BYTE": 0x13,
|
||||
|
||||
// 0x20 range - crypto
|
||||
"SHA3": 0x20,
|
||||
|
||||
// 0x30 range - closure state
|
||||
"ADDRESS": 0x30,
|
||||
"BALANCE": 0x31,
|
||||
"ORIGIN": 0x32,
|
||||
"CALLER": 0x33,
|
||||
"CALLVALUE": 0x34,
|
||||
"CALLDATA": 0x35,
|
||||
"CALLDATASIZE": 0x36,
|
||||
"GASPRICE": 0x38,
|
||||
|
||||
// 0x40 range - block operations
|
||||
"PREVHASH": 0x40,
|
||||
"COINBASE": 0x41,
|
||||
"TIMESTAMP": 0x42,
|
||||
"NUMBER": 0x43,
|
||||
"DIFFICULTY": 0x44,
|
||||
"GASLIMIT": 0x45,
|
||||
|
||||
// 0x50 range - 'storage' and execution
|
||||
"PUSH": 0x50,
|
||||
"POP": 0x51,
|
||||
"DUP": 0x52,
|
||||
"SWAP": 0x53,
|
||||
"MLOAD": 0x54,
|
||||
"MSTORE": 0x55,
|
||||
"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 {
|
||||
@ -69,16 +84,30 @@ func IsOpCode(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func CompileInstr(s string) ([]byte, error) {
|
||||
isOp := IsOpCode(s)
|
||||
func CompileInstr(s interface{}) ([]byte, error) {
|
||||
switch s.(type) {
|
||||
case string:
|
||||
str := s.(string)
|
||||
isOp := IsOpCode(str)
|
||||
if isOp {
|
||||
return []byte{OpCodes[s]}, nil
|
||||
return []byte{OpCodes[str]}, nil
|
||||
}
|
||||
|
||||
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 nil, nil
|
||||
}
|
||||
|
||||
func Instr(instr string) (int, []string, error) {
|
||||
@ -99,3 +128,17 @@ func Instr(instr string) (int, []string, error) {
|
||||
|
||||
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 (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type RlpEncodable interface {
|
||||
RlpEncode() []byte
|
||||
}
|
||||
|
||||
type RlpEncoder struct {
|
||||
rlpData []byte
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ethutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
3
peer.go
3
peer.go
@ -7,7 +7,6 @@ import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
@ -160,7 +159,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
|
||||
connected: 0,
|
||||
disconnect: 0,
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user