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