merge upstream

This commit is contained in:
zelig 2014-07-30 18:03:20 +02:00
commit 9831619881
37 changed files with 1323 additions and 868 deletions

View File

@ -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 0.5.20". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
of Concept 0.6.0". 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.

View File

@ -25,7 +25,7 @@ func Disassemble(script []byte) (asm []string) {
pc.Add(pc, ethutil.Big1)
a := int64(op) - int64(PUSH1) + 1
if int(pc.Int64()+a) > len(script) {
return nil
return
}
data := script[pc.Int64() : pc.Int64()+a]
@ -40,5 +40,5 @@ func Disassemble(script []byte) (asm []string) {
pc.Add(pc, ethutil.Big1)
}
return
return asm
}

View File

@ -3,12 +3,14 @@ package ethchain
import (
"bytes"
"fmt"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
"math/big"
_ "strconv"
"time"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
)
type BlockInfo struct {
@ -39,7 +41,7 @@ type Block struct {
Coinbase []byte
// Block Trie state
//state *ethutil.Trie
state *State
state *ethstate.State
// Difficulty for the current block
Difficulty *big.Int
// Creation time
@ -62,12 +64,6 @@ type Block struct {
TxSha []byte
}
// New block takes a raw encoded string
// XXX DEPRICATED
func NewBlockFromData(raw []byte) *Block {
return NewBlockFromBytes(raw)
}
func NewBlockFromBytes(raw []byte) *Block {
block := &Block{}
block.RlpDecode(raw)
@ -104,7 +100,7 @@ func CreateBlock(root interface{},
}
block.SetUncles([]*Block{})
block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, root))
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, root))
return block
}
@ -116,12 +112,12 @@ func (block *Block) Hash() []byte {
func (block *Block) HashNoNonce() []byte {
return ethcrypto.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash,
block.UncleSha, block.Coinbase, block.state.trie.Root,
block.UncleSha, block.Coinbase, block.state.Trie.Root,
block.TxSha, block.Difficulty, block.Number, block.MinGasPrice,
block.GasLimit, block.GasUsed, block.Time, block.Extra}))
}
func (block *Block) State() *State {
func (block *Block) State() *ethstate.State {
return block.state
}
@ -129,33 +125,6 @@ func (block *Block) Transactions() []*Transaction {
return block.transactions
}
func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
contract := block.state.GetStateObject(addr)
// If we can't pay the fee return
if contract == nil || contract.Amount.Cmp(fee) < 0 /* amount < fee */ {
fmt.Println("Contract has insufficient funds", contract.Amount, fee)
return false
}
base := new(big.Int)
contract.Amount = base.Sub(contract.Amount, fee)
block.state.trie.Update(string(addr), string(contract.RlpEncode()))
data := block.state.trie.Get(string(block.Coinbase))
// Get the ether (Coinbase) and add the fee (gief fee to miner)
account := NewStateObjectFromBytes(block.Coinbase, []byte(data))
base = new(big.Int)
account.Amount = base.Add(account.Amount, fee)
//block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
block.state.UpdateStateObject(account)
return true
}
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
if block.Number.Cmp(big.NewInt(0)) == 0 {
return ethutil.BigPow(10, 6)
@ -312,7 +281,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes()
block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt()
block.Number = header.Get(6).BigInt()
@ -354,7 +323,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes()
block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt()
block.Number = header.Get(6).BigInt()
@ -369,7 +338,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
}
func (block *Block) GetRoot() interface{} {
return block.state.trie.Root
return block.state.Trie.Root
}
func (self *Block) Receipts() []*Receipt {
@ -385,7 +354,7 @@ func (block *Block) header() []interface{} {
// Coinbase address
block.Coinbase,
// root state
block.state.trie.Root,
block.state.Trie.Root,
// Sha of tx
block.TxSha,
// Current block Difficulty
@ -429,7 +398,7 @@ func (block *Block) String() string {
block.PrevHash,
block.UncleSha,
block.Coinbase,
block.state.trie.Root,
block.state.Trie.Root,
block.TxSha,
block.Difficulty,
block.Number,

View File

@ -2,11 +2,12 @@ package ethchain
import (
"bytes"
"math"
"math/big"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"math"
"math/big"
)
var chainlogger = ethlog.NewLogger("CHAIN")
@ -44,7 +45,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
hash := ZeroHash256
if bc.CurrentBlock != nil {
root = bc.CurrentBlock.state.trie.Root
root = bc.CurrentBlock.state.Trie.Root
hash = bc.LastBlockHash
lastBlockTime = bc.CurrentBlock.Time
}
@ -280,7 +281,7 @@ func AddTestNetFunds(block *Block) {
} {
codedAddr := ethutil.Hex2Bytes(addr)
account := block.state.GetAccount(codedAddr)
account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
account.Balance = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
block.state.UpdateStateObject(account)
}
}
@ -297,7 +298,7 @@ func (bc *BlockChain) setLastBlock() {
} else {
AddTestNetFunds(bc.genesisBlock)
bc.genesisBlock.state.trie.Sync()
bc.genesisBlock.state.Trie.Sync()
// Prepare the genesis block
bc.Add(bc.genesisBlock)

View File

@ -7,6 +7,7 @@ import (
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"math/big"
@ -50,8 +51,6 @@ type StateManager struct {
mutex sync.Mutex
// Canonical block chain
bc *BlockChain
// Stack for processing contracts
stack *Stack
// non-persistent key/value memory storage
mem map[string]*big.Int
// Proof of work used for validating
@ -62,10 +61,10 @@ type StateManager struct {
// Transiently state. The trans state isn't ever saved, validated and
// it could be used for setting account nonces without effecting
// the main states.
transState *State
transState *ethstate.State
// Mining state. The mining state is used purely and solely by the mining
// operation.
miningState *State
miningState *ethstate.State
// The last attempted block is mainly used for debugging purposes
// This does not have to be a valid block and will be set during
@ -75,7 +74,6 @@ type StateManager struct {
func NewStateManager(ethereum EthManager) *StateManager {
sm := &StateManager{
stack: NewStack(),
mem: make(map[string]*big.Int),
Pow: &EasyPow{},
Ethereum: ethereum,
@ -87,19 +85,19 @@ func NewStateManager(ethereum EthManager) *StateManager {
return sm
}
func (sm *StateManager) CurrentState() *State {
func (sm *StateManager) CurrentState() *ethstate.State {
return sm.Ethereum.BlockChain().CurrentBlock.State()
}
func (sm *StateManager) TransState() *State {
func (sm *StateManager) TransState() *ethstate.State {
return sm.transState
}
func (sm *StateManager) MiningState() *State {
func (sm *StateManager) MiningState() *ethstate.State {
return sm.miningState
}
func (sm *StateManager) NewMiningState() *State {
func (sm *StateManager) NewMiningState() *ethstate.State {
sm.miningState = sm.Ethereum.BlockChain().CurrentBlock.State().Copy()
return sm.miningState
@ -109,7 +107,7 @@ func (sm *StateManager) BlockChain() *BlockChain {
return sm.bc
}
func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
func (self *StateManager) ProcessTransactions(coinbase *ethstate.StateObject, state *ethstate.State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
var (
receipts Receipts
handled, unhandled Transactions
@ -123,9 +121,9 @@ done:
cb := state.GetStateObject(coinbase.Address())
st := NewStateTransition(cb, tx, state, block)
//fmt.Printf("#%d\n", i+1)
err = st.TransitionState()
if err != nil {
statelogger.Infoln(err)
switch {
case IsNonceErr(err):
err = nil // ignore error
@ -225,7 +223,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
}
if !block.State().Cmp(state) {
err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root)
err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().Trie.Root, state.Trie.Root)
return
}
@ -242,7 +240,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
if dontReact == false {
sm.Ethereum.Reactor().Post("newBlock", block)
state.manifest.Reset()
state.Manifest().Reset()
}
sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
@ -255,7 +253,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
return nil
}
func (sm *StateManager) ApplyDiff(state *State, parent, block *Block) (receipts Receipts, err error) {
func (sm *StateManager) ApplyDiff(state *ethstate.State, parent, block *Block) (receipts Receipts, err error) {
coinbase := state.GetOrNewStateObject(block.Coinbase)
coinbase.SetGasPool(block.CalcGasLimit(parent))
@ -340,7 +338,7 @@ func CalculateUncleReward(block *Block) *big.Int {
return UncleReward
}
func (sm *StateManager) AccumelateRewards(state *State, block *Block) error {
func (sm *StateManager) AccumelateRewards(state *ethstate.State, block *Block) error {
// Get the account associated with the coinbase
account := state.GetAccount(block.Coinbase)
// Reward amount of ether to the coinbase address
@ -364,14 +362,14 @@ func (sm *StateManager) Stop() {
sm.bc.Stop()
}
func (sm *StateManager) notifyChanges(state *State) {
for addr, stateObject := range state.manifest.objectChanges {
func (sm *StateManager) notifyChanges(state *ethstate.State) {
for addr, stateObject := range state.Manifest().ObjectChanges {
sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
}
for stateObjectAddr, mappedObjects := range state.manifest.storageChanges {
for stateObjectAddr, mappedObjects := range state.Manifest().StorageChanges {
for addr, value := range mappedObjects {
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &StorageState{[]byte(stateObjectAddr), []byte(addr), value})
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &ethstate.StorageState{[]byte(stateObjectAddr), []byte(addr), value})
}
}
}

View File

@ -1,52 +0,0 @@
package ethchain
import (
"fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"testing"
)
func TestSync(t *testing.T) {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
db, _ := ethdb.NewMemDatabase()
state := NewState(ethutil.NewTrie(db, ""))
contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
contract.script = []byte{42}
state.UpdateStateObject(contract)
state.Sync()
object := state.GetStateObject([]byte("aa"))
if len(object.Script()) == 0 {
t.Fail()
}
}
func TestObjectGet(t *testing.T) {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
db, _ := ethdb.NewMemDatabase()
ethutil.Config.Db = db
state := NewState(ethutil.NewTrie(db, ""))
contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
state.UpdateStateObject(contract)
contract = state.GetStateObject([]byte("aa"))
contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello"))
o := contract.GetMem(big.NewInt(0))
fmt.Println(o)
state.UpdateStateObject(contract)
contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello00"))
contract = state.GetStateObject([]byte("aa"))
o = contract.GetMem(big.NewInt(0))
fmt.Println("after", o)
}

View File

@ -3,6 +3,11 @@ package ethchain
import (
"fmt"
"math/big"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethvm"
)
/*
@ -27,17 +32,17 @@ type StateTransition struct {
gas, gasPrice *big.Int
value *big.Int
data []byte
state *State
state *ethstate.State
block *Block
cb, rec, sen *StateObject
cb, rec, sen *ethstate.StateObject
}
func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition {
func NewStateTransition(coinbase *ethstate.StateObject, tx *Transaction, state *ethstate.State, block *Block) *StateTransition {
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
}
func (self *StateTransition) Coinbase() *StateObject {
func (self *StateTransition) Coinbase() *ethstate.StateObject {
if self.cb != nil {
return self.cb
}
@ -45,7 +50,7 @@ func (self *StateTransition) Coinbase() *StateObject {
self.cb = self.state.GetOrNewStateObject(self.coinbase)
return self.cb
}
func (self *StateTransition) Sender() *StateObject {
func (self *StateTransition) Sender() *ethstate.StateObject {
if self.sen != nil {
return self.sen
}
@ -54,7 +59,7 @@ func (self *StateTransition) Sender() *StateObject {
return self.sen
}
func (self *StateTransition) Receiver() *StateObject {
func (self *StateTransition) Receiver() *ethstate.StateObject {
if self.tx != nil && self.tx.CreatesContract() {
return nil
}
@ -67,7 +72,7 @@ func (self *StateTransition) Receiver() *StateObject {
return self.rec
}
func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject {
func (self *StateTransition) MakeStateObject(state *ethstate.State, tx *Transaction) *ethstate.StateObject {
contract := MakeContract(tx, state)
return contract
@ -90,8 +95,8 @@ func (self *StateTransition) BuyGas() error {
var err error
sender := self.Sender()
if sender.Amount.Cmp(self.tx.GasValue()) < 0 {
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Amount)
if sender.Balance.Cmp(self.tx.GasValue()) < 0 {
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance)
}
coinbase := self.Coinbase()
@ -154,7 +159,7 @@ func (self *StateTransition) TransitionState() (err error) {
var (
tx = self.tx
sender = self.Sender()
receiver *StateObject
receiver *ethstate.StateObject
)
defer self.RefundGas()
@ -163,22 +168,22 @@ func (self *StateTransition) TransitionState() (err error) {
sender.Nonce += 1
// Transaction gas
if err = self.UseGas(GasTx); err != nil {
if err = self.UseGas(ethvm.GasTx); err != nil {
return
}
// Pay data gas
dataPrice := big.NewInt(int64(len(self.data)))
dataPrice.Mul(dataPrice, GasData)
dataPrice.Mul(dataPrice, ethvm.GasData)
if err = self.UseGas(dataPrice); err != nil {
return
}
if sender.Amount.Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
if sender.Balance.Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance)
}
var snapshot *State
var snapshot *ethstate.State
// If the receiver is nil it's a contract (\0*32).
if tx.CreatesContract() {
// Subtract the (irreversible) amount from the senders account
@ -220,10 +225,10 @@ func (self *StateTransition) TransitionState() (err error) {
return fmt.Errorf("Error during init execution %v", err)
}
receiver.script = code
receiver.Code = code
} else {
if len(receiver.Script()) > 0 {
_, err = self.Eval(receiver.Script(), receiver, "code")
if len(receiver.Code) > 0 {
_, err = self.Eval(receiver.Code, receiver, "code")
if err != nil {
self.state.Set(snapshot)
@ -235,9 +240,9 @@ func (self *StateTransition) TransitionState() (err error) {
return
}
func (self *StateTransition) transferValue(sender, receiver *StateObject) error {
if sender.Amount.Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
func (self *StateTransition) transferValue(sender, receiver *ethstate.StateObject) error {
if sender.Balance.Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance)
}
// Subtract the amount from the senders account
@ -248,34 +253,35 @@ func (self *StateTransition) transferValue(sender, receiver *StateObject) error
return nil
}
func (self *StateTransition) Eval(script []byte, context *StateObject, typ string) (ret []byte, err error) {
func (self *StateTransition) Eval(script []byte, context *ethstate.StateObject, typ string) (ret []byte, err error) {
var (
block = self.block
initiator = self.Sender()
transactor = self.Sender()
state = self.state
env = NewEnv(state, self.tx, self.block)
callerClosure = ethvm.NewClosure(transactor, context, script, self.gas, self.gasPrice)
)
closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice)
vm := NewVm(state, nil, RuntimeVars{
Origin: initiator.Address(),
Block: block,
BlockNumber: block.Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
Value: self.value,
})
vm := ethvm.New(env)
vm.Verbose = true
vm.Fn = typ
ret, err = Call(vm, closure, self.data)
ret, _, err = callerClosure.Call(vm, self.tx.Data)
return
}
func Call(vm *Vm, closure *Closure, data []byte) (ret []byte, err error) {
ret, _, err = closure.Call(vm, data)
// Converts an transaction in to a state object
func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject {
// Create contract if there's no recipient
if tx.IsContract() {
addr := tx.CreationAddress()
return
contract := state.NewStateObject(addr)
contract.InitCode = tx.Data
contract.State = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
return contract
}
return nil
}

View File

@ -4,10 +4,12 @@ import (
"bytes"
"container/list"
"fmt"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethwire"
"math/big"
"sync"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethwire"
)
var txplogger = ethlog.NewLogger("TXP")
@ -90,78 +92,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
}
/*
// Process transaction validates the Tx and processes funds from the
// sender to the recipient.
func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (gas *big.Int, err error) {
fmt.Printf("state root before update %x\n", state.Root())
defer func() {
if r := recover(); r != nil {
txplogger.Infoln(r)
err = fmt.Errorf("%v", r)
}
}()
gas = new(big.Int)
addGas := func(g *big.Int) { gas.Add(gas, g) }
addGas(GasTx)
// Get the sender
sender := state.GetAccount(tx.Sender())
if sender.Nonce != tx.Nonce {
err = NonceError(tx.Nonce, sender.Nonce)
return
}
sender.Nonce += 1
defer func() {
//state.UpdateStateObject(sender)
// Notify all subscribers
pool.Ethereum.Reactor().Post("newTx:post", tx)
}()
txTotalBytes := big.NewInt(int64(len(tx.Data)))
txTotalBytes.Div(txTotalBytes, ethutil.Big32)
addGas(new(big.Int).Mul(txTotalBytes, GasSStore))
rGas := new(big.Int).Set(gas)
rGas.Mul(gas, tx.GasPrice)
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
totAmount := new(big.Int).Add(tx.Value, rGas)
if sender.Amount.Cmp(totAmount) < 0 {
err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
return
}
state.UpdateStateObject(sender)
fmt.Printf("state root after sender update %x\n", state.Root())
// Get the receiver
receiver := state.GetAccount(tx.Recipient)
// Send Tx to self
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
// Subtract the fee
sender.SubAmount(rGas)
} else {
// Subtract the amount from the senders account
sender.SubAmount(totAmount)
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(tx.Value)
state.UpdateStateObject(receiver)
fmt.Printf("state root after receiver update %x\n", state.Root())
}
txplogger.Infof("[TXPL] Processed Tx %x\n", tx.Hash())
return
}
*/
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
// Get the last block so we can retrieve the sender and receiver from
// the merkle trie
@ -182,7 +112,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
totAmount := new(big.Int).Set(tx.Value)
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(totAmount) < 0 {
if sender.Balance.Cmp(totAmount) < 0 {
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
}
@ -252,7 +182,7 @@ func (pool *TxPool) CurrentTransactions() []*Transaction {
return txList
}
func (pool *TxPool) RemoveInvalid(state *State) {
func (pool *TxPool) RemoveInvalid(state *ethstate.State) {
for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*Transaction)
sender := state.GetAccount(tx.Sender())

29
ethchain/vm_env.go Normal file
View File

@ -0,0 +1,29 @@
package ethchain
import (
"github.com/ethereum/eth-go/ethstate"
"math/big"
)
type VMEnv struct {
state *ethstate.State
block *Block
tx *Transaction
}
func NewEnv(state *ethstate.State, tx *Transaction, block *Block) *VMEnv {
return &VMEnv{
state: state,
block: block,
tx: tx,
}
}
func (self *VMEnv) Origin() []byte { return self.tx.Sender() }
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
func (self *VMEnv) Time() int64 { return self.block.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
func (self *VMEnv) Value() *big.Int { return self.tx.Value }
func (self *VMEnv) State() *ethstate.State { return self.state }

View File

@ -1,68 +0,0 @@
package ethchain
import (
_ "bytes"
"fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"testing"
)
func TestRun4(t *testing.T) {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
db, _ := ethdb.NewMemDatabase()
state := NewState(ethutil.NewTrie(db, ""))
callerScript, err := ethutil.Compile(`
this.store[this.origin()] = 10**20
hello := "world"
return lambda {
big to = this.data[0]
big from = this.origin()
big value = this.data[1]
if this.store[from] >= value {
this.store[from] = this.store[from] - value
this.store[to] = this.store[to] + value
}
}
`)
if err != nil {
fmt.Println(err)
}
fmt.Println(Disassemble(callerScript))
callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript)
callerTx.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
// Contract addr as test address
gas := big.NewInt(1000)
gasPrice := big.NewInt(10)
account := NewAccount(ContractAddr, big.NewInt(10000000))
fmt.Println("account.Amount =", account.Amount)
c := MakeContract(callerTx, state)
e := account.ConvertGas(gas, gasPrice)
if e != nil {
fmt.Println(err)
}
fmt.Println("account.Amount =", account.Amount)
callerClosure := NewClosure(account, c, callerScript, state, gas, gasPrice)
vm := NewVm(state, nil, RuntimeVars{
Origin: account.Address(),
BlockNumber: big.NewInt(1),
PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
Time: 1,
Diff: big.NewInt(256),
})
var ret []byte
ret, _, e = callerClosure.Call(vm, nil, nil)
if e != nil {
fmt.Println("error", e)
}
fmt.Println(ret)
}

View File

@ -3,6 +3,14 @@ package eth
import (
"container/list"
"fmt"
"math/rand"
"net"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
@ -10,18 +18,12 @@ import (
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"io/ioutil"
"math/rand"
"net"
"net/http"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
)
const seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
const (
seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
seedNodeAddress = "54.76.56.74:30303"
)
var ethlogger = ethlog.NewLogger("SERV")
@ -422,22 +424,10 @@ func (s *Ethereum) Seed() {
}
// Connect to Peer list
s.ProcessPeerList(peers)
} else {
// Fallback to servers.poc3.txt
resp, err := http.Get(seedTextFileUri)
if err != nil {
ethlogger.Warnln("Fetching seed failed:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
ethlogger.Warnln("Reading seed failed:", err)
return
}
s.ConnectToPeer(string(body))
}
// XXX tmp
s.ConnectToPeer(seedNodeAddress)
}
func (s *Ethereum) peerHandler(listener net.Listener) {

View File

@ -6,6 +6,7 @@ import (
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"strings"
@ -24,11 +25,11 @@ type helper struct {
func EthereumConfig(stateManager *ethchain.StateManager) helper {
return helper{stateManager}
}
func (self helper) obj() *ethchain.StateObject {
func (self helper) obj() *ethstate.StateObject {
return self.sm.CurrentState().GetStateObject(cnfCtr)
}
func (self helper) NameReg() *ethchain.StateObject {
func (self helper) NameReg() *ethstate.StateObject {
if self.obj() != nil {
addr := self.obj().GetStorage(big.NewInt(0))
if len(addr.Bytes()) > 0 {
@ -48,6 +49,12 @@ type PEthereum struct {
}
func NewPEthereum(manager ethchain.EthManager) *PEthereum {
logger.Warnln("DEPRECATED: ethpub.New should be used in favour of ethpub.NewPEthereum")
return New(manager)
}
func New(manager ethchain.EthManager) *PEthereum {
return &PEthereum{
manager,
manager.StateManager(),

View File

@ -3,11 +3,13 @@ package ethpub
import (
"encoding/json"
"fmt"
"strings"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
"strings"
)
// Peer interface exposed to QML
@ -154,10 +156,10 @@ func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *
}
type PStateObject struct {
object *ethchain.StateObject
object *ethstate.StateObject
}
func NewPStateObject(object *ethchain.StateObject) *PStateObject {
func NewPStateObject(object *ethstate.StateObject) *PStateObject {
return &PStateObject{object: object}
}
@ -174,9 +176,9 @@ func (c *PStateObject) GetStorage(address string) string {
return ""
}
func (c *PStateObject) Value() string {
func (c *PStateObject) Balance() string {
if c.object != nil {
return c.object.Amount.String()
return c.object.Balance.String()
}
return ""
@ -200,7 +202,7 @@ func (c *PStateObject) Nonce() int {
func (c *PStateObject) Root() string {
if c.object != nil {
return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State().Root()).Bytes())
return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State.Root()).Bytes())
}
return "<err>"
@ -208,7 +210,7 @@ func (c *PStateObject) Root() string {
func (c *PStateObject) IsContract() bool {
if c.object != nil {
return len(c.object.Script()) > 0
return len(c.object.Code) > 0
}
return false
@ -245,7 +247,7 @@ func (c *PStateObject) StateKeyVal(asJson bool) interface{} {
func (c *PStateObject) Script() string {
if c.object != nil {
return strings.Join(ethchain.Disassemble(c.object.Script()), " ")
return strings.Join(ethchain.Disassemble(c.object.Code), " ")
}
return ""
@ -253,7 +255,7 @@ func (c *PStateObject) Script() string {
func (c *PStateObject) HexScript() string {
if c.object != nil {
return ethutil.Bytes2Hex(c.object.Script())
return ethutil.Bytes2Hex(c.object.Code)
}
return ""
@ -265,6 +267,6 @@ type PStorageState struct {
Value string
}
func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState {
func NewPStorageState(storageObject *ethstate.StorageState) *PStorageState {
return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()}
}

View File

@ -3,10 +3,11 @@ package ethrpc
import (
"encoding/json"
"errors"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"strings"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
)
type EthereumApi struct {
@ -272,7 +273,7 @@ func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error {
return err
}
state := p.ethp.GetStateObject(args.Address)
*reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address})
*reply = NewSuccessRes(BalanceRes{Balance: state.Balance(), Address: args.Address})
return nil
}

0
ethstate/.ethtest Normal file
View File

23
ethstate/errors.go Normal file
View File

@ -0,0 +1,23 @@
package ethstate
import (
"fmt"
"math/big"
)
type GasLimitErr struct {
Message string
Is, Max *big.Int
}
func IsGasLimitErr(err error) bool {
_, ok := err.(*GasLimitErr)
return ok
}
func (err *GasLimitErr) Error() string {
return err.Message
}
func GasLimitError(is, max *big.Int) *GasLimitErr {
return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
}

View File

@ -1,12 +1,16 @@
package ethchain
package ethstate
import (
"math/big"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
var statelogger = ethlog.NewLogger("STATE")
// States within the ethereum protocol are used to store anything
// within the merkle trie. States take care of caching and storing
// nested states. It's the general query interface to retrieve:
@ -14,7 +18,7 @@ import (
// * Accounts
type State struct {
// The trie for this structure
trie *ethtrie.Trie
Trie *ethtrie.Trie
stateObjects map[string]*StateObject
@ -23,14 +27,14 @@ type State struct {
// Create a new state from a given trie
func NewState(trie *ethtrie.Trie) *State {
return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
return &State{Trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
}
// Retrieve the balance from the given address or 0 if object not found
func (self *State) GetBalance(addr []byte) *big.Int {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.Amount
return stateObject.Balance
}
return ethutil.Big0
@ -53,16 +57,16 @@ func (self *State) GetNonce(addr []byte) uint64 {
func (self *State) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address()
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Script()), stateObject.Script())
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Code), stateObject.Code)
self.trie.Update(string(addr), string(stateObject.RlpEncode()))
self.Trie.Update(string(addr), string(stateObject.RlpEncode()))
self.manifest.AddObjectChange(stateObject)
}
// Delete the given state object and delete it from the state trie
func (self *State) DeleteStateObject(stateObject *StateObject) {
self.trie.Delete(string(stateObject.Address()))
self.Trie.Delete(string(stateObject.Address()))
delete(self.stateObjects, string(stateObject.Address()))
}
@ -76,7 +80,7 @@ func (self *State) GetStateObject(addr []byte) *StateObject {
return stateObject
}
data := self.trie.Get(string(addr))
data := self.Trie.Get(string(addr))
if len(data) == 0 {
return nil
}
@ -99,6 +103,8 @@ func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
// Create a state object whether it exist in the trie or not
func (self *State) NewStateObject(addr []byte) *StateObject {
addr = ethutil.Address(addr)
statelogger.Infof("(+) %x\n", addr)
stateObject := NewStateObject(addr)
@ -117,12 +123,12 @@ func (self *State) GetAccount(addr []byte) *StateObject {
//
func (s *State) Cmp(other *State) bool {
return s.trie.Cmp(other.trie)
return s.Trie.Cmp(other.Trie)
}
func (self *State) Copy() *State {
if self.trie != nil {
state := NewState(self.trie.Copy())
if self.Trie != nil {
state := NewState(self.Trie.Copy())
for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy()
}
@ -138,21 +144,21 @@ func (self *State) Set(state *State) {
panic("Tried setting 'state' to nil through 'Set'")
}
self.trie = state.trie
self.Trie = state.Trie
self.stateObjects = state.stateObjects
}
func (s *State) Root() interface{} {
return s.trie.Root
return s.Trie.Root
}
// Resets the trie and all siblings
func (s *State) Reset() {
s.trie.Undo()
s.Trie.Undo()
// Reset all nested states
for _, stateObject := range s.stateObjects {
if stateObject.state == nil {
if stateObject.State == nil {
continue
}
@ -169,14 +175,14 @@ func (s *State) Sync() {
for _, stateObject := range s.stateObjects {
//s.UpdateStateObject(stateObject)
if stateObject.state == nil {
if stateObject.State == nil {
continue
}
stateObject.state.Sync()
stateObject.State.Sync()
}
s.trie.Sync()
s.Trie.Sync()
s.Empty()
}
@ -197,11 +203,11 @@ func (self *State) Update() {
}
// FIXME trie delete is broken
valid, t2 := ethtrie.ParanoiaCheck(self.trie)
valid, t2 := ethtrie.ParanoiaCheck(self.Trie)
if !valid {
statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root, t2.Root)
statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.Trie.Root, t2.Root)
self.trie = t2
self.Trie = t2
}
}
@ -212,6 +218,10 @@ func (self *State) CreateOutputForDiff() {
}
}
func (self *State) Manifest() *Manifest {
return self.manifest
}
// Object manifest
//
// The object manifest is used to keep changes to the state so we can keep track of the changes
@ -221,8 +231,8 @@ type Manifest struct {
objectAddresses map[string]bool
storageAddresses map[string]map[string]bool
objectChanges map[string]*StateObject
storageChanges map[string]map[string]*big.Int
ObjectChanges map[string]*StateObject
StorageChanges map[string]map[string]*big.Int
}
func NewManifest() *Manifest {
@ -233,18 +243,18 @@ func NewManifest() *Manifest {
}
func (m *Manifest) Reset() {
m.objectChanges = make(map[string]*StateObject)
m.storageChanges = make(map[string]map[string]*big.Int)
m.ObjectChanges = make(map[string]*StateObject)
m.StorageChanges = make(map[string]map[string]*big.Int)
}
func (m *Manifest) AddObjectChange(stateObject *StateObject) {
m.objectChanges[string(stateObject.Address())] = stateObject
m.ObjectChanges[string(stateObject.Address())] = stateObject
}
func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) {
if m.storageChanges[string(stateObject.Address())] == nil {
m.storageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
if m.StorageChanges[string(stateObject.Address())] == nil {
m.StorageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
}
m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage
m.StorageChanges[string(stateObject.Address())][string(storageAddr)] = storage
}

View File

@ -1,18 +1,18 @@
package ethchain
package ethstate
import (
"fmt"
"math/big"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"strings"
)
type Code []byte
func (self Code) String() string {
return strings.Join(Disassemble(self), " ")
return string(self) //strings.Join(Disassemble(self), " ")
}
type Storage map[string]*ethutil.Value
@ -31,13 +31,13 @@ type StateObject struct {
// Address of the object
address []byte
// Shared attributes
Amount *big.Int
ScriptHash []byte
Balance *big.Int
CodeHash []byte
Nonce uint64
// Contract related attributes
state *State
script Code
initScript Code
State *State
Code Code
InitCode Code
storage Storage
@ -54,9 +54,10 @@ type StateObject struct {
func (self *StateObject) Reset() {
self.storage = make(Storage)
self.state.Reset()
self.State.Reset()
}
/*
// Converts an transaction in to a state object
func MakeContract(tx *Transaction, state *State) *StateObject {
// Create contract if there's no recipient
@ -64,7 +65,7 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
addr := tx.CreationAddress()
contract := state.NewStateObject(addr)
contract.initScript = tx.Data
contract.initCode = tx.Data
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
return contract
@ -72,23 +73,24 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
return nil
}
*/
func NewStateObject(addr []byte) *StateObject {
// This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter.
address := ethutil.Address(addr)
object := &StateObject{address: address, Amount: new(big.Int), gasPool: new(big.Int)}
object.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
object := &StateObject{address: address, Balance: new(big.Int), gasPool: new(big.Int)}
object.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
object.storage = make(Storage)
object.gasPool = new(big.Int)
return object
}
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
func NewContract(address []byte, balance *big.Int, root []byte) *StateObject {
contract := NewStateObject(address)
contract.Amount = Amount
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
contract.Balance = balance
contract.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
return contract
}
@ -102,15 +104,15 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject {
func (self *StateObject) MarkForDeletion() {
self.remove = true
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Amount)
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Balance)
}
func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr))))
return ethutil.NewValueFromBytes([]byte(c.State.Trie.Get(string(addr))))
}
func (c *StateObject) SetAddr(addr []byte, value interface{}) {
c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
c.State.Trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
}
func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value {
@ -140,15 +142,6 @@ func (self *StateObject) getStorage(k []byte) *ethutil.Value {
func (self *StateObject) setStorage(k []byte, value *ethutil.Value) {
key := ethutil.LeftPadBytes(k, 32)
self.storage[string(key)] = value.Copy()
/*
if value.BigInt().Cmp(ethutil.Big0) == 0 {
self.state.trie.Delete(string(key))
return
}
self.SetAddr(key, value)
*/
}
// Iterate over each storage address and yield callback
@ -160,7 +153,7 @@ func (self *StateObject) EachStorage(cb ethtrie.EachCallback) {
cb(key, encoded)
}
it := self.state.trie.NewIterator()
it := self.State.Trie.NewIterator()
it.Each(func(key string, value *ethutil.Value) {
// If it's cached don't call the callback.
if self.storage[key] == nil {
@ -170,63 +163,47 @@ func (self *StateObject) EachStorage(cb ethtrie.EachCallback) {
}
func (self *StateObject) Sync() {
/*
fmt.Println("############# BEFORE ################")
self.state.EachStorage(func(key string, value *ethutil.Value) {
fmt.Printf("%x %x %x\n", self.Address(), []byte(key), value.Bytes())
})
fmt.Printf("%x @:%x\n", self.Address(), self.state.Root())
fmt.Println("#####################################")
*/
for key, value := range self.storage {
if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 {
//data := self.getStorage([]byte(key))
//fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data)
self.state.trie.Delete(string(key))
self.State.Trie.Delete(string(key))
continue
}
self.SetAddr([]byte(key), value)
}
valid, t2 := ethtrie.ParanoiaCheck(self.state.trie)
valid, t2 := ethtrie.ParanoiaCheck(self.State.Trie)
if !valid {
statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.state.trie.Root, t2.Root)
statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.State.Trie.Root, t2.Root)
self.state.trie = t2
self.State.Trie = t2
}
/*
fmt.Println("############# AFTER ################")
self.state.EachStorage(func(key string, value *ethutil.Value) {
fmt.Printf("%x %x %x\n", self.Address(), []byte(key), value.Bytes())
})
*/
//fmt.Printf("%x @:%x\n", self.Address(), self.state.Root())
}
func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
if int64(len(c.script)-1) < pc.Int64() {
if int64(len(c.Code)-1) < pc.Int64() {
return ethutil.NewValue(0)
}
return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]})
return ethutil.NewValueFromBytes([]byte{c.Code[pc.Int64()]})
}
func (c *StateObject) AddAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Add(c.Amount, amount))
c.SetBalance(new(big.Int).Add(c.Balance, amount))
statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount)
statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Balance, amount)
}
func (c *StateObject) SubAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Sub(c.Amount, amount))
c.SetBalance(new(big.Int).Sub(c.Balance, amount))
statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount)
statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Balance, amount)
}
func (c *StateObject) SetAmount(amount *big.Int) {
c.Amount = amount
func (c *StateObject) SetBalance(amount *big.Int) {
c.Balance = amount
}
//
@ -234,11 +211,11 @@ func (c *StateObject) SetAmount(amount *big.Int) {
//
// Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {}
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
func (c *StateObject) ConvertGas(gas, price *big.Int) error {
total := new(big.Int).Mul(gas, price)
if total.Cmp(c.Amount) > 0 {
return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total)
if total.Cmp(c.Balance) > 0 {
return fmt.Errorf("insufficient amount: %v, %v", c.Balance, total)
}
c.SubAmount(total)
@ -271,19 +248,19 @@ func (self *StateObject) RefundGas(gas, price *big.Int) {
rGas := new(big.Int).Set(gas)
rGas.Mul(rGas, price)
self.Amount.Sub(self.Amount, rGas)
self.Balance.Sub(self.Balance, rGas)
}
func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address())
stateObject.Amount.Set(self.Amount)
stateObject.ScriptHash = ethutil.CopyBytes(self.ScriptHash)
stateObject.Balance.Set(self.Balance)
stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash)
stateObject.Nonce = self.Nonce
if self.state != nil {
stateObject.state = self.state.Copy()
if self.State != nil {
stateObject.State = self.State.Copy()
}
stateObject.script = ethutil.CopyBytes(self.script)
stateObject.initScript = ethutil.CopyBytes(self.initScript)
stateObject.Code = ethutil.CopyBytes(self.Code)
stateObject.InitCode = ethutil.CopyBytes(self.InitCode)
stateObject.storage = self.storage.Copy()
stateObject.gasPool.Set(self.gasPool)
@ -298,10 +275,6 @@ func (self *StateObject) Set(stateObject *StateObject) {
// Attribute accessors
//
func (c *StateObject) State() *State {
return c.state
}
func (c *StateObject) N() *big.Int {
return big.NewInt(int64(c.Nonce))
}
@ -311,19 +284,14 @@ func (c *StateObject) Address() []byte {
return c.address
}
// Returns the main script body
func (c *StateObject) Script() Code {
return c.script
}
// Returns the initialization script
// Returns the initialization Code
func (c *StateObject) Init() Code {
return c.initScript
return c.InitCode
}
// Debug stuff
func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.state.Root(), self.Amount.Bytes(), self.Nonce)
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Balance.Bytes(), self.Nonce)
self.EachStorage(func(addr string, value *ethutil.Value) {
fmt.Printf("%x %x\n", addr, value.Bytes())
})
@ -336,27 +304,27 @@ func (self *StateObject) CreateOutputForDiff() {
// State object encoding methods
func (c *StateObject) RlpEncode() []byte {
var root interface{}
if c.state != nil {
root = c.state.trie.Root
if c.State != nil {
root = c.State.Trie.Root
} else {
root = ""
}
return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethcrypto.Sha3Bin(c.script)})
return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, ethcrypto.Sha3Bin(c.Code)})
}
func (c *StateObject) RlpDecode(data []byte) {
decoder := ethutil.NewValueFromBytes(data)
c.Nonce = decoder.Get(0).Uint()
c.Amount = decoder.Get(1).BigInt()
c.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
c.Balance = decoder.Get(1).BigInt()
c.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
c.storage = make(map[string]*ethutil.Value)
c.gasPool = new(big.Int)
c.ScriptHash = decoder.Get(3).Bytes()
c.CodeHash = decoder.Get(3).Bytes()
c.script, _ = ethutil.Config.Db.Get(c.ScriptHash)
c.Code, _ = ethutil.Config.Db.Get(c.CodeHash)
}
// Storage change object. Used by the manifest for notifying changes to

View File

@ -1,19 +1,23 @@
package ethchain
package ethstate
import (
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
"testing"
)
var ZeroHash256 = make([]byte, 32)
func TestSnapshot(t *testing.T) {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
db, _ := ethdb.NewMemDatabase()
state := NewState(ethutil.NewTrie(db, ""))
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db = db
state := NewState(ethtrie.NewTrie(db, ""))
stateObject := state.GetOrNewStateObject([]byte("aa"))
stateObject := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
state.UpdateStateObject(stateObject)
stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42))
snapshot := state.Copy()
@ -24,7 +28,8 @@ func TestSnapshot(t *testing.T) {
state.Set(snapshot)
stateObject = state.GetStateObject([]byte("aa"))
if !stateObject.GetStorage(ethutil.Big("0")).Cmp(ethutil.NewValue(42)) {
t.Error("Expected storage 0 to be 42")
res := stateObject.GetStorage(ethutil.Big("0"))
if !res.Cmp(ethutil.NewValue(42)) {
t.Error("Expected storage 0 to be 42", res)
}
}

View File

@ -4,14 +4,6 @@ import (
"math/big"
)
var BigInt0 *big.Int = big.NewInt(0)
// True
var BigTrue *big.Int = big.NewInt(1)
// False
var BigFalse *big.Int = big.NewInt(0)
// Big pow
//
// Returns the power of two big integers
@ -73,3 +65,14 @@ func BigMax(x, y *big.Int) *big.Int {
return x
}
// Big min
//
// Returns the minimum size big integer
func BigMin(x, y *big.Int) *big.Int {
if x.Cmp(y) >= 0 {
return y
}
return x
}

View File

@ -44,26 +44,28 @@ func BytesToNumber(b []byte) uint64 {
// Read variable int
//
// Read a variable length number in big endian byte order
func ReadVarint(reader *bytes.Reader) (ret uint64) {
if reader.Len() == 8 {
var num uint64
binary.Read(reader, binary.BigEndian, &num)
ret = uint64(num)
} else if reader.Len() == 4 {
func ReadVarInt(buff []byte) (ret uint64) {
switch l := len(buff); {
case l > 4:
d := LeftPadBytes(buff, 8)
binary.Read(bytes.NewReader(d), binary.BigEndian, &ret)
case l > 2:
var num uint32
binary.Read(reader, binary.BigEndian, &num)
d := LeftPadBytes(buff, 4)
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
ret = uint64(num)
} else if reader.Len() == 2 {
case l > 1:
var num uint16
binary.Read(reader, binary.BigEndian, &num)
d := LeftPadBytes(buff, 2)
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
ret = uint64(num)
} else {
default:
var num uint8
binary.Read(reader, binary.BigEndian, &num)
binary.Read(bytes.NewReader(buff), binary.BigEndian, &num)
ret = uint64(num)
}
return ret
return
}
// Binary length
@ -98,6 +100,7 @@ func Bytes2Hex(d []byte) string {
func Hex2Bytes(str string) []byte {
h, _ := hex.DecodeString(str)
return h
}

View File

@ -61,6 +61,8 @@ var (
Big1 = big.NewInt(1)
Big2 = big.NewInt(2)
Big0 = big.NewInt(0)
BigTrue = Big1
BigFalse = Big0
Big32 = big.NewInt(32)
Big256 = big.NewInt(0xff)
)

View File

@ -3,8 +3,9 @@ package ethutil
import (
"flag"
"fmt"
"github.com/rakyll/globalconf"
"os"
"github.com/rakyll/globalconf"
)
// Config struct
@ -28,8 +29,7 @@ var Config *ConfigManager
func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager {
if Config == nil {
// create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags
_, err := os.Stat(ConfigFile)
if err != nil && os.IsNotExist(err) {
if !FileExist(ConfigFile) {
fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile)
os.Create(ConfigFile)
}

View File

@ -1,6 +1,8 @@
package ethutil
import (
"io/ioutil"
"os"
"os/user"
"strings"
)
@ -18,3 +20,41 @@ func ExpandHomePath(p string) (path string) {
return
}
func FileExist(filePath string) bool {
_, err := os.Stat(filePath)
if err != nil && os.IsNotExist(err) {
return false
}
return true
}
func ReadAllFile(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
data, err := ioutil.ReadAll(file)
if err != nil {
return "", err
}
return string(data), nil
}
func WriteFile(filePath string, content []byte) error {
fh, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()
_, err = fh.Write(content)
if err != nil {
return err
}
return nil
}

View File

@ -55,8 +55,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
return reader.Next(int(char - 0x80))
case char <= 0xbf:
buff := bytes.NewReader(reader.Next(int(char - 0xb8)))
length := ReadVarint(buff)
length := ReadVarInt(reader.Next(int(char - 0xb7)))
return reader.Next(int(length))
@ -72,76 +71,22 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
}
return slice
case char <= 0xff:
length := ReadVarInt(reader.Next(int(char - 0xf7)))
for i := uint64(0); i < length; i++ {
obj := DecodeWithReader(reader)
if obj != nil {
slice = append(slice, obj)
} else {
break
}
}
default:
}
return slice
}
// TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) {
var slice []interface{}
char := int(data[pos])
switch {
case char <= 0x7f:
return data[pos], pos + 1
case char <= 0xb7:
b := uint64(data[pos]) - 0x80
return data[pos+1 : pos+1+b], pos + 1 + b
case char <= 0xbf:
b := uint64(data[pos]) - 0xb7
b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b]))
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
case char <= 0xf7:
b := uint64(data[pos]) - 0xc0
prevPos := pos
pos++
for i := uint64(0); i < b; {
var obj interface{}
// Get the next item in the data list and append it
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
// Increment i by the amount bytes read in the previous
// read
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
case char <= 0xff:
l := uint64(data[pos]) - 0xf7
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
pos = pos + l + 1
prevPos := b
for i := uint64(0); i < uint64(b); {
var obj interface{}
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
default:
panic(fmt.Sprintf("byte not supported: %q", char))
}
return slice, 0
}
var (
directRlp = big.NewInt(0x7f)
numberRlp = big.NewInt(0xb7)
@ -223,3 +168,67 @@ func Encode(object interface{}) []byte {
return buff.Bytes()
}
// TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) {
var slice []interface{}
char := int(data[pos])
switch {
case char <= 0x7f:
return data[pos], pos + 1
case char <= 0xb7:
b := uint64(data[pos]) - 0x80
return data[pos+1 : pos+1+b], pos + 1 + b
case char <= 0xbf:
b := uint64(data[pos]) - 0xb7
b2 := ReadVarInt(data[pos+1 : pos+1+b])
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
case char <= 0xf7:
b := uint64(data[pos]) - 0xc0
prevPos := pos
pos++
for i := uint64(0); i < b; {
var obj interface{}
// Get the next item in the data list and append it
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
// Increment i by the amount bytes read in the previous
// read
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
case char <= 0xff:
l := uint64(data[pos]) - 0xf7
b := ReadVarInt(data[pos+1 : pos+1+l])
pos = pos + l + 1
prevPos := b
for i := uint64(0); i < uint64(b); {
var obj interface{}
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
default:
panic(fmt.Sprintf("byte not supported: %q", char))
}
return slice, 0
}

View File

@ -44,6 +44,17 @@ func TestValueSlice(t *testing.T) {
}
}
func TestLargeData(t *testing.T) {
data := make([]byte, 100000)
enc := Encode(data)
value := NewValue(enc)
value.Decode()
if value.Len() != len(data) {
t.Error("Expected data to be", len(data), "got", value.Len())
}
}
func TestValue(t *testing.T) {
value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01"))
if value.Get(0).Str() != "dog" {

View File

@ -1,7 +1,6 @@
package ethutil
import (
"bytes"
"fmt"
"math/big"
"reflect"
@ -67,7 +66,9 @@ func (val *Value) Uint() uint64 {
} else if Val, ok := val.Val.(uint); ok {
return uint64(Val)
} else if Val, ok := val.Val.([]byte); ok {
return ReadVarint(bytes.NewReader(Val))
return new(big.Int).SetBytes(Val).Uint64()
} else if Val, ok := val.Val.(*big.Int); ok {
return Val.Uint64()
}
return 0
@ -205,6 +206,13 @@ func (val *Value) Cmp(o *Value) bool {
return reflect.DeepEqual(val.Val, o.Val)
}
func (self *Value) DeepCmp(o *Value) bool {
a := NewValue(self.BigInt())
b := NewValue(o.BigInt())
return a.Cmp(b)
}
func (val *Value) Encode() []byte {
return Encode(val.Val)
}
@ -260,6 +268,55 @@ func (val *Value) Append(v interface{}) *Value {
return val
}
const (
valOpAdd = iota
valOpDiv
valOpMul
valOpPow
valOpSub
)
// Math stuff
func (self *Value) doOp(op int, other interface{}) *Value {
left := self.BigInt()
right := NewValue(other).BigInt()
switch op {
case valOpAdd:
self.Val = left.Add(left, right)
case valOpDiv:
self.Val = left.Div(left, right)
case valOpMul:
self.Val = left.Mul(left, right)
case valOpPow:
self.Val = left.Exp(left, right, Big0)
case valOpSub:
self.Val = left.Sub(left, right)
}
return self
}
func (self *Value) Add(other interface{}) *Value {
return self.doOp(valOpAdd, other)
}
func (self *Value) Sub(other interface{}) *Value {
return self.doOp(valOpSub, other)
}
func (self *Value) Div(other interface{}) *Value {
return self.doOp(valOpDiv, other)
}
func (self *Value) Mul(other interface{}) *Value {
return self.doOp(valOpMul, other)
}
func (self *Value) Pow(other interface{}) *Value {
return self.doOp(valOpPow, other)
}
type ValueIterator struct {
value *Value
currentValue *Value

View File

@ -63,3 +63,18 @@ func TestIterator(t *testing.T) {
i++
}
}
func TestMath(t *testing.T) {
a := NewValue(1)
a.Add(1).Add(1)
if !a.DeepCmp(NewValue(3)) {
t.Error("Expected 3, got", a)
}
a = NewValue(2)
a.Sub(1).Sub(1)
if !a.DeepCmp(NewValue(0)) {
t.Error("Expected 0, got", a)
}
}

0
ethvm/.ethtest Normal file
View File

44
ethvm/asm.go Normal file
View File

@ -0,0 +1,44 @@
package ethvm
import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
func Disassemble(script []byte) (asm []string) {
pc := new(big.Int)
for {
if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 {
return
}
// Get the memory location of pc
val := script[pc.Int64()]
// Get the opcode (it must be an opcode!)
op := OpCode(val)
asm = append(asm, fmt.Sprintf("%v", op))
switch op {
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
pc.Add(pc, ethutil.Big1)
a := int64(op) - int64(PUSH1) + 1
if int(pc.Int64()+a) > len(script) {
return nil
}
data := script[pc.Int64() : pc.Int64()+a]
if len(data) == 0 {
data = []byte{0}
}
asm = append(asm, fmt.Sprintf("0x%x", data))
pc.Add(pc, big.NewInt(a-1))
}
pc.Add(pc, ethutil.Big1)
}
return
}

View File

@ -1,26 +1,25 @@
package ethchain
package ethvm
// TODO Re write VM to use values instead of big integers?
import (
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
type ClosureRef interface {
ReturnGas(*big.Int, *big.Int, *State)
ReturnGas(*big.Int, *big.Int)
Address() []byte
GetStorage(*big.Int) *ethutil.Value
SetStorage(*big.Int, *ethutil.Value)
N() *big.Int
}
// Basic inline closure object which implement the 'closure' interface
type Closure struct {
caller ClosureRef
object *StateObject
Script []byte
State *State
object *ethstate.StateObject
Code []byte
Gas, UsedGas, Price *big.Int
@ -28,8 +27,8 @@ type Closure struct {
}
// Create a new closure for the given data items
func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure {
c := &Closure{caller: caller, object: object, Script: script, State: state, Args: nil}
func NewClosure(caller ClosureRef, object *ethstate.StateObject, code []byte, gas, price *big.Int) *Closure {
c := &Closure{caller: caller, object: object, Code: code, Args: nil}
// Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition
@ -57,11 +56,11 @@ func (c *Closure) Get(x *big.Int) *ethutil.Value {
}
func (c *Closure) Gets(x, y *big.Int) *ethutil.Value {
if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) {
if x.Int64() >= int64(len(c.Code)) || y.Int64() >= int64(len(c.Code)) {
return ethutil.NewValue(0)
}
partial := c.Script[x.Int64() : x.Int64()+y.Int64()]
partial := c.Code[x.Int64() : x.Int64()+y.Int64()]
return ethutil.NewValue(partial)
}
@ -84,7 +83,7 @@ func (c *Closure) Call(vm *Vm, args []byte) ([]byte, *big.Int, error) {
func (c *Closure) Return(ret []byte) []byte {
// Return the remaining gas to the caller
c.caller.ReturnGas(c.Gas, c.Price, c.State)
c.caller.ReturnGas(c.Gas, c.Price)
return ret
}
@ -102,20 +101,16 @@ func (c *Closure) UseGas(gas *big.Int) bool {
}
// Implement the caller interface
func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
func (c *Closure) ReturnGas(gas, price *big.Int) {
// Return the gas to the closure
c.Gas.Add(c.Gas, gas)
c.UsedGas.Sub(c.UsedGas, gas)
}
func (c *Closure) Object() *StateObject {
func (c *Closure) Object() *ethstate.StateObject {
return c.object
}
func (c *Closure) Caller() ClosureRef {
return c.caller
}
func (c *Closure) N() *big.Int {
return c.object.N()
}

27
ethvm/common.go Normal file
View File

@ -0,0 +1,27 @@
package ethvm
import (
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
var vmlogger = ethlog.NewLogger("VM")
var (
GasStep = big.NewInt(1)
GasSha = big.NewInt(20)
GasSLoad = big.NewInt(20)
GasSStore = big.NewInt(100)
GasBalance = big.NewInt(20)
GasCreate = big.NewInt(100)
GasCall = big.NewInt(20)
GasMemory = big.NewInt(1)
GasData = big.NewInt(5)
GasTx = big.NewInt(500)
Pow256 = ethutil.BigPow(2, 256)
LogTyPretty byte = 0x1
LogTyDiff byte = 0x2
)

View File

@ -1,4 +1,4 @@
package ethchain
package ethvm
import (
"fmt"

346
ethvm/types.go Normal file
View File

@ -0,0 +1,346 @@
package ethvm
import (
"fmt"
)
type OpCode int
// Op codes
const (
// 0x0 range - arithmetic ops
STOP = 0x00
ADD = 0x01
MUL = 0x02
SUB = 0x03
DIV = 0x04
SDIV = 0x05
MOD = 0x06
SMOD = 0x07
EXP = 0x08
NEG = 0x09
LT = 0x0a
GT = 0x0b
SLT = 0x0c
SGT = 0x0d
EQ = 0x0e
NOT = 0x0f
// 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
CALLDATALOAD = 0x35
CALLDATASIZE = 0x36
CALLDATACOPY = 0x37
CODESIZE = 0x38
CODECOPY = 0x39
GASPRICE = 0x3a
// 0x40 range - block operations
PREVHASH = 0x40
COINBASE = 0x41
TIMESTAMP = 0x42
NUMBER = 0x43
DIFFICULTY = 0x44
GASLIMIT = 0x45
// 0x50 range - 'storage' and execution
POP = 0x50
DUP = 0x51
SWAP = 0x52
MLOAD = 0x53
MSTORE = 0x54
MSTORE8 = 0x55
SLOAD = 0x56
SSTORE = 0x57
JUMP = 0x58
JUMPI = 0x59
PC = 0x5a
MSIZE = 0x5b
GAS = 0x5c
// 0x60 range
PUSH1 = 0x60
PUSH2 = 0x61
PUSH3 = 0x62
PUSH4 = 0x63
PUSH5 = 0x64
PUSH6 = 0x65
PUSH7 = 0x66
PUSH8 = 0x67
PUSH9 = 0x68
PUSH10 = 0x69
PUSH11 = 0x6a
PUSH12 = 0x6b
PUSH13 = 0x6c
PUSH14 = 0x6d
PUSH15 = 0x6e
PUSH16 = 0x6f
PUSH17 = 0x70
PUSH18 = 0x71
PUSH19 = 0x72
PUSH20 = 0x73
PUSH21 = 0x74
PUSH22 = 0x75
PUSH23 = 0x76
PUSH24 = 0x77
PUSH25 = 0x78
PUSH26 = 0x79
PUSH27 = 0x7a
PUSH28 = 0x7b
PUSH29 = 0x7c
PUSH30 = 0x7d
PUSH31 = 0x7e
PUSH32 = 0x7f
// 0xf0 range - closures
CREATE = 0xf0
CALL = 0xf1
RETURN = 0xf2
// 0x70 range - other
LOG = 0xfe // XXX Unofficial
SUICIDE = 0xff
)
// Since the opcodes aren't all in order we can't use a regular slice
var opCodeToString = map[OpCode]string{
// 0x0 range - arithmetic ops
STOP: "STOP",
ADD: "ADD",
MUL: "MUL",
SUB: "SUB",
DIV: "DIV",
SDIV: "SDIV",
MOD: "MOD",
SMOD: "SMOD",
EXP: "EXP",
NEG: "NEG",
LT: "LT",
GT: "GT",
SLT: "SLT",
SGT: "SGT",
EQ: "EQ",
NOT: "NOT",
// 0x10 range - bit ops
AND: "AND",
OR: "OR",
XOR: "XOR",
BYTE: "BYTE",
// 0x20 range - crypto
SHA3: "SHA3",
// 0x30 range - closure state
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
CALLER: "CALLER",
CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE: "TXGASPRICE",
// 0x40 range - block operations
PREVHASH: "PREVHASH",
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT",
// 0x50 range - 'storage' and execution
POP: "POP",
DUP: "DUP",
SWAP: "SWAP",
MLOAD: "MLOAD",
MSTORE: "MSTORE",
MSTORE8: "MSTORE8",
SLOAD: "SLOAD",
SSTORE: "SSTORE",
JUMP: "JUMP",
JUMPI: "JUMPI",
PC: "PC",
MSIZE: "MSIZE",
GAS: "GAS",
// 0x60 range - push
PUSH1: "PUSH1",
PUSH2: "PUSH2",
PUSH3: "PUSH3",
PUSH4: "PUSH4",
PUSH5: "PUSH5",
PUSH6: "PUSH6",
PUSH7: "PUSH7",
PUSH8: "PUSH8",
PUSH9: "PUSH9",
PUSH10: "PUSH10",
PUSH11: "PUSH11",
PUSH12: "PUSH12",
PUSH13: "PUSH13",
PUSH14: "PUSH14",
PUSH15: "PUSH15",
PUSH16: "PUSH16",
PUSH17: "PUSH17",
PUSH18: "PUSH18",
PUSH19: "PUSH19",
PUSH20: "PUSH20",
PUSH21: "PUSH21",
PUSH22: "PUSH22",
PUSH23: "PUSH23",
PUSH24: "PUSH24",
PUSH25: "PUSH25",
PUSH26: "PUSH26",
PUSH27: "PUSH27",
PUSH28: "PUSH28",
PUSH29: "PUSH29",
PUSH30: "PUSH30",
PUSH31: "PUSH31",
PUSH32: "PUSH32",
// 0xf0 range
CREATE: "CREATE",
CALL: "CALL",
RETURN: "RETURN",
// 0x70 range - other
LOG: "LOG",
SUICIDE: "SUICIDE",
}
func (o OpCode) String() string {
str := opCodeToString[o]
if len(str) == 0 {
return fmt.Sprintf("Missing opcode 0x%x", int(o))
}
return str
}
// Op codes for assembling
var OpCodes = map[string]byte{
// 0x0 range - arithmetic ops
"STOP": 0x00,
"ADD": 0x01,
"MUL": 0x02,
"SUB": 0x03,
"DIV": 0x04,
"SDIV": 0x05,
"MOD": 0x06,
"SMOD": 0x07,
"EXP": 0x08,
"NEG": 0x09,
"LT": 0x0a,
"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,
"CALLDATALOAD": 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
"POP": 0x51,
"DUP": 0x52,
"SWAP": 0x53,
"MLOAD": 0x54,
"MSTORE": 0x55,
"MSTORE8": 0x56,
"SLOAD": 0x57,
"SSTORE": 0x58,
"JUMP": 0x59,
"JUMPI": 0x5a,
"PC": 0x5b,
"MSIZE": 0x5c,
// 0x70 range - 'push'
"PUSH1": 0x60,
"PUSH2": 0x61,
"PUSH3": 0x62,
"PUSH4": 0x63,
"PUSH5": 0x64,
"PUSH6": 0x65,
"PUSH7": 0x66,
"PUSH8": 0x67,
"PUSH9": 0x68,
"PUSH10": 0x69,
"PUSH11": 0x6a,
"PUSH12": 0x6b,
"PUSH13": 0x6c,
"PUSH14": 0x6d,
"PUSH15": 0x6e,
"PUSH16": 0x6f,
"PUSH17": 0x70,
"PUSH18": 0x71,
"PUSH19": 0x72,
"PUSH20": 0x73,
"PUSH21": 0x74,
"PUSH22": 0x75,
"PUSH23": 0x76,
"PUSH24": 0x77,
"PUSH25": 0x78,
"PUSH26": 0x70,
"PUSH27": 0x7a,
"PUSH28": 0x7b,
"PUSH29": 0x7c,
"PUSH30": 0x7d,
"PUSH31": 0x7e,
"PUSH32": 0x7f,
// 0xf0 range - closures
"CREATE": 0xf0,
"CALL": 0xf1,
"RETURN": 0xf2,
// 0x70 range - other
"LOG": 0xfe,
"SUICIDE": 0x7f,
}
func IsOpCode(s string) bool {
for key, _ := range OpCodes {
if key == s {
return true
}
}
return false
}

View File

@ -1,35 +1,18 @@
package ethchain
package ethvm
import (
"fmt"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"math"
"math/big"
)
var vmlogger = ethlog.NewLogger("VM")
var (
GasStep = big.NewInt(1)
GasSha = big.NewInt(20)
GasSLoad = big.NewInt(20)
GasSStore = big.NewInt(100)
GasBalance = big.NewInt(20)
GasCreate = big.NewInt(100)
GasCall = big.NewInt(20)
GasMemory = big.NewInt(1)
GasData = big.NewInt(5)
GasTx = big.NewInt(500)
LogTyPretty byte = 0x1
LogTyDiff byte = 0x2
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
)
type Debugger interface {
BreakHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
StepHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
BreakHook(step int, op OpCode, mem *Memory, stack *Stack, object *ethstate.StateObject) bool
StepHook(step int, op OpCode, mem *Memory, stack *Stack, object *ethstate.StateObject) bool
BreakPoints() []int64
SetCode(byteCode []byte)
}
@ -40,11 +23,7 @@ type Vm struct {
// non-persistent key/value memory storage
mem map[string]*big.Int
vars RuntimeVars
state *State
stateManager *StateManager
env Environment
Verbose bool
@ -59,51 +38,38 @@ type Vm struct {
BreakPoints []int64
Stepping bool
Fn string
Recoverable bool
}
type RuntimeVars struct {
Origin []byte
Block *Block
BlockNumber *big.Int
PrevHash []byte
Coinbase []byte
Time int64
Diff *big.Int
TxData []string
Value *big.Int
type Environment interface {
State() *ethstate.State
Origin() []byte
BlockNumber() *big.Int
PrevHash() []byte
Coinbase() []byte
Time() int64
Difficulty() *big.Int
Value() *big.Int
}
func (self *Vm) Printf(format string, v ...interface{}) *Vm {
if self.Verbose && self.logTy == LogTyPretty {
self.logStr += fmt.Sprintf(format, v...)
type Object interface {
GetStorage(key *big.Int) *ethutil.Value
SetStorage(key *big.Int, value *ethutil.Value)
}
return self
}
func (self *Vm) Endl() *Vm {
if self.Verbose && self.logTy == LogTyPretty {
vmlogger.Debugln(self.logStr)
self.logStr = ""
}
return self
}
func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
func New(env Environment) *Vm {
lt := LogTyPretty
if ethutil.Config.Diff {
lt = LogTyDiff
}
return &Vm{vars: vars, state: state, stateManager: stateManager, logTy: lt}
return &Vm{env: env, logTy: lt, Recoverable: true}
}
var Pow256 = ethutil.BigPow(2, 256)
var isRequireError = false
func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
if self.Recoverable {
// Recover from any require exception
defer func() {
if r := recover(); r != nil {
@ -112,18 +78,19 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
vmlogger.Errorln("vm err", err)
}
}()
}
// Debug hook
if vm.Dbg != nil {
vm.Dbg.SetCode(closure.Script)
if self.Dbg != nil {
self.Dbg.SetCode(closure.Code)
}
// Don't bother with the execution if there's no code.
if len(closure.Script) == 0 {
if len(closure.Code) == 0 {
return closure.Return(nil), nil
}
vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", vm.Fn, closure.Address(), closure.Gas, closure.Args)
vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", self.Fn, closure.Address(), closure.Gas, closure.Args)
var (
op OpCode
@ -135,7 +102,6 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
prevStep = 0
require = func(m int) {
if stack.Len() < m {
isRequireError = true
panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m))
}
}
@ -154,10 +120,10 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// XXX Leave this Println intact. Don't change this to the log system.
// Used for creating diffs between implementations
if vm.logTy == LogTyDiff {
if self.logTy == LogTyDiff {
/*
switch op {
case STOP, RETURN, SUICIDE:
closure.object.Sync()
closure.object.EachStorage(func(key string, value *ethutil.Value) {
value.Decode()
fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
@ -170,6 +136,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
}
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
*/
}
gas := new(big.Int)
@ -261,8 +228,8 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
return closure.Return(nil), err
}
vm.Printf("(pc) %-3d -o- %-14s", pc, op.String())
vm.Printf(" (g) %-3v (%v)", gas, closure.Gas)
self.Printf("(pc) %-3d -o- %-14s", pc, op.String())
self.Printf(" (g) %-3v (%v)", gas, closure.Gas)
mem.Resize(newMemSize)
@ -274,96 +241,87 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case ADD:
require(2)
x, y := stack.Popn()
vm.Printf(" %v + %v", y, x)
self.Printf(" %v + %v", y, x)
base.Add(y, x)
vm.Printf(" = %v", base)
self.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case SUB:
require(2)
x, y := stack.Popn()
vm.Printf(" %v - %v", y, x)
self.Printf(" %v - %v", y, x)
base.Sub(y, x)
vm.Printf(" = %v", base)
self.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case MUL:
require(2)
x, y := stack.Popn()
vm.Printf(" %v * %v", y, x)
self.Printf(" %v * %v", y, x)
base.Mul(y, x)
vm.Printf(" = %v", base)
self.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case DIV:
require(2)
x, y := stack.Popn()
vm.Printf(" %v / %v", y, x)
self.Printf(" %v / %v", y, x)
if x.Cmp(ethutil.Big0) != 0 {
base.Div(y, x)
}
vm.Printf(" = %v", base)
self.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case SDIV:
require(2)
x, y := stack.Popn()
// n > 2**255
if x.Cmp(Pow256) > 0 {
x.Sub(Pow256, x)
self.Printf(" %v / %v", y, x)
if x.Cmp(ethutil.Big0) != 0 {
base.Div(y, x)
}
if y.Cmp(Pow256) > 0 {
y.Sub(Pow256, y)
}
z := new(big.Int)
z.Div(x, y)
if z.Cmp(Pow256) > 0 {
z.Sub(Pow256, z)
}
// Push result on to the stack
stack.Push(z)
self.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case MOD:
require(2)
x, y := stack.Popn()
vm.Printf(" %v %% %v", y, x)
self.Printf(" %v %% %v", y, x)
base.Mod(y, x)
vm.Printf(" = %v", base)
self.Printf(" = %v", base)
stack.Push(base)
case SMOD:
require(2)
x, y := stack.Popn()
// n > 2**255
if x.Cmp(Pow256) > 0 {
x.Sub(Pow256, x)
}
if y.Cmp(Pow256) > 0 {
y.Sub(Pow256, y)
}
z := new(big.Int)
z.Mod(x, y)
if z.Cmp(Pow256) > 0 {
z.Sub(Pow256, z)
}
// Push result on to the stack
stack.Push(z)
self.Printf(" %v %% %v", y, x)
base.Mod(y, x)
self.Printf(" = %v", base)
stack.Push(base)
case EXP:
require(2)
x, y := stack.Popn()
vm.Printf(" %v ** %v", y, x)
self.Printf(" %v ** %v", y, x)
base.Exp(y, x, Pow256)
vm.Printf(" = %v", base)
self.Printf(" = %v", base)
stack.Push(base)
case NEG:
@ -373,7 +331,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case LT:
require(2)
x, y := stack.Popn()
vm.Printf(" %v < %v", y, x)
self.Printf(" %v < %v", y, x)
// x < y
if y.Cmp(x) < 0 {
stack.Push(ethutil.BigTrue)
@ -383,7 +341,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case GT:
require(2)
x, y := stack.Popn()
vm.Printf(" %v > %v", y, x)
self.Printf(" %v > %v", y, x)
// x > y
if y.Cmp(x) > 0 {
@ -395,7 +353,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case SLT:
require(2)
x, y := stack.Popn()
vm.Printf(" %v < %v", y, x)
self.Printf(" %v < %v", y, x)
// x < y
if y.Cmp(x) < 0 {
stack.Push(ethutil.BigTrue)
@ -405,7 +363,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case SGT:
require(2)
x, y := stack.Popn()
vm.Printf(" %v > %v", y, x)
self.Printf(" %v > %v", y, x)
// x > y
if y.Cmp(x) > 0 {
@ -417,7 +375,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case EQ:
require(2)
x, y := stack.Popn()
vm.Printf(" %v == %v", y, x)
self.Printf(" %v == %v", y, x)
// x == y
if x.Cmp(y) == 0 {
@ -438,29 +396,29 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case AND:
require(2)
x, y := stack.Popn()
vm.Printf(" %v & %v", y, x)
self.Printf(" %v & %v", y, x)
stack.Push(base.And(y, x))
case OR:
require(2)
x, y := stack.Popn()
vm.Printf(" %v | %v", y, x)
self.Printf(" %v | %v", y, x)
stack.Push(base.Or(y, x))
case XOR:
require(2)
x, y := stack.Popn()
vm.Printf(" %v ^ %v", y, x)
self.Printf(" %v ^ %v", y, x)
stack.Push(base.Xor(y, x))
case BYTE:
require(2)
val, th := stack.Popn()
if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 {
byt := big.NewInt(int64(val.Bytes()[th.Int64()]))
byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
stack.Push(byt)
vm.Printf(" => 0x%x", byt.Bytes())
self.Printf(" => 0x%x", byt.Bytes())
} else {
stack.Push(ethutil.BigFalse)
}
@ -473,53 +431,61 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
stack.Push(ethutil.BigD(data))
vm.Printf(" => %x", data)
self.Printf(" => %x", data)
// 0x30 range
case ADDRESS:
stack.Push(ethutil.BigD(closure.Address()))
vm.Printf(" => %x", closure.Address())
self.Printf(" => %x", closure.Address())
case BALANCE:
require(1)
addr := stack.Pop().Bytes()
balance := vm.state.GetBalance(addr)
balance := self.env.State().GetBalance(addr)
stack.Push(balance)
vm.Printf(" => %v (%x)", balance, addr)
self.Printf(" => %v (%x)", balance, addr)
case ORIGIN:
stack.Push(ethutil.BigD(vm.vars.Origin))
origin := self.env.Origin()
vm.Printf(" => %x", vm.vars.Origin)
stack.Push(ethutil.BigD(origin))
self.Printf(" => %x", origin)
case CALLER:
caller := closure.caller.Address()
stack.Push(ethutil.BigD(caller))
vm.Printf(" => %x", caller)
self.Printf(" => %x", caller)
case CALLVALUE:
stack.Push(vm.vars.Value)
value := self.env.Value()
vm.Printf(" => %v", vm.vars.Value)
stack.Push(value)
self.Printf(" => %v", value)
case CALLDATALOAD:
require(1)
offset := stack.Pop().Int64()
var (
offset = stack.Pop()
data = make([]byte, 32)
lenData = big.NewInt(int64(len(closure.Args)))
)
data := make([]byte, 32)
if len(closure.Args) >= int(offset) {
l := int64(math.Min(float64(offset+32), float64(len(closure.Args))))
if lenData.Cmp(offset) >= 0 {
length := new(big.Int).Add(offset, ethutil.Big32)
length = ethutil.BigMin(length, lenData)
copy(data, closure.Args[offset:l])
copy(data, closure.Args[offset.Int64():length.Int64()])
}
vm.Printf(" => 0x%x", data)
self.Printf(" => 0x%x", data)
stack.Push(ethutil.BigD(data))
case CALLDATASIZE:
l := int64(len(closure.Args))
stack.Push(big.NewInt(l))
vm.Printf(" => %d", l)
self.Printf(" => %d", l)
case CALLDATACOPY:
var (
size = int64(len(closure.Args))
@ -539,13 +505,13 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
mem.Set(mOff, l, code)
case CODESIZE:
l := big.NewInt(int64(len(closure.Script)))
l := big.NewInt(int64(len(closure.Code)))
stack.Push(l)
vm.Printf(" => %d", l)
self.Printf(" => %d", l)
case CODECOPY:
var (
size = int64(len(closure.Script))
size = int64(len(closure.Code))
mOff = stack.Pop().Int64()
cOff = stack.Pop().Int64()
l = stack.Pop().Int64()
@ -558,25 +524,45 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
l = 0
}
code := closure.Script[cOff : cOff+l]
//fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff)
code := closure.Code[cOff : cOff+l]
mem.Set(mOff, l, code)
//fmt.Println(Code(mem.Get(mOff, l)))
case GASPRICE:
stack.Push(closure.Price)
self.Printf(" => %v", closure.Price)
// 0x40 range
case PREVHASH:
stack.Push(ethutil.BigD(vm.vars.PrevHash))
prevHash := self.env.PrevHash()
stack.Push(ethutil.BigD(prevHash))
self.Printf(" => 0x%x", prevHash)
case COINBASE:
stack.Push(ethutil.BigD(vm.vars.Coinbase))
coinbase := self.env.Coinbase()
stack.Push(ethutil.BigD(coinbase))
self.Printf(" => 0x%x", coinbase)
case TIMESTAMP:
stack.Push(big.NewInt(vm.vars.Time))
time := self.env.Time()
stack.Push(big.NewInt(time))
self.Printf(" => 0x%x", time)
case NUMBER:
stack.Push(vm.vars.BlockNumber)
number := self.env.BlockNumber()
stack.Push(number)
self.Printf(" => 0x%x", number.Bytes())
case DIFFICULTY:
stack.Push(vm.vars.Diff)
difficulty := self.env.Difficulty()
stack.Push(difficulty)
self.Printf(" => 0x%x", difficulty.Bytes())
case GASLIMIT:
// TODO
stack.Push(big.NewInt(0))
@ -593,7 +579,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
step += int(op) - int(PUSH1) + 1
vm.Printf(" => 0x%x", data.Bytes())
self.Printf(" => 0x%x", data.Bytes())
case POP:
require(1)
stack.Pop()
@ -601,7 +587,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
require(1)
stack.Push(stack.Peek())
vm.Printf(" => 0x%x", stack.Peek().Bytes())
self.Printf(" => 0x%x", stack.Peek().Bytes())
case SWAP:
require(2)
x, y := stack.Popn()
@ -613,14 +599,14 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
val := ethutil.BigD(mem.Get(offset.Int64(), 32))
stack.Push(val)
vm.Printf(" => 0x%x", val.Bytes())
self.Printf(" => 0x%x", val.Bytes())
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
require(2)
// Pop value of the stack
val, mStart := stack.Popn()
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
vm.Printf(" => 0x%x", val)
self.Printf(" => 0x%x", val)
case MSTORE8:
require(2)
val, mStart := stack.Popn()
@ -628,7 +614,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
//mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
mem.store[mStart.Int64()] = byte(val.Int64() & 0xff)
vm.Printf(" => 0x%x", val)
self.Printf(" => 0x%x", val)
case SLOAD:
require(1)
loc := stack.Pop()
@ -636,21 +622,21 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
stack.Push(val.BigInt())
vm.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
case SSTORE:
require(2)
val, loc := stack.Popn()
closure.SetStorage(loc, ethutil.NewValue(val))
// Add the change to manifest
vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
self.env.State().Manifest().AddStorageChange(closure.Object(), loc.Bytes(), val)
vm.Printf(" {0x%x : 0x%x}", loc, val)
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
case JUMP:
require(1)
pc = stack.Pop()
// Reduce pc by one because of the increment that's at the end of this for loop
vm.Printf(" ~> %v", pc).Endl()
self.Printf(" ~> %v", pc).Endl()
continue
case JUMPI:
@ -659,11 +645,11 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
if cond.Cmp(ethutil.BigTrue) >= 0 {
pc = pos
vm.Printf(" ~> %v (t)", pc).Endl()
self.Printf(" ~> %v (t)", pc).Endl()
continue
} else {
vm.Printf(" (f)")
self.Printf(" (f)")
}
case PC:
stack.Push(pc)
@ -682,21 +668,21 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// Snapshot the current stack so we are able to
// revert back to it later.
snapshot = vm.state.Copy()
snapshot = self.env.State().Copy()
)
// Generate a new address
addr := ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64())
for i := uint64(0); vm.state.GetStateObject(addr) != nil; i++ {
ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64()+i)
addr := ethcrypto.CreateAddress(closure.Address(), closure.object.Nonce)
for i := uint64(0); self.env.State().GetStateObject(addr) != nil; i++ {
ethcrypto.CreateAddress(closure.Address(), closure.object.Nonce+i)
}
closure.object.Nonce++
vm.Printf(" (*) %x", addr).Endl()
self.Printf(" (*) %x", addr).Endl()
// Create a new contract
contract := vm.state.NewStateObject(addr)
if contract.Amount.Cmp(value) >= 0 {
contract := self.env.State().NewStateObject(addr)
if contract.Balance.Cmp(value) >= 0 {
closure.object.SubAmount(value)
contract.AddAmount(value)
@ -709,35 +695,35 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
closure.UseGas(closure.Gas)
// Create the closure
c := NewClosure(closure, contract, initCode, vm.state, gas, closure.Price)
c := NewClosure(closure, contract, initCode, gas, closure.Price)
// Call the closure and set the return value as
// main script.
contract.script, err = Call(vm, c, nil)
contract.Code, _, err = c.Call(self, nil)
} else {
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
}
if err != nil {
stack.Push(ethutil.BigFalse)
// Revert the state as it was before.
vm.state.Set(snapshot)
self.env.State().Set(snapshot)
vm.Printf("CREATE err %v", err)
self.Printf("CREATE err %v", err)
} else {
stack.Push(ethutil.BigD(addr))
vm.Printf("CREATE success")
self.Printf("CREATE success")
}
vm.Endl()
self.Endl()
// Debug hook
if vm.Dbg != nil {
vm.Dbg.SetCode(closure.Script)
if self.Dbg != nil {
self.Dbg.SetCode(closure.Code)
}
case CALL:
require(7)
vm.Endl()
self.Endl()
gas := stack.Pop()
// Pop gas and value of the stack.
@ -750,30 +736,30 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
if closure.object.Amount.Cmp(value) < 0 {
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
if closure.object.Balance.Cmp(value) < 0 {
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
closure.ReturnGas(gas, nil, nil)
closure.ReturnGas(gas, nil)
stack.Push(ethutil.BigFalse)
} else {
snapshot := vm.state.Copy()
snapshot := self.env.State().Copy()
stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
stateObject := self.env.State().GetOrNewStateObject(addr.Bytes())
closure.object.SubAmount(value)
stateObject.AddAmount(value)
// Create a new callable closure
c := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price)
c := NewClosure(closure, stateObject, stateObject.Code, gas, closure.Price)
// Executer the closure and get the return value (if any)
ret, err := Call(vm, c, args)
ret, _, err := c.Call(self, args)
if err != nil {
stack.Push(ethutil.BigFalse)
vmlogger.Debugf("Closure execution failed. %v\n", err)
vm.state.Set(snapshot)
self.env.State().Set(snapshot)
} else {
stack.Push(ethutil.BigTrue)
@ -781,8 +767,8 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
}
// Debug hook
if vm.Dbg != nil {
vm.Dbg.SetCode(closure.Script)
if self.Dbg != nil {
self.Dbg.SetCode(closure.Code)
}
}
case RETURN:
@ -790,44 +776,43 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
size, offset := stack.Popn()
ret := mem.Get(offset.Int64(), size.Int64())
vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
self.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
return closure.Return(ret), nil
case SUICIDE:
require(1)
receiver := vm.state.GetOrNewStateObject(stack.Pop().Bytes())
receiver := self.env.State().GetOrNewStateObject(stack.Pop().Bytes())
receiver.AddAmount(closure.object.Amount)
receiver.AddAmount(closure.object.Balance)
closure.object.MarkForDeletion()
fallthrough
case STOP: // Stop the closure
vm.Endl()
self.Endl()
return closure.Return(nil), nil
default:
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
fmt.Println(Code(closure.Script))
return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
}
pc.Add(pc, ethutil.Big1)
vm.Endl()
self.Endl()
if vm.Dbg != nil {
for _, instrNo := range vm.Dbg.BreakPoints() {
if self.Dbg != nil {
for _, instrNo := range self.Dbg.BreakPoints() {
if pc.Cmp(big.NewInt(instrNo)) == 0 {
vm.Stepping = true
self.Stepping = true
if !vm.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) {
if !self.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) {
return nil, nil
}
} else if vm.Stepping {
if !vm.Dbg.StepHook(prevStep, op, mem, stack, closure.Object()) {
} else if self.Stepping {
if !self.Dbg.StepHook(prevStep, op, mem, stack, closure.Object()) {
return nil, nil
}
}
@ -836,3 +821,20 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
}
}
func (self *Vm) Printf(format string, v ...interface{}) *Vm {
if self.Verbose && self.logTy == LogTyPretty {
self.logStr += fmt.Sprintf(format, v...)
}
return self
}
func (self *Vm) Endl() *Vm {
if self.Verbose && self.logTy == LogTyPretty {
vmlogger.Debugln(self.logStr)
self.logStr = ""
}
return self
}

45
ethvm/vm_test.go Normal file
View File

@ -0,0 +1,45 @@
package ethvm
import (
"fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"log"
"math/big"
"os"
"testing"
)
type TestEnv struct {
}
func (self TestEnv) Origin() []byte { return nil }
func (self TestEnv) BlockNumber() *big.Int { return nil }
func (self TestEnv) PrevHash() []byte { return nil }
func (self TestEnv) Coinbase() []byte { return nil }
func (self TestEnv) Time() int64 { return 0 }
func (self TestEnv) Difficulty() *big.Int { return nil }
func (self TestEnv) Value() *big.Int { return nil }
func (self TestEnv) State() *ethstate.State { return nil }
func TestVm(t *testing.T) {
ethlog.AddLogSystem(ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4)))
db, _ := ethdb.NewMemDatabase()
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db = db
stateObject := ethstate.NewStateObject([]byte{'j', 'e', 'f', 'f'})
callerClosure := NewClosure(stateObject, stateObject, []byte{0x60, 0x01}, big.NewInt(1000000), big.NewInt(0))
vm := New(TestEnv{})
vm.Verbose = true
ret, _, e := callerClosure.Call(vm, nil)
if e != nil {
fmt.Println("error", e)
}
fmt.Println(ret)
}

103
peer.go
View File

@ -4,15 +4,16 @@ import (
"bytes"
"container/list"
"fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"net"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
)
var peerlogger = ethlog.NewLogger("PEER")
@ -21,7 +22,7 @@ const (
// The size of the output buffer for writing messages
outputBufferSize = 50
// Current protocol version
ProtocolVersion = 23
ProtocolVersion = 25
// Interval for ping/pong message
pingPongTimer = 2 * time.Second
)
@ -122,9 +123,7 @@ type Peer struct {
// Last received pong message
lastPong int64
// Indicates whether a MsgGetPeersTy was requested of the peer
// this to prevent receiving false peers.
requestedPeerList bool
lastBlockReceived time.Time
host []byte
port uint16
@ -180,10 +179,9 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
// Set up the connection in another goroutine so we don't block the main thread
go func() {
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
conn, err := p.Connect(addr)
if err != nil {
peerlogger.Debugln("Connection to peer failed", err)
peerlogger.Debugln("Connection to peer failed. Giving up.", err)
p.Stop()
return
}
@ -199,6 +197,23 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
return p
}
func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
const maxTries = 3
for attempts := 0; attempts < maxTries; attempts++ {
conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
if err != nil {
//peerlogger.Debugf("Peer connection failed. Retrying (%d/%d) (%s)\n", attempts+1, maxTries, addr)
time.Sleep(time.Duration(attempts*20) * time.Second)
continue
}
// Success
return
}
return
}
// Getters
func (p *Peer) PingTime() string {
return p.pingTime.String()
@ -279,12 +294,14 @@ out:
// Ping timer
case <-pingTimer.C:
/*
timeSince := time.Since(time.Unix(p.lastPong, 0))
if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
p.Stop()
return
}
*/
p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
p.pingStartTime = time.Now()
@ -348,11 +365,17 @@ func (p *Peer) HandleInbound() {
// last pong so the peer handler knows this peer is still
// active.
p.lastPong = time.Now().Unix()
p.pingTime = time.Now().Sub(p.pingStartTime)
p.pingTime = time.Since(p.pingStartTime)
case ethwire.MsgBlockTy:
// Get all blocks and process them
var block, lastBlock *ethchain.Block
var err error
//var block, lastBlock *ethchain.Block
//var err error
var (
block, lastBlock *ethchain.Block
blockChain = p.ethereum.BlockChain()
err error
)
// Make sure we are actually receiving anything
if msg.Data.Len()-1 > 1 && p.diverted {
@ -368,11 +391,11 @@ func (p *Peer) HandleInbound() {
for i := msg.Data.Len() - 1; i >= 0; i-- {
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
// Do we have this block on our chain? If so we can continue
if !p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) {
if !blockChain.HasBlock(block.Hash()) {
// We don't have this block, but we do have a block with the same prevHash, diversion time!
if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) {
if blockChain.HasBlockWithPrevHash(block.PrevHash) {
p.diverted = false
if !p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) {
if !blockChain.FindCanonicalChainFromMsg(msg, block.PrevHash) {
p.SyncWithPeerToLastKnown()
break nextMsg
}
@ -397,10 +420,7 @@ func (p *Peer) HandleInbound() {
for i := msg.Data.Len() - 1; i >= 0; i-- {
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
//p.ethereum.StateManager().PrepareDefault(block)
//state := p.ethereum.StateManager().CurrentState()
err = p.ethereum.StateManager().Process(block, false)
if err != nil {
if ethutil.Config.Debug {
peerlogger.Infof("Block %x failed\n", block.Hash())
@ -411,6 +431,8 @@ func (p *Peer) HandleInbound() {
} else {
lastBlock = block
}
p.lastBlockReceived = time.Now()
}
if msg.Data.Len() <= 1 {
@ -422,19 +444,20 @@ func (p *Peer) HandleInbound() {
if err != nil {
// If the parent is unknown try to catch up with this peer
if ethchain.IsParentErr(err) {
/*
b := ethchain.NewBlockFromRlpValue(msg.Data.Get(0))
peerlogger.Infof("Attempting to catch (%x). Parent known\n", b.Hash())
peerlogger.Infof("Attempting to catch (%x). Parent unknown\n", b.Hash())
p.catchingUp = false
p.CatchupWithPeer(b.Hash())
peerlogger.Infoln(b)
*/
/*
peerlogger.Infoln("Attempting to catch. Parent known")
p.catchingUp = false
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
*/
} else if ethchain.IsValidationErr(err) {
fmt.Println("Err:", err)
p.catchingUp = false
@ -463,15 +486,10 @@ func (p *Peer) HandleInbound() {
p.ethereum.TxPool().QueueTransaction(tx)
}
case ethwire.MsgGetPeersTy:
// Flag this peer as a 'requested of new peers' this to
// prevent malicious peers being forced.
p.requestedPeerList = true
// Peer asked for list of connected peers
p.pushPeers()
case ethwire.MsgPeersTy:
// Received a list of peers (probably because MsgGetPeersTy was send)
// Only act on message if we actually requested for a peers list
if p.requestedPeerList {
data := msg.Data
// Create new list of possible peers for the ethereum to process
peers := make([]string, data.Len())
@ -483,10 +501,6 @@ func (p *Peer) HandleInbound() {
// Connect to the list of peers
p.ethereum.ProcessPeerList(peers)
// Mark unrequested again
p.requestedPeerList = false
}
case ethwire.MsgGetChainTy:
var parent *ethchain.Block
// Length minus one since the very last element in the array is a count
@ -559,6 +573,25 @@ func (p *Peer) HandleInbound() {
p.Stop()
}
// General update method
func (self *Peer) update() {
serviceTimer := time.NewTicker(5 * time.Second)
out:
for {
select {
case <-serviceTimer.C:
if time.Since(self.lastBlockReceived) > 10*time.Second {
self.catchingUp = false
}
case <-self.quit:
break out
}
}
serviceTimer.Stop()
}
func (p *Peer) Start() {
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
@ -581,6 +614,8 @@ func (p *Peer) Start() {
go p.HandleOutbound()
// Run the inbound handler in a new goroutine
go p.HandleInbound()
// Run the general update handler
go p.update()
// Wait a few seconds for startup and then ask for an initial ping
time.Sleep(2 * time.Second)
@ -698,11 +733,13 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
ethlogger.Infof("Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers)
/*
// Catch up with the connected peer
if !p.ethereum.IsUpToDate() {
peerlogger.Debugln("Already syncing up with a peer; sleeping")
time.Sleep(10 * time.Second)
}
*/
p.SyncWithPeerToLastKnown()
peerlogger.Debugln(p)
@ -757,7 +794,7 @@ func (p *Peer) CatchupWithPeer(blockHash []byte) {
if !p.catchingUp {
// Make sure nobody else is catching up when you want to do this
p.catchingUp = true
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(30)})
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(100)})
p.QueueMessage(msg)
peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())