Merge branch 'release/0.5.14'

This commit is contained in:
obscuren 2014-06-20 00:47:52 +02:00
commit 3f1f8438ed
20 changed files with 417 additions and 464 deletions

View File

@ -6,7 +6,7 @@ Ethereum
Ethereum Go Development package (C) Jeffrey Wilcke Ethereum Go Development package (C) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof Ethereum is currently in its testing phase. The current state is "Proof
of Concept 5.0 RC13". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). of Concept 5.0 RC14". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
Ethereum Go is split up in several sub packages Please refer to each Ethereum Go is split up in several sub packages Please refer to each
individual package for more information. individual package for more information.

View File

@ -28,7 +28,7 @@ func Disassemble(script []byte) (asm []string) {
if len(data) == 0 { if len(data) == 0 {
data = []byte{0} data = []byte{0}
} }
asm = append(asm, fmt.Sprintf("%#x", data)) asm = append(asm, fmt.Sprintf("0x%x", data))
pc.Add(pc, big.NewInt(a-1)) pc.Add(pc, big.NewInt(a-1))
} }

View File

@ -1,236 +0,0 @@
package ethchain
import (
"bytes"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject {
contract := MakeContract(tx, state)
if contract != nil {
state.states[string(tx.CreationAddress())] = contract.state
return contract
}
return nil
}
func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) (ret []byte, gas *big.Int, err error) {
account := state.GetAccount(tx.Sender())
err = account.ConvertGas(tx.Gas, tx.GasPrice)
if err != nil {
ethutil.Config.Log.Debugln(err)
return
}
closure := NewClosure(account, object, script, state, tx.Gas, tx.GasPrice)
vm := NewVm(state, sm, RuntimeVars{
Origin: account.Address(),
BlockNumber: block.BlockInfo().Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
Value: tx.Value,
//Price: tx.GasPrice,
})
ret, gas, err = closure.Call(vm, tx.Data, nil)
// Update the account (refunds)
state.UpdateStateObject(account)
state.UpdateStateObject(object)
return
}
func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObject, 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 {
ethutil.Config.Log.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
self.Ethereum.Reactor().Post("newTx:post", tx)
}()
txTotalBytes := big.NewInt(int64(len(tx.Data)))
//fmt.Println("txTotalBytes", txTotalBytes)
//txTotalBytes.Div(txTotalBytes, ethutil.Big32)
addGas(new(big.Int).Mul(txTotalBytes, GasData))
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 {
state.UpdateStateObject(sender)
err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
return
}
coinbase.BuyGas(gas, tx.GasPrice)
state.UpdateStateObject(coinbase)
fmt.Printf("1. root %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)
state.UpdateStateObject(sender)
fmt.Printf("3. root %x\n", state.Root())
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(tx.Value)
state.UpdateStateObject(receiver)
fmt.Printf("2. root %x\n", state.Root())
}
ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash())
return
}
func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) {
/*
Applies transactions to the given state and creates new
state objects where needed.
If said objects needs to be created
run the initialization script provided by the transaction and
assume there's a return value. The return value will be set to
the script section of the state object.
*/
var (
addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) }
gas = new(big.Int)
script []byte
)
totalGasUsed = big.NewInt(0)
snapshot := state.Snapshot()
ca := state.GetAccount(coinbase)
// Apply the transaction to the current state
gas, err = sm.ProcessTransaction(tx, ca, state, false)
addTotalGas(gas)
fmt.Println("gas used by tx", gas)
if tx.CreatesContract() {
if err == nil {
// Create a new state object and the transaction
// as it's data provider.
contract := sm.MakeStateObject(state, tx)
if contract != nil {
fmt.Println(Disassemble(contract.Init()))
// Evaluate the initialization script
// and use the return value as the
// script section for the state object.
script, gas, err = sm.EvalScript(state, contract.Init(), contract, tx, block)
fmt.Println("gas used by eval", gas)
addTotalGas(gas)
fmt.Println("total =", totalGasUsed)
fmt.Println("script len =", len(script))
if err != nil {
err = fmt.Errorf("[STATE] Error during init script run %v", err)
return
}
contract.script = script
state.UpdateStateObject(contract)
} else {
err = fmt.Errorf("[STATE] Unable to create contract")
}
} else {
err = fmt.Errorf("[STATE] contract creation tx: %v for sender %x", err, tx.Sender())
}
} else {
// Find the state object at the "recipient" address. If
// there's an object attempt to run the script.
stateObject := state.GetStateObject(tx.Recipient)
if err == nil && stateObject != nil && len(stateObject.Script()) > 0 {
_, gas, err = sm.EvalScript(state, stateObject.Script(), stateObject, tx, block)
addTotalGas(gas)
}
}
parent := sm.bc.GetBlock(block.PrevHash)
total := new(big.Int).Add(block.GasUsed, totalGasUsed)
limit := block.CalcGasLimit(parent)
if total.Cmp(limit) > 0 {
state.Revert(snapshot)
err = GasLimitError(total, limit)
}
return
}
// Apply transactions uses the transaction passed to it and applies them onto
// the current processing state.
func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) {
// Process each transaction/contract
var receipts []*Receipt
var validTxs []*Transaction
var ignoredTxs []*Transaction // Transactions which go over the gasLimit
totalUsedGas := big.NewInt(0)
for _, tx := range txs {
usedGas, err := sm.ApplyTransaction(coinbase, state, block, tx)
if err != nil {
if IsNonceErr(err) {
continue
}
if IsGasLimitErr(err) {
ignoredTxs = append(ignoredTxs, tx)
// We need to figure out if we want to do something with thse txes
ethutil.Config.Log.Debugln("Gastlimit:", err)
continue
}
ethutil.Config.Log.Infoln(err)
}
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas))
receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative}
receipts = append(receipts, receipt)
validTxs = append(validTxs, tx)
}
fmt.Println("################# MADE\n", receipts, "\n############################")
// Update the total gas used for the block (to be mined)
block.GasUsed = totalUsedGas
return receipts, validTxs
}

View File

