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 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
individual package for more information.

View File

@ -28,7 +28,7 @@ func Disassemble(script []byte) (asm []string) {
if len(data) == 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))
}

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 (
"fmt"
_ "github.com/ethereum/eth-go/ethutil"
"math"
"math/big"
)
@ -118,7 +118,13 @@ func (m *Memory) Resize(size uint64) {
}
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 {

View File

@ -13,8 +13,6 @@ import (
type State struct {
// The trie for this structure
trie *ethutil.Trie
// Nested states
states map[string]*State
stateObjects map[string]*StateObject
@ -23,7 +21,7 @@ type State struct {
// Create a new state from a given trie
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
@ -38,12 +36,16 @@ func (s *State) Reset() {
stateObject.state.Reset()
}
s.Empty()
}
// Syncs the trie and all siblings
func (s *State) Sync() {
// Sync all nested states
for _, stateObject := range s.stateObjects {
s.UpdateStateObject(stateObject)
if stateObject.state == nil {
continue
}
@ -52,6 +54,18 @@ func (s *State) 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.
@ -64,6 +78,12 @@ func (s *State) EachStorage(cb ethutil.EachCallback) {
it.Each(cb)
}
func (self *State) ResetStateObject(stateObject *StateObject) {
delete(self.stateObjects, string(stateObject.Address()))
stateObject.state.Reset()
}
func (self *State) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address()
@ -98,13 +118,21 @@ func (self *State) GetStateObject(addr []byte) *StateObject {
func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
stateObject := self.GetStateObject(addr)
if stateObject == nil {
stateObject = NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject
stateObject = self.NewStateObject(addr)
}
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 {
return self.GetOrNewStateObject(addr)
}
@ -126,13 +154,10 @@ func (self *State) Copy() *State {
return nil
}
func (s *State) Snapshot() *State {
return s.Copy()
}
func (s *State) Revert(snapshot *State) {
s.trie = snapshot.trie
s.states = snapshot.states
func (self *State) Set(state *State) {
//s.trie = snapshot.trie
//s.stateObjects = snapshot.stateObjects
self = state
}
func (s *State) Put(key, object []byte) {

View File

@ -127,6 +127,9 @@ done:
// Notify all subscribers
self.Ethereum.Reactor().Post("newTx:post", tx)
// Update the state with pending changes
state.Update()
txGas.Sub(txGas, st.gas)
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative}
@ -135,8 +138,6 @@ done:
handled = append(handled, tx)
}
fmt.Println("################# MADE\n", receipts, "\n############################")
parent.GasUsed = totalUsedGas
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
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
sm.mutex.Lock()
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 {
return ParentError(block.PrevHash)
}
fmt.Println(block.Receipts())
coinbase := state.GetOrNewStateObject(block.Coinbase)
coinbase.SetGasPool(block.CalcGasLimit(parent))
// Process the transactions on to current block
//sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions())
sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions())
receipts, _, _, _ := 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
if err := sm.ValidateBlock(block); err != nil {
if err = sm.ValidateBlock(block); err != nil {
fmt.Println("[SM] Error validating block:", err)
return err
}
// I'm not sure, but I don't know if there should be thrown
// 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)
return err
}
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
@ -270,10 +281,12 @@ func (sm *StateManager) ValidateBlock(block *Block) error {
return ValidationError("Block timestamp less then prev block %v", diff)
}
/* XXX
// New blocks must be within the 15 minute range of the last block.
if diff > int64(15*time.Minute) {
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
if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) {

View File

@ -4,8 +4,15 @@ import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"strings"
)
type Code []byte
func (self Code) String() string {
return strings.Join(Disassemble(self), " ")
}
type StateObject struct {
// Address of the object
address []byte
@ -15,8 +22,8 @@ type StateObject struct {
Nonce uint64
// Contract related attributes
state *State
script []byte
initScript []byte
script Code
initScript Code
// Total gas pool is the total amount of gas currently
// left if this object is the coinbase. Gas is directly
@ -30,12 +37,9 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
if tx.IsContract() {
addr := tx.CreationAddress()
value := tx.Value
contract := NewContract(addr, value, ZeroHash256)
contract := state.NewStateObject(addr)
contract.initScript = tx.Data
state.UpdateStateObject(contract)
contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, ""))
return contract
}
@ -44,7 +48,7 @@ func MakeContract(tx *Transaction, state *State) *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 {
@ -120,13 +124,13 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
func (c *StateObject) AddAmount(amount *big.Int) {
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) {
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) {
@ -172,6 +176,26 @@ func (self *StateObject) RefundGas(gas, price *big.Int) {
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 {
stCopy := &StateObject{}
stCopy.address = make([]byte, len(self.address))
@ -190,6 +214,7 @@ func (self *StateObject) Copy() *StateObject {
return stCopy
}
*/
// Returns the address of the contract/account
func (c *StateObject) Address() []byte {
@ -197,12 +222,12 @@ func (c *StateObject) Address() []byte {
}
// Returns the main script body
func (c *StateObject) Script() []byte {
func (c *StateObject) Script() Code {
return c.script
}
// Returns the initialization script
func (c *StateObject) Init() []byte {
func (c *StateObject) Init() Code {
return c.initScript
}

View File

@ -23,9 +23,11 @@ import (
* 6) Derive new state root
*/
type StateTransition struct {
coinbase []byte
coinbase, receiver []byte
tx *Transaction
gas *big.Int
gas, gasPrice *big.Int
value *big.Int
data []byte
state *State
block *Block
@ -33,7 +35,7 @@ type StateTransition struct {
}
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 {
@ -53,7 +55,7 @@ func (self *StateTransition) Sender() *StateObject {
return self.sen
}
func (self *StateTransition) Receiver() *StateObject {
if self.tx.CreatesContract() {
if self.tx != nil && self.tx.CreatesContract() {
return nil
}
@ -67,13 +69,8 @@ func (self *StateTransition) Receiver() *StateObject {
func (self *StateTransition) 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 (self *StateTransition) UseGas(amount *big.Int) error {
@ -94,7 +91,7 @@ func (self *StateTransition) BuyGas() 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(), self.tx.Value)
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Amount)
}
coinbase := self.Coinbase()
@ -102,7 +99,6 @@ func (self *StateTransition) BuyGas() error {
if err != nil {
return err
}
//self.state.UpdateStateObject(coinbase)
self.AddGas(self.tx.Gas)
sender.SubAmount(self.tx.GasValue())
@ -119,22 +115,10 @@ func (self *StateTransition) RefundGas() {
sender.AddAmount(remaining)
}
func (self *StateTransition) TransitionState() (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)
}
}()
*/
func (self *StateTransition) preCheck() (err error) {
var (
tx = self.tx
sender = self.Sender()
receiver *StateObject
)
// Make sure this transaction's nonce is correct
@ -147,38 +131,49 @@ func (self *StateTransition) TransitionState() (err error) {
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() {
self.RefundGas()
if sender != nil {
self.state.UpdateStateObject(sender)
if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r)
err = fmt.Errorf("state transition err %v", r)
}
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
sender.Nonce += 1
// Get the receiver (TODO fix this, if coinbase is the receiver we need to save/retrieve)
receiver = self.Receiver()
// Transaction gas
if err = self.UseGas(GasTx); err != nil {
return err
return
}
// Pay data gas
dataPrice := big.NewInt(int64(len(tx.Data)))
dataPrice := big.NewInt(int64(len(self.data)))
dataPrice.Mul(dataPrice, GasData)
if err = self.UseGas(dataPrice); err != nil {
return err
return
}
// 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
receiver = self.MakeStateObject(self.state, tx)
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
if err = self.transferValue(sender, receiver); err != nil {
return err
return
}
// Process the init code and create 'valid' contract
if tx.CreatesContract() {
if IsContractAddr(self.receiver) {
// Evaluate the initialization script
// and use the return value as the
// script section for the state object.
//script, gas, err = sm.Eval(state, contract.Init(), contract, tx, block)
code, err := self.Eval(receiver.Init(), receiver)
if err != nil {
return fmt.Errorf("Error during init script run %v", err)
self.data = nil
code, err, deepErr := self.Eval(receiver.Init(), receiver)
if err != nil || deepErr {
self.state.ResetStateObject(receiver)
return fmt.Errorf("Error during init script run %v (deepErr = %v)", err, deepErr)
}
receiver.script = code
} else {
if len(receiver.Script()) > 0 {
_, err := self.Eval(receiver.Script(), receiver)
var deepErr bool
_, err, deepErr = self.Eval(receiver.Script(), receiver)
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 {
if sender.Amount.Cmp(self.tx.Value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount)
if sender.Amount.Cmp(self.value) < 0 {
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
sender.SubAmount(self.tx.Value)
sender.SubAmount(self.value)
// 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
}
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 (
tx = self.tx
block = self.block
initiator = self.Sender()
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{
Origin: initiator.Address(),
BlockNumber: block.BlockInfo().Number,
Block: block,
BlockNumber: block.Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
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
}

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

View File

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

View File

@ -1,12 +1,9 @@
package ethchain
import (
_ "bytes"
"fmt"
"github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go"
"math"
_ "math"
"math/big"
)
@ -45,11 +42,18 @@ type Vm struct {
state *State
stateManager *StateManager
Verbose bool
logStr string
err error
}
type RuntimeVars struct {
Origin []byte
BlockNumber uint64
Block *Block
BlockNumber *big.Int
PrevHash []byte
Coinbase []byte
Time int64
@ -58,6 +62,23 @@ type RuntimeVars struct {
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 {
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) {
// Recover from any require exception
defer func() {
if r := recover(); r != nil /*&& isRequireError*/ {
if r := recover(); r != nil {
ret = closure.Return(nil)
err = fmt.Errorf("%v", r)
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
mem := &Memory{}
@ -95,10 +116,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
step := 0
prevStep := 0
if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("# op\n")
}
for {
prevStep = step
// 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)
// Get the opcode (it must be an opcode!)
op := OpCode(val.Uint())
if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
}
gas := new(big.Int)
addStepGasUsage := func(amount *big.Int) {
if amount.Cmp(ethutil.Big0) >= 0 {
gas.Add(gas, amount)
}
}
addStepGasUsage(GasStep)
var newMemSize uint64 = 0
switch op {
case STOP:
gas.Set(ethutil.Big0)
case SUICIDE:
gas.Set(ethutil.Big0)
case SLOAD:
gas.Set(GasSLoad)
case SSTORE:
@ -169,7 +187,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case CALL:
require(7)
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()
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) {
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)
switch op {
@ -204,29 +227,41 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case ADD:
require(2)
x, y := stack.Popn()
// (x + y) % 2 ** 256
base.Add(x, y)
vm.Printf(" %v + %v", y, x)
base.Add(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case SUB:
require(2)
x, y := stack.Popn()
// (x - y) % 2 ** 256
base.Sub(x, y)
vm.Printf(" %v - %v", y, x)
base.Sub(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case MUL:
require(2)
x, y := stack.Popn()
// (x * y) % 2 ** 256
base.Mul(x, y)
vm.Printf(" %v * %v", y, x)
base.Mul(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case DIV:
require(2)
x, y := stack.Popn()
// floor(x / y)
base.Div(x, y)
vm.Printf(" %v / %v", y, x)
base.Div(y, x)
vm.Printf(" = %v", base)
// Pop result back on the stack
stack.Push(base)
case SDIV:
@ -249,7 +284,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case MOD:
require(2)
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)
case SMOD:
require(2)
@ -271,7 +311,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case EXP:
require(2)
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)
case NEG:
@ -280,7 +325,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
stack.Push(base)
case LT:
require(2)
x, y := stack.Popn()
y, x := stack.Popn()
vm.Printf(" %v < %v", x, y)
// x < y
if x.Cmp(y) < 0 {
stack.Push(ethutil.BigTrue)
@ -289,7 +335,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
}
case GT:
require(2)
x, y := stack.Popn()
y, x := stack.Popn()
vm.Printf(" %v > %v", x, y)
// x > y
if x.Cmp(y) > 0 {
stack.Push(ethutil.BigTrue)
@ -299,6 +347,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case EQ:
require(2)
x, y := stack.Popn()
vm.Printf(" %v == %v", y, x)
// x == y
if x.Cmp(y) == 0 {
stack.Push(ethutil.BigTrue)
@ -318,24 +368,21 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case AND:
require(2)
x, y := stack.Popn()
if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) {
stack.Push(ethutil.BigTrue)
} else {
stack.Push(ethutil.BigFalse)
}
vm.Printf(" %v & %v", y, x)
stack.Push(base.And(y, x))
case OR:
require(2)
x, y := stack.Popn()
if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) {
stack.Push(ethutil.BigTrue)
} else {
stack.Push(ethutil.BigFalse)
}
vm.Printf(" %v | %v", y, x)
stack.Push(base.Or(y, x))
case XOR:
require(2)
x, y := stack.Popn()
stack.Push(base.Xor(x, y))
vm.Printf(" %v ^ %v", y, x)
stack.Push(base.Xor(y, x))
case BYTE:
require(2)
val, th := stack.Popn()
@ -360,25 +407,35 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case ORIGIN:
stack.Push(ethutil.BigD(vm.vars.Origin))
case CALLER:
stack.Push(ethutil.BigD(closure.caller.Address()))
caller := closure.caller.Address()
stack.Push(ethutil.BigD(caller))
vm.Printf(" => %x", caller)
case CALLVALUE:
stack.Push(vm.vars.Value)
case CALLDATALOAD:
require(1)
offset := stack.Pop().Int64()
var data []byte
if len(closure.Args) >= int(offset+32) {
data = closure.Args[offset : offset+32]
} else {
data = []byte{0}
data := make([]byte, 32)
if len(closure.Args) >= int(offset) {
l := int64(math.Min(float64(offset+32), float64(len(closure.Args))))
copy(data, closure.Args[offset:l])
}
vm.Printf(" => 0x%x", data)
stack.Push(ethutil.BigD(data))
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:
panic("not implemented")
case CODESIZE:
stack.Push(big.NewInt(int64(len(closure.Script))))
case CODECOPY:
var (
size = int64(len(closure.Script))
@ -408,7 +465,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case TIMESTAMP:
stack.Push(big.NewInt(vm.vars.Time))
case NUMBER:
stack.Push(big.NewInt(int64(vm.vars.BlockNumber)))
stack.Push(vm.vars.BlockNumber)
case DIFFICULTY:
stack.Push(vm.vars.Diff)
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)))
step += int(op) - int(PUSH1) + 1
vm.Printf(" => 0x%x", data.Bytes())
case POP:
require(1)
stack.Pop()
case DUP:
require(1)
stack.Push(stack.Peek())
vm.Printf(" => 0x%x", stack.Peek().Bytes())
case SWAP:
require(2)
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
val, mStart := stack.Popn()
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
vm.Printf(" => 0x%x", val)
case MSTORE8:
require(2)
val, mStart := stack.Popn()
base.And(val, new(big.Int).SetInt64(0xff))
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
vm.Printf(" => 0x%x", val)
case SLOAD:
require(1)
loc := stack.Pop()
val := closure.GetMem(loc)
//fmt.Println("get", val.BigInt(), "@", loc)
stack.Push(val.BigInt())
vm.Printf(" {} 0x%x", val)
case SSTORE:
require(2)
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))
}
// Add the change to manifest
vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
vm.Printf(" {0x%x} 0x%x", loc, val)
case JUMP:
require(1)
pc = stack.Pop()
// 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
case JUMPI:
require(2)
cond, pos := stack.Popn()
if cond.Cmp(ethutil.BigTrue) == 0 {
if cond.Cmp(ethutil.BigTrue) >= 0 {
pc = pos
//pc.Sub(pc, ethutil.Big1)
vm.Printf(" ~> %v (t)", pc).Endl()
continue
} else {
vm.Printf(" (f)")
}
case 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
// revert back to it later.
snapshot := vm.state.Snapshot()
snapshot := vm.state.Copy()
// Generate a new address
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)
// Revert the state as it was before.
vm.state.Revert(snapshot)
vm.state.Set(snapshot)
} else {
stack.Push(ethutil.BigD(addr))
vm.state.UpdateStateObject(contract)
}
case CALL:
// TODO RE-WRITE
require(7)
// Closure addr
addr := stack.Pop()
vm.Endl()
gas := stack.Pop()
// Pop gas and value of the stack.
gas, value := stack.Popn()
value, addr := stack.Popn()
// Pop input size and offset
inSize, inOffset := stack.Popn()
// 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
args := mem.Get(inOffset.Int64(), inSize.Int64())
snapshot := vm.state.Snapshot()
closure.object.Nonce += 1
if closure.object.Amount.Cmp(value) < 0 {
ethutil.Config.Log.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
stack.Push(ethutil.BigFalse)
} else {
// Fetch the contract which will serve as the closure body
contract := vm.state.GetStateObject(addr.Bytes())
snapshot := vm.state.Copy()
if contract != nil {
stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
closure.object.SubAmount(value)
// Add the value to the state object
contract.AddAmount(value)
stateObject.AddAmount(value)
// 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)
ret, _, err := closure.Call(vm, args, hook)
if err != nil {
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 {
stack.Push(ethutil.BigTrue)
vm.state.UpdateStateObject(contract)
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:
require(2)
size, offset := stack.Popn()
ret := mem.Get(offset.Int64(), size.Int64())
vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
return closure.Return(ret), nil
case SUICIDE:
require(1)
receiver := vm.state.GetAccount(stack.Pop().Bytes())
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
case STOP: // Stop the closure
vm.Printf(" (g) %v", closure.Gas).Endl()
return closure.Return(nil), nil
default:
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)
vm.Endl()
if hook != nil {
if !hook(prevStep, op, mem, stack, closure.Object()) {
return nil, nil

View File

@ -149,7 +149,9 @@ func (s *Ethereum) IsUpToDate() bool {
})
return upToDate
}
func (s *Ethereum) PushPeer(peer *Peer) {
s.peers.PushBack(peer)
}
func (s *Ethereum) IsListening() bool {
return s.listening
}
@ -159,14 +161,11 @@ func (s *Ethereum) AddPeer(conn net.Conn) {
if peer != nil {
if s.peers.Len() < s.MaxPeers {
s.peers.PushBack(peer)
peer.Start()
} else {
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) {
@ -233,12 +232,7 @@ func (s *Ethereum) ConnectToPeer(addr string) error {
return nil
}
peer := 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)
NewOutboundPeer(addr, s, s.serverCaps)
}
return nil

View File

@ -154,6 +154,8 @@ func (self *Miner) mineNewBlock() {
// Accumulate the rewards included for this 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")
// 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)
} 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) {
slice := strings.Split(s, "\n")
for _, dataItem := range slice {

View File

@ -104,16 +104,17 @@ type PTx struct {
func NewPTx(tx *ethchain.Transaction) *PTx {
hash := hex.EncodeToString(tx.Hash())
receiver := hex.EncodeToString(tx.Recipient)
if receiver == "" {
if receiver == "0000000000000000000000000000000000000000" {
receiver = hex.EncodeToString(tx.CreationAddress())
}
sender := hex.EncodeToString(tx.Sender())
createsContract := tx.CreatesContract()
data := string(tx.Data)
var data string
if tx.CreatesContract() {
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)}

View File

@ -5,6 +5,7 @@ import (
"encoding/binary"
"fmt"
"math/big"
"strings"
)
// Number to bytes
@ -91,7 +92,7 @@ func IsHex(str string) bool {
}
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:])
} else {
ret = cb(str)

View File

@ -75,7 +75,7 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s
if Config == nil {
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.Identifier = id
Config.Log = NewLogger(logTypes, LogLevelDebug)

View File

@ -173,12 +173,21 @@ func TestTriePurge(t *testing.T) {
func TestTrieIt(t *testing.T) {
_, trie := New()
trie.Update("c", LONG_WORD)
trie.Update("ca", LONG_WORD)
trie.Update("cat", LONG_WORD)
it := trie.NewIterator()
it.Each(func(key string, node *Value) {
fmt.Println(key, ":", node.Str())
})
data := [][]string{
{"do", "verb"},
{"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 {
if a, ok := val.Val.([]byte); ok {
return a
} else if s, ok := val.Val.(byte); ok {
return []byte{s}
}
return []byte{}

34
peer.go
View File

@ -2,6 +2,7 @@ package eth
import (
"bytes"
"container/list"
"fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
@ -124,6 +125,7 @@ type Peer struct {
port uint16
caps Caps
// This peer's public key
pubkey []byte
// 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
go func() {
conn, err := net.DialTimeout("tcp", addr, 30*time.Second)
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
if err != nil {
ethutil.Config.Log.Debugln("Connection to peer failed", err)
@ -614,6 +616,30 @@ func (p *Peer) pushPeers() {
func (p *Peer) handleHandshake(msg *ethwire.Msg) {
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 {
ethutil.Config.Log.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint())
p.Stop()
@ -625,7 +651,6 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
// If this is an inbound connection send an ack back
if p.inbound {
p.pubkey = c.Get(5).Bytes()
p.port = uint16(c.Get(4).Uint())
// Self connect detection
@ -647,6 +672,11 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
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
if !p.ethereum.IsUpToDate() {
ethutil.Config.Log.Debugln("Already syncing up with a peer; sleeping")