@ -2,7 +2,7 @@ package ethchain
import ( import (
"fmt" "fmt"
_ "github.com/ethereum/eth-go/ethutil" "math"
"math/big" "math/big"
) )
@ -118,7 +118,13 @@ func (m *Memory) Resize(size uint64) {
} }
func (m *Memory) Get(offset, size int64) []byte { func (m *Memory) Get(offset, size int64) []byte {
return m.store[offset : offset+size] if len(m.store) > int(offset) {
end := int(math.Min(float64(len(m.store)), float64(offset+size)))
return m.store[offset:end]
}
return nil
} }
func (m *Memory) Len() int { func (m *Memory) Len() int {

View File

@ -13,8 +13,6 @@ import (
type State struct { type State struct {
// The trie for this structure // The trie for this structure
trie *ethutil.Trie trie *ethutil.Trie
// Nested states
states map[string]*State
stateObjects map[string]*StateObject stateObjects map[string]*StateObject
@ -23,7 +21,7 @@ type State struct {
// Create a new state from a given trie // Create a new state from a given trie
func NewState(trie *ethutil.Trie) *State { func NewState(trie *ethutil.Trie) *State {
return &State{trie: trie, states: make(map[string]*State), stateObjects: make(map[string]*StateObject), manifest: NewManifest()} return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
} }
// Resets the trie and all siblings // Resets the trie and all siblings
@ -38,12 +36,16 @@ func (s *State) Reset() {
stateObject.state.Reset() stateObject.state.Reset()
} }
s.Empty()
} }
// Syncs the trie and all siblings // Syncs the trie and all siblings
func (s *State) Sync() { func (s *State) Sync() {
// Sync all nested states // Sync all nested states
for _, stateObject := range s.stateObjects { for _, stateObject := range s.stateObjects {
s.UpdateStateObject(stateObject)
if stateObject.state == nil { if stateObject.state == nil {
continue continue
} }
@ -52,6 +54,18 @@ func (s *State) Sync() {
} }
s.trie.Sync() s.trie.Sync()
s.Empty()
}
func (self *State) Empty() {
self.stateObjects = make(map[string]*StateObject)
}
func (self *State) Update() {
for _, stateObject := range self.stateObjects {
self.UpdateStateObject(stateObject)
}
} }
// Purges the current trie. // Purges the current trie.
@ -64,6 +78,12 @@ func (s *State) EachStorage(cb ethutil.EachCallback) {
it.Each(cb) it.Each(cb)
} }
func (self *State) ResetStateObject(stateObject *StateObject) {
delete(self.stateObjects, string(stateObject.Address()))
stateObject.state.Reset()
}
func (self *State) UpdateStateObject(stateObject *StateObject) { func (self *State) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address() addr := stateObject.Address()
@ -98,13 +118,21 @@ func (self *State) GetStateObject(addr []byte) *StateObject {
func (self *State) GetOrNewStateObject(addr []byte) *StateObject { func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject == nil { if stateObject == nil {
stateObject = NewStateObject(addr) stateObject = self.NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject
} }
return stateObject return stateObject
} }
func (self *State) NewStateObject(addr []byte) *StateObject {
ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(+) %x\n", addr)
stateObject := NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject
return stateObject
}
func (self *State) GetAccount(addr []byte) *StateObject { func (self *State) GetAccount(addr []byte) *StateObject {
return self.GetOrNewStateObject(addr) return self.GetOrNewStateObject(addr)
} }
@ -126,13 +154,10 @@ func (self *State) Copy() *State {
return nil return nil
} }
func (s *State) Snapshot() *State { func (self *State) Set(state *State) {
return s.Copy() //s.trie = snapshot.trie
} //s.stateObjects = snapshot.stateObjects
self = state
func (s *State) Revert(snapshot *State) {
s.trie = snapshot.trie
s.states = snapshot.states
} }
func (s *State) Put(key, object []byte) { func (s *State) Put(key, object []byte) {

View File

@ -127,6 +127,9 @@ done:
// Notify all subscribers // Notify all subscribers
self.Ethereum.Reactor().Post("newTx:post", tx) self.Ethereum.Reactor().Post("newTx:post", tx)
// Update the state with pending changes
state.Update()
txGas.Sub(txGas, st.gas) txGas.Sub(txGas, st.gas)
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas)) accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative}
@ -135,8 +138,6 @@ done:
handled = append(handled, tx) handled = append(handled, tx)
} }
fmt.Println("################# MADE\n", receipts, "\n############################")
parent.GasUsed = totalUsedGas parent.GasUsed = totalUsedGas
return receipts, handled, unhandled, err return receipts, handled, unhandled, err
@ -154,7 +155,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) error {
} }
// Block processing and validating with a given (temporarily) state // Block processing and validating with a given (temporarily) state
func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontReact bool) error { func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontReact bool) (err error) {
// Processing a blocks may never happen simultaneously // Processing a blocks may never happen simultaneously
sm.mutex.Lock() sm.mutex.Lock()
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
@ -175,30 +176,40 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea
if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil { if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil {
return ParentError(block.PrevHash) return ParentError(block.PrevHash)
} }
fmt.Println(block.Receipts())
coinbase := state.GetOrNewStateObject(block.Coinbase) coinbase := state.GetOrNewStateObject(block.Coinbase)
coinbase.SetGasPool(block.CalcGasLimit(parent)) coinbase.SetGasPool(block.CalcGasLimit(parent))
// Process the transactions on to current block // Process the transactions on to current block
//sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) receipts, _, _, _ := sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions())
sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) defer func() {
if err != nil {
if len(receipts) == len(block.Receipts()) {
for i, receipt := range block.Receipts() {
ethutil.Config.Log.Debugf("diff (r) %v ~ %x <=> (c) %v ~ %x (%x)\n", receipt.CumulativeGasUsed, receipt.PostState[0:4], receipts[i].CumulativeGasUsed, receipts[i].PostState[0:4], receipt.Tx.Hash())
}
} else {
ethutil.Config.Log.Debugln("Unable to print receipt diff. Length didn't match", len(receipts), "for", len(block.Receipts()))
}
}
}()
// Block validation // Block validation
if err := sm.ValidateBlock(block); err != nil { if err = sm.ValidateBlock(block); err != nil {
fmt.Println("[SM] Error validating block:", err) fmt.Println("[SM] Error validating block:", err)
return err return err
} }
// I'm not sure, but I don't know if there should be thrown // I'm not sure, but I don't know if there should be thrown
// any errors at this time. // any errors at this time.
if err := sm.AccumelateRewards(state, block); err != nil { if err = sm.AccumelateRewards(state, block); err != nil {
fmt.Println("[SM] Error accumulating reward", err) fmt.Println("[SM] Error accumulating reward", err)
return err return err
} }
if !block.State().Cmp(state) { if !block.State().Cmp(state) {
return 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
} }
// Calculate the new total difficulty and sync back to the db // Calculate the new total difficulty and sync back to the db
@ -270,10 +281,12 @@ func (sm *StateManager) ValidateBlock(block *Block) error {
return ValidationError("Block timestamp less then prev block %v", diff) return ValidationError("Block timestamp less then prev block %v", diff)
} }
/* XXX
// New blocks must be within the 15 minute range of the last block. // New blocks must be within the 15 minute range of the last block.
if diff > int64(15*time.Minute) { if diff > int64(15*time.Minute) {
return ValidationError("Block is too far in the future of last block (> 15 minutes)") return ValidationError("Block is too far in the future of last block (> 15 minutes)")
} }
*/
// Verify the nonce of the block. Return an error if it's not valid // Verify the nonce of the block. Return an error if it's not valid
if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) { if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) {

View File

@ -4,8 +4,15 @@ import (
"fmt" "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big" "math/big"
"strings"
) )
type Code []byte
func (self Code) String() string {
return strings.Join(Disassemble(self), " ")
}
type StateObject struct { type StateObject struct {
// Address of the object // Address of the object
address []byte address []byte
@ -15,8 +22,8 @@ type StateObject struct {
Nonce uint64 Nonce uint64
// Contract related attributes // Contract related attributes
state *State state *State
script []byte script Code
initScript []byte initScript Code
// Total gas pool is the total amount of gas currently // Total gas pool is the total amount of gas currently
// left if this object is the coinbase. Gas is directly // left if this object is the coinbase. Gas is directly
@ -30,12 +37,9 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
if tx.IsContract() { if tx.IsContract() {
addr := tx.CreationAddress() addr := tx.CreationAddress()
value := tx.Value contract := state.NewStateObject(addr)
contract := NewContract(addr, value, ZeroHash256)
contract.initScript = tx.Data contract.initScript = tx.Data
contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, ""))
state.UpdateStateObject(contract)
return contract return contract
} }
@ -44,7 +48,7 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
} }
func NewStateObject(addr []byte) *StateObject { func NewStateObject(addr []byte) *StateObject {
return &StateObject{address: addr, Amount: new(big.Int)} return &StateObject{address: addr, Amount: new(big.Int), gasPool: new(big.Int)}
} }
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
@ -120,13 +124,13 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
func (c *StateObject) AddAmount(amount *big.Int) { func (c *StateObject) AddAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Add(c.Amount, amount)) c.SetAmount(new(big.Int).Add(c.Amount, amount))
ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount)
} }
func (c *StateObject) SubAmount(amount *big.Int) { func (c *StateObject) SubAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Sub(c.Amount, amount)) c.SetAmount(new(big.Int).Sub(c.Amount, amount))
ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount)
} }
func (c *StateObject) SetAmount(amount *big.Int) { func (c *StateObject) SetAmount(amount *big.Int) {
@ -172,6 +176,26 @@ func (self *StateObject) RefundGas(gas, price *big.Int) {
self.Amount.Sub(self.Amount, rGas) self.Amount.Sub(self.Amount, rGas)
} }
func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address())
stateObject.Amount.Set(self.Amount)
stateObject.ScriptHash = ethutil.CopyBytes(self.ScriptHash)
stateObject.Nonce = self.Nonce
if self.state != nil {
stateObject.state = self.state.Copy()
}
stateObject.script = ethutil.CopyBytes(self.script)
stateObject.initScript = ethutil.CopyBytes(self.initScript)
//stateObject.gasPool.Set(self.gasPool)
return self
}
func (self *StateObject) Set(stateObject *StateObject) {
self = stateObject
}
/*
func (self *StateObject) Copy() *StateObject { func (self *StateObject) Copy() *StateObject {
stCopy := &StateObject{} stCopy := &StateObject{}
stCopy.address = make([]byte, len(self.address)) stCopy.address = make([]byte, len(self.address))
@ -190,6 +214,7 @@ func (self *StateObject) Copy() *StateObject {
return stCopy return stCopy
} }
*/
// Returns the address of the contract/account // Returns the address of the contract/account
func (c *StateObject) Address() []byte { func (c *StateObject) Address() []byte {
@ -197,12 +222,12 @@ func (c *StateObject) Address() []byte {
} }
// Returns the main script body // Returns the main script body
func (c *StateObject) Script() []byte { func (c *StateObject) Script() Code {
return c.script return c.script
} }
// Returns the initialization script // Returns the initialization script
func (c *StateObject) Init() []byte { func (c *StateObject) Init() Code {
return c.initScript return c.initScript
} }

View File

@ -23,9 +23,11 @@ import (
* 6) Derive new state root * 6) Derive new state root
*/ */
type StateTransition struct { type StateTransition struct {
coinbase []byte coinbase, receiver []byte
tx *Transaction tx *Transaction
gas *big.Int gas, gasPrice *big.Int
value *big.Int
data []byte
state *State state *State
block *Block block *Block
@ -33,7 +35,7 @@ type StateTransition struct {
} }
func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition { func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition {
return &StateTransition{coinbase.Address(), tx, new(big.Int), state, block, coinbase, nil, nil} 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() *StateObject {
@ -53,7 +55,7 @@ func (self *StateTransition) Sender() *StateObject {
return self.sen return self.sen
} }
func (self *StateTransition) Receiver() *StateObject { func (self *StateTransition) Receiver() *StateObject {
if self.tx.CreatesContract() { if self.tx != nil && self.tx.CreatesContract() {
return nil return nil
} }
@ -67,13 +69,8 @@ func (self *StateTransition) Receiver() *StateObject {
func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject { func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject {
contract := MakeContract(tx, state) contract := MakeContract(tx, state)
if contract != nil {
state.states[string(tx.CreationAddress())] = contract.state
return contract return contract
}
return nil
} }
func (self *StateTransition) UseGas(amount *big.Int) error { func (self *StateTransition) UseGas(amount *big.Int) error {
@ -94,7 +91,7 @@ func (self *StateTransition) BuyGas() error {
sender := self.Sender() sender := self.Sender()
if sender.Amount.Cmp(self.tx.GasValue()) < 0 { if sender.Amount.Cmp(self.tx.GasValue()) < 0 {
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), self.tx.Value) return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Amount)
} }
coinbase := self.Coinbase() coinbase := self.Coinbase()
@ -102,7 +99,6 @@ func (self *StateTransition) BuyGas() error {
if err != nil { if err != nil {
return err return err
} }
//self.state.UpdateStateObject(coinbase)
self.AddGas(self.tx.Gas) self.AddGas(self.tx.Gas)
sender.SubAmount(self.tx.GasValue()) sender.SubAmount(self.tx.GasValue())
@ -119,22 +115,10 @@ func (self *StateTransition) RefundGas() {
sender.AddAmount(remaining) sender.AddAmount(remaining)
} }
func (self *StateTransition) TransitionState() (err error) { func (self *StateTransition) preCheck() (err error) {
//snapshot := st.state.Snapshot()
/*
defer func() {
if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r)
err = fmt.Errorf("state transition err %v", r)
}
}()
*/
var ( var (
tx = self.tx tx = self.tx
sender = self.Sender() sender = self.Sender()
receiver *StateObject
) )
// Make sure this transaction's nonce is correct // Make sure this transaction's nonce is correct
@ -147,38 +131,49 @@ func (self *StateTransition) TransitionState() (err error) {
return err return err
} }
// XXX Transactions after this point are considered valid. return nil
}
func (self *StateTransition) TransitionState() (err error) {
ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(~) %x\n", self.tx.Hash())
/*
defer func() { defer func() {
self.RefundGas() if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r)
if sender != nil { err = fmt.Errorf("state transition err %v", r)
self.state.UpdateStateObject(sender)
} }
if receiver != nil {
self.state.UpdateStateObject(receiver)
}
self.state.UpdateStateObject(self.Coinbase())
}() }()
*/
// XXX Transactions after this point are considered valid.
if err = self.preCheck(); err != nil {
return
}
var (
tx = self.tx
sender = self.Sender()
receiver *StateObject
)
defer self.RefundGas()
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
sender.Nonce += 1 sender.Nonce += 1
// Get the receiver (TODO fix this, if coinbase is the receiver we need to save/retrieve)
receiver = self.Receiver() receiver = self.Receiver()
// Transaction gas // Transaction gas
if err = self.UseGas(GasTx); err != nil { if err = self.UseGas(GasTx); err != nil {
return err return
} }
// Pay data gas // Pay data gas
dataPrice := big.NewInt(int64(len(tx.Data))) dataPrice := big.NewInt(int64(len(self.data)))
dataPrice.Mul(dataPrice, GasData) dataPrice.Mul(dataPrice, GasData)
if err = self.UseGas(dataPrice); err != nil { if err = self.UseGas(dataPrice); err != nil {
return err return
} }
// If the receiver is nil it's a contract (\0*32). // If the receiver is nil it's a contract (\0*32).
@ -186,75 +181,83 @@ func (self *StateTransition) TransitionState() (err error) {
// Create a new state object for the contract // Create a new state object for the contract
receiver = self.MakeStateObject(self.state, tx) receiver = self.MakeStateObject(self.state, tx)
if receiver == nil { if receiver == nil {
return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx) return fmt.Errorf("Unable to create contract")
} }
} }
// Transfer value from sender to receiver // Transfer value from sender to receiver
if err = self.transferValue(sender, receiver); err != nil { if err = self.transferValue(sender, receiver); err != nil {
return err return
} }
// Process the init code and create 'valid' contract // Process the init code and create 'valid' contract
if tx.CreatesContract() { if IsContractAddr(self.receiver) {
// Evaluate the initialization script // Evaluate the initialization script
// and use the return value as the // and use the return value as the
// script section for the state object. // script section for the state object.
//script, gas, err = sm.Eval(state, contract.Init(), contract, tx, block) self.data = nil
code, err := self.Eval(receiver.Init(), receiver)
if err != nil { code, err, deepErr := self.Eval(receiver.Init(), receiver)
return fmt.Errorf("Error during init script run %v", err) if err != nil || deepErr {
self.state.ResetStateObject(receiver)
return fmt.Errorf("Error during init script run %v (deepErr = %v)", err, deepErr)
} }
receiver.script = code receiver.script = code
} else { } else {
if len(receiver.Script()) > 0 { if len(receiver.Script()) > 0 {
_, err := self.Eval(receiver.Script(), receiver) var deepErr bool
_, err, deepErr = self.Eval(receiver.Script(), receiver)
if err != nil { if err != nil {
return fmt.Errorf("Error during code execution %v", err) self.state.ResetStateObject(receiver)
return fmt.Errorf("Error during code execution %v (deepErr = %v)", err, deepErr)
} }
} }
} }
return nil return
} }
func (self *StateTransition) transferValue(sender, receiver *StateObject) error { func (self *StateTransition) transferValue(sender, receiver *StateObject) error {
if sender.Amount.Cmp(self.tx.Value) < 0 { if sender.Amount.Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount) return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
} }
if self.tx.Value.Cmp(ethutil.Big0) > 0 { //if self.value.Cmp(ethutil.Big0) > 0 {
// Subtract the amount from the senders account // Subtract the amount from the senders account
sender.SubAmount(self.tx.Value) sender.SubAmount(self.value)
// Add the amount to receivers account which should conclude this transaction // Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(self.tx.Value) receiver.AddAmount(self.value)
ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], self.tx.Value, self.tx.Hash()) //ethutil.Config.Log.Debugf("%x => %x (%v)\n", sender.Address()[:4], receiver.Address()[:4], self.value)
} //}
return nil return nil
} }
func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error) { func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error, deepErr bool) {
var ( var (
tx = self.tx
block = self.block block = self.block
initiator = self.Sender() initiator = self.Sender()
state = self.state state = self.state
) )
closure := NewClosure(initiator, context, script, state, self.gas, tx.GasPrice) closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice)
vm := NewVm(state, nil, RuntimeVars{ vm := NewVm(state, nil, RuntimeVars{
Origin: initiator.Address(), Origin: initiator.Address(),
BlockNumber: block.BlockInfo().Number, Block: block,
BlockNumber: block.Number,
PrevHash: block.PrevHash, PrevHash: block.PrevHash,
Coinbase: block.Coinbase, Coinbase: block.Coinbase,
Time: block.Time, Time: block.Time,
Diff: block.Difficulty, Diff: block.Difficulty,
Value: tx.Value, Value: self.value,
}) })
ret, _, err = closure.Call(vm, tx.Data, nil) vm.Verbose = true
ret, _, err = closure.Call(vm, self.data, nil)
deepErr = vm.err != nil
return return
} }

View File

@ -10,6 +10,10 @@ import (
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
func IsContractAddr(addr []byte) bool {
return bytes.Compare(addr, ContractAddr) == 0
}
type Transaction struct { type Transaction struct {
Nonce uint64 Nonce uint64
Recipient []byte Recipient []byte
@ -65,7 +69,7 @@ func (tx *Transaction) CreatesContract() bool {
return tx.contractCreation return tx.contractCreation
} }
/* Depricated */ /* Deprecated */
func (tx *Transaction) IsContract() bool { func (tx *Transaction) IsContract() bool {
return tx.CreatesContract() return tx.CreatesContract()
} }
@ -149,7 +153,7 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.r = decoder.Get(7).Bytes() tx.r = decoder.Get(7).Bytes()
tx.s = decoder.Get(8).Bytes() tx.s = decoder.Get(8).Bytes()
if bytes.Compare(tx.Recipient, ContractAddr) == 0 { if IsContractAddr(tx.Recipient) {
tx.contractCreation = true tx.contractCreation = true
} }
} }

View File

@ -166,6 +166,7 @@ var opCodeToString = map[OpCode]string{
GASLIMIT: "GASLIMIT", GASLIMIT: "GASLIMIT",
// 0x50 range - 'storage' and execution // 0x50 range - 'storage' and execution
POP: "POP",
DUP: "DUP", DUP: "DUP",
SWAP: "SWAP", SWAP: "SWAP",
MLOAD: "MLOAD", MLOAD: "MLOAD",
@ -226,7 +227,7 @@ var opCodeToString = map[OpCode]string{
func (o OpCode) String() string { func (o OpCode) String() string {
str := opCodeToString[o] str := opCodeToString[o]
if len(str) == 0 { if len(str) == 0 {
return fmt.Sprintf("Missing opcode %#x", int(o)) return fmt.Sprintf("Missing opcode 0x%x", int(o))
} }
return str return str

View File

@ -1,12 +1,9 @@
package ethchain package ethchain
import ( import (
_ "bytes"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go"
"math" "math"
_ "math"
"math/big" "math/big"
) )
@ -45,11 +42,18 @@ type Vm struct {
state *State state *State
stateManager *StateManager stateManager *StateManager
Verbose bool
logStr string
err error
} }
type RuntimeVars struct { type RuntimeVars struct {
Origin []byte Origin []byte
BlockNumber uint64 Block *Block
BlockNumber *big.Int
PrevHash []byte PrevHash []byte
Coinbase []byte Coinbase []byte
Time int64 Time int64
@ -58,6 +62,23 @@ type RuntimeVars struct {
Value *big.Int Value *big.Int
} }
func (self *Vm) Printf(format string, v ...interface{}) *Vm {
if self.Verbose {
self.logStr += fmt.Sprintf(format, v...)
}
return self
}
func (self *Vm) Endl() *Vm {
if self.Verbose {
ethutil.Config.Log.Infoln(self.logStr)
self.logStr = ""
}
return self
}
func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm { func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
return &Vm{vars: vars, state: state, stateManager: stateManager} return &Vm{vars: vars, state: state, stateManager: stateManager}
} }
@ -69,14 +90,14 @@ var isRequireError = false
func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) { func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) {
// Recover from any require exception // Recover from any require exception
defer func() { defer func() {
if r := recover(); r != nil /*&& isRequireError*/ { if r := recover(); r != nil {
ret = closure.Return(nil) ret = closure.Return(nil)
err = fmt.Errorf("%v", r) err = fmt.Errorf("%v", r)
fmt.Println("vm err", err) fmt.Println("vm err", err)
} }
}() }()
ethutil.Config.Log.Debugf("[VM] Running closure %x\n", closure.object.Address()) ethutil.Config.Log.Debugf("[VM] (~) %x gas: %v (d) %x\n", closure.object.Address(), closure.Gas, closure.Args)
// Memory for the current closure // Memory for the current closure
mem := &Memory{} mem := &Memory{}
@ -95,10 +116,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
step := 0 step := 0
prevStep := 0 prevStep := 0
if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("# op\n")
}
for { for {
prevStep = step prevStep = step
// The base for all big integer arithmetic // The base for all big integer arithmetic
@ -109,21 +126,22 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
val := closure.Get(pc) val := closure.Get(pc)
// Get the opcode (it must be an opcode!) // Get the opcode (it must be an opcode!)
op := OpCode(val.Uint()) op := OpCode(val.Uint())
if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
}
gas := new(big.Int) gas := new(big.Int)
addStepGasUsage := func(amount *big.Int) { addStepGasUsage := func(amount *big.Int) {
if amount.Cmp(ethutil.Big0) >= 0 {
gas.Add(gas, amount) gas.Add(gas, amount)
} }
}
addStepGasUsage(GasStep) addStepGasUsage(GasStep)
var newMemSize uint64 = 0 var newMemSize uint64 = 0
switch op { switch op {
case STOP: case STOP:
gas.Set(ethutil.Big0)
case SUICIDE: case SUICIDE:
gas.Set(ethutil.Big0)
case SLOAD: case SLOAD:
gas.Set(GasSLoad) gas.Set(GasSLoad)
case SSTORE: case SSTORE:
@ -169,7 +187,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case CALL: case CALL:
require(7) require(7)
gas.Set(GasCall) gas.Set(GasCall)
addStepGasUsage(stack.data[stack.Len()-2]) addStepGasUsage(stack.data[stack.Len()-1])
x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64() x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64()
y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64() y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64()
@ -189,11 +207,16 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
} }
if !closure.UseGas(gas) { if !closure.UseGas(gas) {
ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas) err := fmt.Errorf("Insufficient gas for %v. req %v has %v", op, gas, closure.Gas)
return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) closure.UseGas(closure.Gas)
return closure.Return(nil), err
} }
vm.Printf("(pc) %-3d -o- %-14s", pc, op.String())
vm.Printf(" (g) %-3v (%v)", gas, closure.Gas)
mem.Resize(newMemSize) mem.Resize(newMemSize)
switch op { switch op {
@ -204,29 +227,41 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case ADD: case ADD:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
// (x + y) % 2 ** 256 vm.Printf(" %v + %v", y, x)
base.Add(x, y)
base.Add(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case SUB: case SUB:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
// (x - y) % 2 ** 256 vm.Printf(" %v - %v", y, x)
base.Sub(x, y)
base.Sub(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case MUL: case MUL:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
// (x * y) % 2 ** 256 vm.Printf(" %v * %v", y, x)
base.Mul(x, y)
base.Mul(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case DIV: case DIV:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
// floor(x / y) vm.Printf(" %v / %v", y, x)
base.Div(x, y)
base.Div(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case SDIV: case SDIV:
@ -249,7 +284,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case MOD: case MOD:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
base.Mod(x, y)
vm.Printf(" %v %% %v", y, x)
base.Mod(y, x)
vm.Printf(" = %v", base)
stack.Push(base) stack.Push(base)
case SMOD: case SMOD:
require(2) require(2)
@ -271,7 +311,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case EXP: case EXP:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
base.Exp(x, y, Pow256)
vm.Printf(" %v ** %v", y, x)
base.Exp(y, x, Pow256)
vm.Printf(" = %v", base)
stack.Push(base) stack.Push(base)
case NEG: case NEG:
@ -280,7 +325,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
stack.Push(base) stack.Push(base)
case LT: case LT:
require(2) require(2)
x, y := stack.Popn() y, x := stack.Popn()
vm.Printf(" %v < %v", x, y)
// x < y // x < y
if x.Cmp(y) < 0 { if x.Cmp(y) < 0 {
stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
@ -289,7 +335,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
} }
case GT: case GT:
require(2) require(2)
x, y := stack.Popn() y, x := stack.Popn()
vm.Printf(" %v > %v", x, y)
// x > y // x > y
if x.Cmp(y) > 0 { if x.Cmp(y) > 0 {
stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
@ -299,6 +347,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case EQ: case EQ:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
vm.Printf(" %v == %v", y, x)
// x == y // x == y
if x.Cmp(y) == 0 { if x.Cmp(y) == 0 {
stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
@ -318,24 +368,21 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case AND: case AND:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) { vm.Printf(" %v & %v", y, x)
stack.Push(ethutil.BigTrue)
} else {
stack.Push(ethutil.BigFalse)
}
stack.Push(base.And(y, x))
case OR: case OR:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) { vm.Printf(" %v | %v", y, x)
stack.Push(ethutil.BigTrue)
} else { stack.Push(base.Or(y, x))
stack.Push(ethutil.BigFalse)
}
case XOR: case XOR:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
stack.Push(base.Xor(x, y)) vm.Printf(" %v ^ %v", y, x)
stack.Push(base.Xor(y, x))
case BYTE: case BYTE:
require(2) require(2)
val, th := stack.Popn() val, th := stack.Popn()
@ -360,25 +407,35 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case ORIGIN: case ORIGIN:
stack.Push(ethutil.BigD(vm.vars.Origin)) stack.Push(ethutil.BigD(vm.vars.Origin))
case CALLER: case CALLER:
stack.Push(ethutil.BigD(closure.caller.Address())) caller := closure.caller.Address()
stack.Push(ethutil.BigD(caller))
vm.Printf(" => %x", caller)
case CALLVALUE: case CALLVALUE:
stack.Push(vm.vars.Value) stack.Push(vm.vars.Value)
case CALLDATALOAD: case CALLDATALOAD:
require(1) require(1)
offset := stack.Pop().Int64() offset := stack.Pop().Int64()
var data []byte data := make([]byte, 32)
if len(closure.Args) >= int(offset+32) { if len(closure.Args) >= int(offset) {
data = closure.Args[offset : offset+32] l := int64(math.Min(float64(offset+32), float64(len(closure.Args))))
} else {
data = []byte{0} copy(data, closure.Args[offset:l])
} }
vm.Printf(" => 0x%x", data)
stack.Push(ethutil.BigD(data)) stack.Push(ethutil.BigD(data))
case CALLDATASIZE: case CALLDATASIZE:
stack.Push(big.NewInt(int64(len(closure.Args)))) l := int64(len(closure.Args))
stack.Push(big.NewInt(l))
vm.Printf(" => %d", l)
case CALLDATACOPY: case CALLDATACOPY:
panic("not implemented")
case CODESIZE: case CODESIZE:
stack.Push(big.NewInt(int64(len(closure.Script))))
case CODECOPY: case CODECOPY:
var ( var (
size = int64(len(closure.Script)) size = int64(len(closure.Script))
@ -408,7 +465,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case TIMESTAMP: case TIMESTAMP:
stack.Push(big.NewInt(vm.vars.Time)) stack.Push(big.NewInt(vm.vars.Time))
case NUMBER: case NUMBER:
stack.Push(big.NewInt(int64(vm.vars.BlockNumber))) stack.Push(vm.vars.BlockNumber)
case DIFFICULTY: case DIFFICULTY:
stack.Push(vm.vars.Diff) stack.Push(vm.vars.Diff)
case GASLIMIT: case GASLIMIT:
@ -426,12 +483,16 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
pc.Add(pc, a.Sub(a, big.NewInt(1))) pc.Add(pc, a.Sub(a, big.NewInt(1)))
step += int(op) - int(PUSH1) + 1 step += int(op) - int(PUSH1) + 1
vm.Printf(" => 0x%x", data.Bytes())
case POP: case POP:
require(1) require(1)
stack.Pop() stack.Pop()
case DUP: case DUP:
require(1) require(1)
stack.Push(stack.Peek()) stack.Push(stack.Peek())
vm.Printf(" => 0x%x", stack.Peek().Bytes())
case SWAP: case SWAP:
require(2) require(2)
x, y := stack.Popn() x, y := stack.Popn()
@ -446,38 +507,53 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
// Pop value of the stack // Pop value of the stack
val, mStart := stack.Popn() val, mStart := stack.Popn()
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
vm.Printf(" => 0x%x", val)
case MSTORE8: case MSTORE8:
require(2) require(2)
val, mStart := stack.Popn() val, mStart := stack.Popn()
base.And(val, new(big.Int).SetInt64(0xff)) base.And(val, new(big.Int).SetInt64(0xff))
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
vm.Printf(" => 0x%x", val)
case SLOAD: case SLOAD:
require(1) require(1)
loc := stack.Pop() loc := stack.Pop()
val := closure.GetMem(loc) val := closure.GetMem(loc)
//fmt.Println("get", val.BigInt(), "@", loc)
stack.Push(val.BigInt()) stack.Push(val.BigInt())
vm.Printf(" {} 0x%x", val)
case SSTORE: case SSTORE:
require(2) require(2)
val, loc := stack.Popn() val, loc := stack.Popn()
//fmt.Println("storing", val, "@", loc)
// FIXME This should be handled in the Trie it self
if val.Cmp(big.NewInt(0)) != 0 {
closure.SetStorage(loc, ethutil.NewValue(val)) closure.SetStorage(loc, ethutil.NewValue(val))
}
// Add the change to manifest // Add the change to manifest
vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val) vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
vm.Printf(" {0x%x} 0x%x", loc, val)
case JUMP: case JUMP:
require(1) require(1)
pc = stack.Pop() pc = stack.Pop()
// Reduce pc by one because of the increment that's at the end of this for loop // Reduce pc by one because of the increment that's at the end of this for loop
//pc.Sub(pc, ethutil.Big1) vm.Printf(" ~> %v", pc).Endl()
continue continue
case JUMPI: case JUMPI:
require(2) require(2)
cond, pos := stack.Popn() cond, pos := stack.Popn()
if cond.Cmp(ethutil.BigTrue) == 0 { if cond.Cmp(ethutil.BigTrue) >= 0 {
pc = pos pc = pos
//pc.Sub(pc, ethutil.Big1)
vm.Printf(" ~> %v (t)", pc).Endl()
continue continue
} else {
vm.Printf(" (f)")
} }
case PC: case PC:
stack.Push(pc) stack.Push(pc)
@ -494,7 +570,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
// Snapshot the current stack so we are able to // Snapshot the current stack so we are able to
// revert back to it later. // revert back to it later.
snapshot := vm.state.Snapshot() snapshot := vm.state.Copy()
// Generate a new address // Generate a new address
addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N()) addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N())
@ -522,19 +598,18 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
// Revert the state as it was before. // Revert the state as it was before.
vm.state.Revert(snapshot) vm.state.Set(snapshot)
} else { } else {
stack.Push(ethutil.BigD(addr)) stack.Push(ethutil.BigD(addr))
vm.state.UpdateStateObject(contract)
} }
case CALL: case CALL:
// TODO RE-WRITE
require(7) require(7)
// Closure addr
addr := stack.Pop() vm.Endl()
gas := stack.Pop()
// Pop gas and value of the stack. // Pop gas and value of the stack.
gas, value := stack.Popn() value, addr := stack.Popn()
// Pop input size and offset // Pop input size and offset
inSize, inOffset := stack.Popn() inSize, inOffset := stack.Popn()
// Pop return size and offset // Pop return size and offset
@ -543,58 +618,59 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
// Get the arguments from the memory // Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64()) args := mem.Get(inOffset.Int64(), inSize.Int64())
snapshot := vm.state.Snapshot()
closure.object.Nonce += 1
if closure.object.Amount.Cmp(value) < 0 { if closure.object.Amount.Cmp(value) < 0 {
ethutil.Config.Log.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) ethutil.Config.Log.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
} else { } else {
// Fetch the contract which will serve as the closure body snapshot := vm.state.Copy()
contract := vm.state.GetStateObject(addr.Bytes())
if contract != nil { stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
closure.object.SubAmount(value)
// Add the value to the state object // Add the value to the state object
contract.AddAmount(value) stateObject.AddAmount(value)
// Create a new callable closure // Create a new callable closure
closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price) closure := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price)
// Executer the closure and get the return value (if any) // Executer the closure and get the return value (if any)
ret, _, err := closure.Call(vm, args, hook) ret, _, err := closure.Call(vm, args, hook)
if err != nil { if err != nil {
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
// Reset the changes applied this object
vm.state.Revert(snapshot) ethutil.Config.Log.Debugf("Closure execution failed. %v\n", err)
vm.err = err
vm.state.Set(snapshot)
} else { } else {
stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
vm.state.UpdateStateObject(contract)
mem.Set(retOffset.Int64(), retSize.Int64(), ret) mem.Set(retOffset.Int64(), retSize.Int64(), ret)
} }
} else {
ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes())
stack.Push(ethutil.BigFalse)
}
} }
case RETURN: case RETURN:
require(2) require(2)
size, offset := stack.Popn() size, offset := stack.Popn()
ret := mem.Get(offset.Int64(), size.Int64()) ret := mem.Get(offset.Int64(), size.Int64())
vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
return closure.Return(ret), nil return closure.Return(ret), nil
case SUICIDE: case SUICIDE:
require(1) require(1)
receiver := vm.state.GetAccount(stack.Pop().Bytes()) receiver := vm.state.GetAccount(stack.Pop().Bytes())
receiver.AddAmount(closure.object.Amount) receiver.AddAmount(closure.object.Amount)
vm.state.UpdateStateObject(receiver)
closure.object.state.Purge() trie := closure.object.state.trie
trie.NewIterator().Each(func(key string, v *ethutil.Value) {
trie.Delete(key)
})
fallthrough fallthrough
case STOP: // Stop the closure case STOP: // Stop the closure
vm.Printf(" (g) %v", closure.Gas).Endl()
return closure.Return(nil), nil return closure.Return(nil), nil
default: default:
ethutil.Config.Log.Debugf("Invalid opcode %x\n", op) ethutil.Config.Log.Debugf("Invalid opcode %x\n", op)
@ -604,6 +680,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
pc.Add(pc, ethutil.Big1) pc.Add(pc, ethutil.Big1)
vm.Endl()
if hook != nil { if hook != nil {
if !hook(prevStep, op, mem, stack, closure.Object()) { if !hook(prevStep, op, mem, stack, closure.Object()) {
return nil, nil return nil, nil

View File

@ -149,7 +149,9 @@ func (s *Ethereum) IsUpToDate() bool {
}) })
return upToDate return upToDate
} }
func (s *Ethereum) PushPeer(peer *Peer) {
s.peers.PushBack(peer)
}
func (s *Ethereum) IsListening() bool { func (s *Ethereum) IsListening() bool {
return s.listening return s.listening
} }
@ -159,14 +161,11 @@ func (s *Ethereum) AddPeer(conn net.Conn) {
if peer != nil { if peer != nil {
if s.peers.Len() < s.MaxPeers { if s.peers.Len() < s.MaxPeers {
s.peers.PushBack(peer)
peer.Start() peer.Start()
} else { } else {
ethutil.Config.Log.Debugf("[SERV] Max connected peers reached. Not adding incoming peer.") ethutil.Config.Log.Debugf("[SERV] Max connected peers reached. Not adding incoming peer.")
} }
} }
s.reactor.Post("peerList", s.peers)
} }
func (s *Ethereum) ProcessPeerList(addrs []string) { func (s *Ethereum) ProcessPeerList(addrs []string) {
@ -233,12 +232,7 @@ func (s *Ethereum) ConnectToPeer(addr string) error {
return nil return nil
} }
peer := NewOutboundPeer(addr, s, s.serverCaps) NewOutboundPeer(addr, s, s.serverCaps)
s.peers.PushBack(peer)
ethutil.Config.Log.Infof("[SERV] Adding peer (%s) %d / %d\n", addr, s.peers.Len(), s.MaxPeers)
s.reactor.Post("peerList", s.peers)
} }
return nil return nil

View File

@ -154,6 +154,8 @@ func (self *Miner) mineNewBlock() {
// Accumulate the rewards included for this block // Accumulate the rewards included for this block
stateManager.AccumelateRewards(self.block.State(), self.block) stateManager.AccumelateRewards(self.block.State(), self.block)
self.block.State().Update()
ethutil.Config.Log.Infoln("[MINER] Mining on block. Includes", len(self.txs), "transactions") ethutil.Config.Log.Infoln("[MINER] Mining on block. Includes", len(self.txs), "transactions")
// Find a valid nonce // Find a valid nonce

View File

@ -170,11 +170,6 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, sc
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script) tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script)
} else { } else {
// Just in case it was submitted as a 0x prefixed string
if len(scriptStr) > 0 && scriptStr[0:2] == "0x" {
scriptStr = scriptStr[2:len(scriptStr)]
}
data := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { data := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
slice := strings.Split(s, "\n") slice := strings.Split(s, "\n")
for _, dataItem := range slice { for _, dataItem := range slice {

View File

@ -104,16 +104,17 @@ type PTx struct {
func NewPTx(tx *ethchain.Transaction) *PTx { func NewPTx(tx *ethchain.Transaction) *PTx {
hash := hex.EncodeToString(tx.Hash()) hash := hex.EncodeToString(tx.Hash())
receiver := hex.EncodeToString(tx.Recipient) receiver := hex.EncodeToString(tx.Recipient)
if receiver == "0000000000000000000000000000000000000000" {
if receiver == "" {
receiver = hex.EncodeToString(tx.CreationAddress()) receiver = hex.EncodeToString(tx.CreationAddress())
} }
sender := hex.EncodeToString(tx.Sender()) sender := hex.EncodeToString(tx.Sender())
createsContract := tx.CreatesContract() createsContract := tx.CreatesContract()
data := string(tx.Data) var data string
if tx.CreatesContract() { if tx.CreatesContract() {
data = strings.Join(ethchain.Disassemble(tx.Data), "\n") data = strings.Join(ethchain.Disassemble(tx.Data), "\n")
} else {
data = hex.EncodeToString(tx.Data)
} }
return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)} return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)}

View File

@ -5,6 +5,7 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math/big" "math/big"
"strings"
) )
// Number to bytes // Number to bytes
@ -91,7 +92,7 @@ func IsHex(str string) bool {
} }
func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) { func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) {
if len(str) > 1 && str[0:2] == "0x" { if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") {
ret = FromHex(str[2:]) ret = FromHex(str[2:])
} else { } else {
ret = cb(str) ret = cb(str)

View File

@ -75,7 +75,7 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s
if Config == nil { if Config == nil {
path := ApplicationFolder(base) path := ApplicationFolder(base)
Config = &config{ExecPath: path, Debug: true, Ver: "0.5.13"} Config = &config{ExecPath: path, Debug: true, Ver: "0.5.14"}
Config.conf = g Config.conf = g
Config.Identifier = id Config.Identifier = id
Config.Log = NewLogger(logTypes, LogLevelDebug) Config.Log = NewLogger(logTypes, LogLevelDebug)

View File

@ -173,12 +173,21 @@ func TestTriePurge(t *testing.T) {
func TestTrieIt(t *testing.T) { func TestTrieIt(t *testing.T) {
_, trie := New() _, trie := New()
trie.Update("c", LONG_WORD)
trie.Update("ca", LONG_WORD)
trie.Update("cat", LONG_WORD)
it := trie.NewIterator() data := [][]string{
it.Each(func(key string, node *Value) { {"do", "verb"},
fmt.Println(key, ":", node.Str()) {"ether", "wookiedoo"},
}) {"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
}
for _, item := range data {
trie.Update(item[0], item[1])
}
fmt.Printf("root %x", trie.Root)
} }

View File

@ -114,6 +114,8 @@ func (val *Value) Str() string {
func (val *Value) Bytes() []byte { func (val *Value) Bytes() []byte {
if a, ok := val.Val.([]byte); ok { if a, ok := val.Val.([]byte); ok {
return a return a
} else if s, ok := val.Val.(byte); ok {
return []byte{s}
} }
return []byte{} return []byte{}

34
peer.go
View File

@ -2,6 +2,7 @@ package eth
import ( import (
"bytes" "bytes"
"container/list"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
@ -124,6 +125,7 @@ type Peer struct {
port uint16 port uint16
caps Caps caps Caps
// This peer's public key
pubkey []byte pubkey []byte
// Indicated whether the node is catching up or not // Indicated whether the node is catching up or not
@ -171,7 +173,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
// Set up the connection in another goroutine so we don't block the main thread // Set up the connection in another goroutine so we don't block the main thread
go func() { go func() {
conn, err := net.DialTimeout("tcp", addr, 30*time.Second) conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
if err != nil { if err != nil {
ethutil.Config.Log.Debugln("Connection to peer failed", err) ethutil.Config.Log.Debugln("Connection to peer failed", err)
@ -614,6 +616,30 @@ func (p *Peer) pushPeers() {
func (p *Peer) handleHandshake(msg *ethwire.Msg) { func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := msg.Data c := msg.Data
// Set pubkey
p.pubkey = c.Get(5).Bytes()
if p.pubkey == nil {
//ethutil.Config.Log.Debugln("Pubkey required, not supplied in handshake.")
p.Stop()
return
}
usedPub := 0
// This peer is already added to the peerlist so we expect to find a double pubkey at least once
eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
if bytes.Compare(p.pubkey, peer.pubkey) == 0 {
usedPub++
}
})
if usedPub > 0 {
//ethutil.Config.Log.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey)
p.Stop()
return
}
if c.Get(0).Uint() != ProtocolVersion { if c.Get(0).Uint() != ProtocolVersion {
ethutil.Config.Log.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint()) ethutil.Config.Log.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint())
p.Stop() p.Stop()
@ -625,7 +651,6 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
// If this is an inbound connection send an ack back // If this is an inbound connection send an ack back
if p.inbound { if p.inbound {
p.pubkey = c.Get(5).Bytes()
p.port = uint16(c.Get(4).Uint()) p.port = uint16(c.Get(4).Uint())
// Self connect detection // Self connect detection
@ -647,6 +672,11 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
p.SetVersion(c.Get(2).Str()) p.SetVersion(c.Get(2).Str())
} }
p.ethereum.PushPeer(p)
p.ethereum.reactor.Post("peerList", p.ethereum.Peers())
ethutil.Config.Log.Infof("[SERV] Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers)
// Catch up with the connected peer // Catch up with the connected peer
if !p.ethereum.IsUpToDate() { if !p.ethereum.IsUpToDate() {
ethutil.Config.Log.Debugln("Already syncing up with a peer; sleeping") ethutil.Config.Log.Debugln("Already syncing up with a peer; sleeping")