merge upstream

This commit is contained in:
zelig 2014-07-14 18:50:06 +01:00
commit 3d5db7288f
24 changed files with 499 additions and 296 deletions

View File

@ -24,6 +24,10 @@ func Disassemble(script []byte) (asm []string) {
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
pc.Add(pc, ethutil.Big1) pc.Add(pc, ethutil.Big1)
a := int64(op) - int64(PUSH1) + 1 a := int64(op) - int64(PUSH1) + 1
if int(pc.Int64()+a) > len(script) {
return nil
}
data := script[pc.Int64() : pc.Int64()+a] data := script[pc.Int64() : pc.Int64()+a]
if len(data) == 0 { if len(data) == 0 {
data = []byte{0} data = []byte{0}

View File

@ -294,7 +294,6 @@ func (bc *BlockChain) setLastBlock() {
bc.LastBlockHash = block.Hash() bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = block.Number.Uint64() bc.LastBlockNumber = block.Number.Uint64()
chainlogger.Infof("Last known block height #%d\n", bc.LastBlockNumber)
} else { } else {
AddTestNetFunds(bc.genesisBlock) AddTestNetFunds(bc.genesisBlock)
@ -309,7 +308,7 @@ func (bc *BlockChain) setLastBlock() {
// Set the last know difficulty (might be 0x0 as initial value, Genesis) // Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
chainlogger.Infof("Last block: %x\n", bc.CurrentBlock.Hash()) chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
} }
func (bc *BlockChain) SetTotalDifficulty(td *big.Int) { func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {

View File

@ -84,13 +84,7 @@ func (c *Closure) Call(vm *Vm, args []byte) ([]byte, *big.Int, error) {
func (c *Closure) Return(ret []byte) []byte { func (c *Closure) Return(ret []byte) []byte {
// Return the remaining gas to the caller // Return the remaining gas to the caller
// If no caller is present return it to c.caller.ReturnGas(c.Gas, c.Price, c.State)
// the origin (i.e. contract or tx)
if c.caller != nil {
c.caller.ReturnGas(c.Gas, c.Price, c.State)
} else {
c.object.ReturnGas(c.Gas, c.Price, c.State)
}
return ret return ret
} }

View File

@ -3,7 +3,6 @@ package ethchain
import ( import (
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/sha3" "github.com/obscuren/sha3"
"hash" "hash"
@ -15,7 +14,7 @@ import (
var powlogger = ethlog.NewLogger("POW") var powlogger = ethlog.NewLogger("POW")
type PoW interface { type PoW interface {
Search(block *Block, reactChan chan ethreact.Event) []byte Search(block *Block, reactChan chan ethutil.React) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool Verify(hash []byte, diff *big.Int, nonce []byte) bool
} }
@ -23,7 +22,7 @@ type EasyPow struct {
hash *big.Int hash *big.Int
} }
func (pow *EasyPow) Search(block *Block, reactChan chan ethreact.Event) []byte { func (pow *EasyPow) Search(block *Block, reactChan chan ethutil.React) []byte {
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce() hash := block.HashNoNonce()
diff := block.Difficulty diff := block.Difficulty

View File

@ -1,6 +1,7 @@
package ethchain package ethchain
import ( import (
"fmt"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
@ -26,6 +27,131 @@ func NewState(trie *ethtrie.Trie) *State {
return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
} }
// Iterate over each storage address and yield callback
func (s *State) EachStorage(cb ethtrie.EachCallback) {
it := s.trie.NewIterator()
it.Each(cb)
}
// Retrieve the balance from the given address or 0 if object not found
func (self *State) GetBalance(addr []byte) *big.Int {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.Amount
}
return ethutil.Big0
}
func (self *State) GetNonce(addr []byte) uint64 {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.Nonce
}
return 0
}
//
// Setting, updating & deleting state object methods
//
// Update the given state object and apply it to state trie
func (self *State) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address()
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Script()), stateObject.Script())
self.trie.Update(string(addr), string(stateObject.RlpEncode()))
self.manifest.AddObjectChange(stateObject)
}
// Delete the given state object and delete it from the state trie
func (self *State) DeleteStateObject(stateObject *StateObject) {
self.trie.Delete(string(stateObject.Address()))
delete(self.stateObjects, string(stateObject.Address()))
}
// Retrieve a state object given my the address. Nil if not found
func (self *State) GetStateObject(addr []byte) *StateObject {
stateObject := self.stateObjects[string(addr)]
if stateObject != nil {
return stateObject
}
data := self.trie.Get(string(addr))
if len(data) == 0 {
return nil
}
stateObject = NewStateObjectFromBytes(addr, []byte(data))
self.stateObjects[string(addr)] = stateObject
return stateObject
}
// Retrieve a state object or create a new state object if nil
func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
stateObject := self.GetStateObject(addr)
if stateObject == nil {
stateObject = self.NewStateObject(addr)
}
return stateObject
}
// Create a state object whether it exist in the trie or not
func (self *State) NewStateObject(addr []byte) *StateObject {
statelogger.Infof("(+) %x\n", addr)
stateObject := NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject
return stateObject
}
// Deprecated
func (self *State) GetAccount(addr []byte) *StateObject {
return self.GetOrNewStateObject(addr)
}
//
// Setting, copying of the state methods
//
func (s *State) Cmp(other *State) bool {
return s.trie.Cmp(other.trie)
}
func (self *State) Copy() *State {
if self.trie != nil {
state := NewState(self.trie.Copy())
for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy()
}
return state
}
return nil
}
func (self *State) Set(state *State) {
if state == nil {
panic("Tried setting 'state' to nil through 'Set'")
}
self.trie = state.trie
self.stateObjects = state.stateObjects
//*self = *state
}
func (s *State) Root() interface{} {
return s.trie.Root
}
// Resets the trie and all siblings // Resets the trie and all siblings
func (s *State) Reset() { func (s *State) Reset() {
s.trie.Undo() s.trie.Undo()
@ -36,7 +162,8 @@ func (s *State) Reset() {
continue continue
} }
stateObject.state.Reset() //stateObject.state.Reset()
stateObject.Reset()
} }
s.Empty() s.Empty()
@ -67,109 +194,29 @@ func (self *State) Empty() {
func (self *State) Update() { func (self *State) Update() {
for _, stateObject := range self.stateObjects { for _, stateObject := range self.stateObjects {
if stateObject.remove { if stateObject.remove {
self.trie.Delete(string(stateObject.Address())) self.DeleteStateObject(stateObject)
} else { } else {
stateObject.Sync()
self.UpdateStateObject(stateObject) self.UpdateStateObject(stateObject)
} }
} }
}
// Purges the current trie. // FIXME trie delete is broken
func (s *State) Purge() int { valid, t2 := ethtrie.ParanoiaCheck(self.trie)
return s.trie.NewIterator().Purge() if !valid {
} self.trie = t2
func (s *State) EachStorage(cb ethtrie.EachCallback) {
it := s.trie.NewIterator()
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()
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Script()), stateObject.Script())
self.trie.Update(string(addr), string(stateObject.RlpEncode()))
self.manifest.AddObjectChange(stateObject)
}
func (self *State) GetStateObject(addr []byte) *StateObject {
stateObject := self.stateObjects[string(addr)]
if stateObject != nil {
return stateObject
} }
}
data := self.trie.Get(string(addr)) // Debug stuff
if len(data) == 0 { func (self *State) CreateOutputForDiff() {
return nil for addr, stateObject := range self.stateObjects {
fmt.Printf("0x%x 0x%x 0x%x 0x%x\n", addr, stateObject.state.Root(), stateObject.Amount.Bytes(), stateObject.Nonce)
stateObject.state.EachStorage(func(addr string, value *ethutil.Value) {
fmt.Printf("0x%x 0x%x\n", addr, value.Bytes())
})
} }
stateObject = NewStateObjectFromBytes(addr, []byte(data))
self.stateObjects[string(addr)] = stateObject
return stateObject
}
func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
stateObject := self.GetStateObject(addr)
if stateObject == nil {
stateObject = self.NewStateObject(addr)
}
return stateObject
}
func (self *State) NewStateObject(addr []byte) *StateObject {
statelogger.Infof("(+) %x\n", addr)
stateObject := NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject
return stateObject
}
func (self *State) GetAccount(addr []byte) *StateObject {
return self.GetOrNewStateObject(addr)
}
func (s *State) Cmp(other *State) bool {
return s.trie.Cmp(other.trie)
}
func (self *State) Copy() *State {
if self.trie != nil {
state := NewState(self.trie.Copy())
for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy()
}
return state
}
return nil
}
func (self *State) Set(state *State) {
if state == nil {
panic("Tried setting 'state' to nil through 'Set'")
}
*self = *state
}
func (s *State) Put(key, object []byte) {
s.trie.Update(string(key), string(object))
}
func (s *State) Root() interface{} {
return s.trie.Root
} }
// Object manifest // Object manifest

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
@ -37,7 +36,7 @@ type EthManager interface {
BlockChain() *BlockChain BlockChain() *BlockChain
TxPool() *TxPool TxPool() *TxPool
Broadcast(msgType ethwire.MsgType, data []interface{}) Broadcast(msgType ethwire.MsgType, data []interface{})
Reactor() *ethreact.ReactorEngine Reactor() *ethutil.ReactorEngine
PeerCount() int PeerCount() int
IsMining() bool IsMining() bool
IsListening() bool IsListening() bool
@ -67,6 +66,11 @@ type StateManager struct {
// Mining state. The mining state is used purely and solely by the mining // Mining state. The mining state is used purely and solely by the mining
// operation. // operation.
miningState *State miningState *State
// The last attempted block is mainly used for debugging purposes
// This does not have to be a valid block and will be set during
// 'Process' & canonical validation.
lastAttemptedBlock *Block
} }
func NewStateManager(ethereum EthManager) *StateManager { func NewStateManager(ethereum EthManager) *StateManager {
@ -146,6 +150,10 @@ done:
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
handled = append(handled, tx) handled = append(handled, tx)
if ethutil.Config.Diff {
state.CreateOutputForDiff()
}
} }
parent.GasUsed = totalUsedGas parent.GasUsed = totalUsedGas
@ -166,6 +174,8 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
return ParentError(block.PrevHash) return ParentError(block.PrevHash)
} }
sm.lastAttemptedBlock = block
var ( var (
parent = sm.bc.GetBlock(block.PrevHash) parent = sm.bc.GetBlock(block.PrevHash)
state = parent.State() state = parent.State()
@ -177,6 +187,10 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
// before that. // before that.
defer state.Reset() defer state.Reset()
if ethutil.Config.Diff {
fmt.Printf("## 0x%x 0x%x ##\n", block.Hash(), block.Number)
}
receipts, err := sm.ApplyDiff(state, parent, block) receipts, err := sm.ApplyDiff(state, parent, block)
defer func() { defer func() {
if err != nil { if err != nil {

View File

@ -27,6 +27,8 @@ type StateObject struct {
script Code script Code
initScript Code initScript Code
storage map[string]*ethutil.Value
// 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
// purchased of the coinbase. // purchased of the coinbase.
@ -38,6 +40,10 @@ type StateObject struct {
remove bool remove bool
} }
func (self *StateObject) Reset() {
self.storage = make(map[string]*ethutil.Value)
}
// Converts an transaction in to a state object // Converts an transaction in to a state object
func MakeContract(tx *Transaction, state *State) *StateObject { func MakeContract(tx *Transaction, state *State) *StateObject {
// Create contract if there's no recipient // Create contract if there's no recipient
@ -55,14 +61,19 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
} }
func NewStateObject(addr []byte) *StateObject { func NewStateObject(addr []byte) *StateObject {
object := &StateObject{address: addr, Amount: new(big.Int), gasPool: new(big.Int)} // This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter.
address := ethutil.LeftPadBytes(addr, 20)
object := &StateObject{address: address, Amount: new(big.Int), gasPool: new(big.Int)}
object.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) object.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
object.storage = make(map[string]*ethutil.Value)
return object return object
} }
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
contract := &StateObject{address: address, Amount: Amount, Nonce: 0} contract := NewStateObject(address)
contract.Amount = Amount
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
return contract return contract
@ -84,6 +95,7 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject {
func (self *StateObject) MarkForDeletion() { func (self *StateObject) MarkForDeletion() {
self.remove = true self.remove = true
statelogger.Infof("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Amount)
} }
func (c *StateObject) GetAddr(addr []byte) *ethutil.Value { func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
@ -94,24 +106,43 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) {
c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
} }
func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) { func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value {
addr := ethutil.BigToBytes(num, 256) return self.getStorage(key.Bytes())
}
func (self *StateObject) SetStorage(key *big.Int, value *ethutil.Value) {
self.setStorage(key.Bytes(), value)
}
if val.BigInt().Cmp(ethutil.Big0) == 0 { func (self *StateObject) getStorage(key []byte) *ethutil.Value {
c.state.trie.Delete(string(addr)) k := ethutil.LeftPadBytes(key, 32)
return value := self.storage[string(k)]
if value == nil {
value = self.GetAddr(k)
self.storage[string(k)] = value
} }
c.SetAddr(addr, val) return value
} }
func (c *StateObject) GetStorage(num *big.Int) *ethutil.Value { func (self *StateObject) setStorage(key []byte, value *ethutil.Value) {
nb := ethutil.BigToBytes(num, 256) k := ethutil.LeftPadBytes(key, 32)
return c.GetAddr(nb) self.storage[string(k)] = value
} }
func (self *StateObject) Sync() {
for key, value := range self.storage {
if value.BigInt().Cmp(ethutil.Big0) == 0 {
self.state.trie.Delete(string(key))
continue
}
self.SetAddr([]byte(key), value)
}
}
func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
if int64(len(c.script)-1) < pc.Int64() { if int64(len(c.script)-1) < pc.Int64() {
return ethutil.NewValue(0) return ethutil.NewValue(0)
@ -248,6 +279,7 @@ func (c *StateObject) RlpDecode(data []byte) {
c.Nonce = decoder.Get(0).Uint() c.Nonce = decoder.Get(0).Uint()
c.Amount = decoder.Get(1).BigInt() c.Amount = decoder.Get(1).BigInt()
c.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) c.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
c.storage = make(map[string]*ethutil.Value)
c.ScriptHash = decoder.Get(3).Bytes() c.ScriptHash = decoder.Get(3).Bytes()

View File

@ -164,8 +164,6 @@ func (self *StateTransition) TransitionState() (err error) {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
sender.Nonce += 1 sender.Nonce += 1
receiver = self.Receiver()
// Transaction gas // Transaction gas
if err = self.UseGas(GasTx); err != nil { if err = self.UseGas(GasTx); err != nil {
return return
@ -178,13 +176,23 @@ func (self *StateTransition) TransitionState() (err error) {
return return
} }
/* FIXME
* If tx goes TO "0", goes OOG during init, reverse changes, but initial endowment should happen. The ether is lost forever
*/
var snapshot *State
// If the receiver is nil it's a contract (\0*32). // If the receiver is nil it's a contract (\0*32).
if receiver == nil { if tx.CreatesContract() {
snapshot = self.state.Copy()
// 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)
self.rec = receiver
if receiver == nil { if receiver == nil {
return fmt.Errorf("Unable to create contract") return fmt.Errorf("Unable to create contract")
} }
} else {
receiver = self.Receiver()
} }
// Transfer value from sender to receiver // Transfer value from sender to receiver
@ -192,7 +200,9 @@ func (self *StateTransition) TransitionState() (err error) {
return return
} }
//snapshot := self.state.Copy() if snapshot == nil {
snapshot = self.state.Copy()
}
// Process the init code and create 'valid' contract // Process the init code and create 'valid' contract
if IsContractAddr(self.receiver) { if IsContractAddr(self.receiver) {
@ -203,8 +213,7 @@ func (self *StateTransition) TransitionState() (err error) {
code, err := self.Eval(receiver.Init(), receiver, "init") code, err := self.Eval(receiver.Init(), receiver, "init")
if err != nil { if err != nil {
//self.state.Set(snapshot) self.state.Set(snapshot)
self.state.ResetStateObject(receiver)
return fmt.Errorf("Error during init execution %v", err) return fmt.Errorf("Error during init execution %v", err)
} }
@ -214,8 +223,7 @@ func (self *StateTransition) TransitionState() (err error) {
if len(receiver.Script()) > 0 { if len(receiver.Script()) > 0 {
_, err = self.Eval(receiver.Script(), receiver, "code") _, err = self.Eval(receiver.Script(), receiver, "code")
if err != nil { if err != nil {
//self.state.Set(snapshot) self.state.Set(snapshot)
self.state.ResetStateObject(receiver)
return fmt.Errorf("Error during code execution %v", err) return fmt.Errorf("Error during code execution %v", err)
} }

View File

@ -22,12 +22,16 @@ var (
GasMemory = big.NewInt(1) GasMemory = big.NewInt(1)
GasData = big.NewInt(5) GasData = big.NewInt(5)
GasTx = big.NewInt(500) GasTx = big.NewInt(500)
LogTyPretty byte = 0x1
LogTyDiff byte = 0x2
) )
type Debugger interface { type Debugger interface {
BreakHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool BreakHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
StepHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool StepHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
BreakPoints() []int64 BreakPoints() []int64
SetCode(byteCode []byte)
} }
type Vm struct { type Vm struct {
@ -44,6 +48,7 @@ type Vm struct {
Verbose bool Verbose bool
logTy byte
logStr string logStr string
err error err error
@ -69,7 +74,7 @@ type RuntimeVars struct {
} }
func (self *Vm) Printf(format string, v ...interface{}) *Vm { func (self *Vm) Printf(format string, v ...interface{}) *Vm {
if self.Verbose { if self.Verbose && self.logTy == LogTyPretty {
self.logStr += fmt.Sprintf(format, v...) self.logStr += fmt.Sprintf(format, v...)
} }
@ -77,7 +82,7 @@ func (self *Vm) Printf(format string, v ...interface{}) *Vm {
} }
func (self *Vm) Endl() *Vm { func (self *Vm) Endl() *Vm {
if self.Verbose { if self.Verbose && self.logTy == LogTyPretty {
vmlogger.Debugln(self.logStr) vmlogger.Debugln(self.logStr)
self.logStr = "" self.logStr = ""
} }
@ -86,7 +91,12 @@ func (self *Vm) Endl() *Vm {
} }
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} lt := LogTyPretty
if ethutil.Config.Diff {
lt = LogTyDiff
}
return &Vm{vars: vars, state: state, stateManager: stateManager, logTy: lt}
} }
var Pow256 = ethutil.BigPow(2, 256) var Pow256 = ethutil.BigPow(2, 256)
@ -103,7 +113,17 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
} }
}() }()
vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", vm.Fn, closure.object.Address(), closure.Gas, closure.Args) // Debug hook
if vm.Dbg != nil {
vm.Dbg.SetCode(closure.Script)
}
// Don't bother with the execution if there's no code.
if len(closure.Script) == 0 {
return closure.Return(nil), nil
}
vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", vm.Fn, closure.Address(), closure.Gas, closure.Args)
var ( var (
op OpCode op OpCode
@ -132,6 +152,17 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// Get the opcode (it must be an opcode!) // Get the opcode (it must be an opcode!)
op = OpCode(val.Uint()) op = OpCode(val.Uint())
// XXX Leave this Println intact. Don't change this to the log system.
// Used for creating diffs between implementations
if vm.logTy == LogTyDiff {
b := pc.Bytes()
if len(b) == 0 {
b = []byte{0}
}
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
}
gas := new(big.Int) gas := new(big.Int)
addStepGasUsage := func(amount *big.Int) { addStepGasUsage := func(amount *big.Int) {
if amount.Cmp(ethutil.Big0) >= 0 { if amount.Cmp(ethutil.Big0) >= 0 {
@ -167,7 +198,9 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
require(2) require(2)
newMemSize = stack.Peek().Uint64() + 32 newMemSize = stack.Peek().Uint64() + 32
case MLOAD: case MLOAD:
require(1)
newMemSize = stack.Peek().Uint64() + 32
case MSTORE8: case MSTORE8:
require(2) require(2)
newMemSize = stack.Peek().Uint64() + 1 newMemSize = stack.Peek().Uint64() + 1
@ -415,7 +448,10 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
require(2) require(2)
val, th := stack.Popn() val, th := stack.Popn()
if th.Cmp(big.NewInt(32)) < 0 { if th.Cmp(big.NewInt(32)) < 0 {
stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64())) byt := big.NewInt(int64(val.Bytes()[th.Int64()]))
stack.Push(byt)
vm.Printf(" => 0x%x", byt.Bytes())
} else { } else {
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
} }
@ -427,13 +463,26 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
data := ethcrypto.Sha3Bin(mem.Get(offset.Int64(), size.Int64())) data := ethcrypto.Sha3Bin(mem.Get(offset.Int64(), size.Int64()))
stack.Push(ethutil.BigD(data)) stack.Push(ethutil.BigD(data))
vm.Printf(" => %x", data)
// 0x30 range // 0x30 range
case ADDRESS: case ADDRESS:
stack.Push(ethutil.BigD(closure.Object().Address())) stack.Push(ethutil.BigD(closure.Address()))
vm.Printf(" => %x", closure.Address())
case BALANCE: case BALANCE:
stack.Push(closure.object.Amount) require(1)
addr := stack.Pop().Bytes()
balance := vm.state.GetBalance(addr)
stack.Push(balance)
vm.Printf(" => %v (%x)", balance, addr)
case ORIGIN: case ORIGIN:
stack.Push(ethutil.BigD(vm.vars.Origin)) stack.Push(ethutil.BigD(vm.vars.Origin))
vm.Printf(" => %v", vm.vars.Origin)
case CALLER: case CALLER:
caller := closure.caller.Address() caller := closure.caller.Address()
stack.Push(ethutil.BigD(caller)) stack.Push(ethutil.BigD(caller))
@ -441,6 +490,8 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
vm.Printf(" => %x", caller) vm.Printf(" => %x", caller)
case CALLVALUE: case CALLVALUE:
stack.Push(vm.vars.Value) stack.Push(vm.vars.Value)
vm.Printf(" => %v", vm.vars.Value)
case CALLDATALOAD: case CALLDATALOAD:
require(1) require(1)
offset := stack.Pop().Int64() offset := stack.Pop().Int64()
@ -499,8 +550,10 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
} }
code := closure.Script[cOff : cOff+l] code := closure.Script[cOff : cOff+l]
fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff)
mem.Set(mOff, l, code) mem.Set(mOff, l, code)
fmt.Println(Code(mem.Get(mOff, l)))
case GASPRICE: case GASPRICE:
stack.Push(closure.Price) stack.Push(closure.Price)
@ -562,8 +615,9 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
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))
mem.store[mStart.Int64()] = byte(val.Int64() & 0xff)
vm.Printf(" => 0x%x", val) vm.Printf(" => 0x%x", val)
case SLOAD: case SLOAD:
@ -623,9 +677,9 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
) )
// Generate a new address // Generate a new address
addr := ethcrypto.CreateAddress(closure.object.Address(), closure.object.Nonce) addr := ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64())
for i := uint64(0); vm.state.GetStateObject(addr) != nil; i++ { for i := uint64(0); vm.state.GetStateObject(addr) != nil; i++ {
ethcrypto.CreateAddress(closure.object.Address(), closure.object.Nonce+i) ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64()+i)
} }
closure.object.Nonce++ closure.object.Nonce++
@ -638,14 +692,15 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
contract.AddAmount(value) contract.AddAmount(value)
// Set the init script // Set the init script
contract.initScript = mem.Get(offset.Int64(), size.Int64()) initCode := mem.Get(offset.Int64(), size.Int64())
//fmt.Printf("%x\n", initCode)
// Transfer all remaining gas to the new // Transfer all remaining gas to the new
// contract so it may run the init script // contract so it may run the init script
gas := new(big.Int).Set(closure.Gas) gas := new(big.Int).Set(closure.Gas)
closure.UseGas(closure.Gas) closure.UseGas(closure.Gas)
// Create the closure // Create the closure
c := NewClosure(closure, contract, contract.initScript, vm.state, gas, closure.Price) c := NewClosure(closure, contract, initCode, vm.state, gas, closure.Price)
// Call the closure and set the return value as // Call the closure and set the return value as
// main script. // main script.
contract.script, err = Call(vm, c, nil) contract.script, err = Call(vm, c, nil)
@ -665,6 +720,11 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
vm.Printf("CREATE success") vm.Printf("CREATE success")
} }
vm.Endl() vm.Endl()
// Debug hook
if vm.Dbg != nil {
vm.Dbg.SetCode(closure.Script)
}
case CALL: case CALL:
require(7) require(7)
@ -683,10 +743,11 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
if closure.object.Amount.Cmp(value) < 0 { if closure.object.Amount.Cmp(value) < 0 {
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
closure.ReturnGas(gas, nil, nil)
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
} else { } else {
//snapshot := vm.state.Copy() snapshot := vm.state.Copy()
stateObject := vm.state.GetOrNewStateObject(addr.Bytes()) stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
@ -702,13 +763,17 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
vmlogger.Debugf("Closure execution failed. %v\n", err) vmlogger.Debugf("Closure execution failed. %v\n", err)
//vm.state.Set(snapshot) vm.state.Set(snapshot)
vm.state.ResetStateObject(stateObject)
} else { } else {
stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
mem.Set(retOffset.Int64(), retSize.Int64(), ret) mem.Set(retOffset.Int64(), retSize.Int64(), ret)
} }
// Debug hook
if vm.Dbg != nil {
vm.Dbg.SetCode(closure.Script)
}
} }
case RETURN: case RETURN:
require(2) require(2)
@ -721,18 +786,12 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
case SUICIDE: case SUICIDE:
require(1) require(1)
receiver := vm.state.GetAccount(stack.Pop().Bytes()) receiver := vm.state.GetOrNewStateObject(stack.Pop().Bytes())
receiver.AddAmount(closure.object.Amount) receiver.AddAmount(closure.object.Amount)
closure.object.MarkForDeletion() closure.object.MarkForDeletion()
/*
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.Endl() vm.Endl()
@ -752,6 +811,8 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
if vm.Dbg != nil { if vm.Dbg != nil {
for _, instrNo := range vm.Dbg.BreakPoints() { for _, instrNo := range vm.Dbg.BreakPoints() {
if pc.Cmp(big.NewInt(instrNo)) == 0 { if pc.Cmp(big.NewInt(instrNo)) == 0 {
vm.Stepping = true
if !vm.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) { if !vm.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) {
return nil, nil return nil, nil
} }

View File

@ -2,9 +2,9 @@ package ethcrypto
import ( import (
"code.google.com/p/go.crypto/ripemd160" "code.google.com/p/go.crypto/ripemd160"
"code.google.com/p/go.crypto/sha3"
"crypto/sha256" "crypto/sha256"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/sha3"
) )
func Sha256Bin(data []byte) []byte { func Sha256Bin(data []byte) []byte {

View File

@ -3,7 +3,9 @@ package ethcrypto
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"path" "path"
"path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
@ -12,6 +14,14 @@ import (
func InitWords() []string { func InitWords() []string {
_, thisfile, _, _ := runtime.Caller(1) _, thisfile, _, _ := runtime.Caller(1)
filename := path.Join(path.Dir(thisfile), "mnemonic.words.lst") filename := path.Join(path.Dir(thisfile), "mnemonic.words.lst")
if _, err := os.Stat(filename); os.IsNotExist(err) {
fmt.Printf("reading mnemonic word list file 'mnemonic.words.lst' from source folder failed, looking in current folder.")
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
panic(fmt.Errorf("problem getting current folder: ", err))
}
filename = path.Join(dir, "mnemonic.words.lst")
}
content, err := ioutil.ReadFile(filename) content, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
panic(fmt.Errorf("reading mnemonic word list file 'mnemonic.words.lst' failed: ", err)) panic(fmt.Errorf("reading mnemonic word list file 'mnemonic.words.lst' failed: ", err))

View File

@ -6,7 +6,6 @@ import (
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethrpc" "github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
@ -21,6 +20,8 @@ import (
"time" "time"
) )
const seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
var ethlogger = ethlog.NewLogger("SERV") var ethlogger = ethlog.NewLogger("SERV")
func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) { func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
@ -72,7 +73,7 @@ type Ethereum struct {
listening bool listening bool
reactor *ethreact.ReactorEngine reactor *ethutil.ReactorEngine
RpcServer *ethrpc.JsonRpcServer RpcServer *ethrpc.JsonRpcServer
@ -107,7 +108,7 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
keyManager: keyManager, keyManager: keyManager,
clientIdentity: clientIdentity, clientIdentity: clientIdentity,
} }
ethereum.reactor = ethreact.New() ethereum.reactor = ethutil.NewReactorEngine()
ethereum.txPool = ethchain.NewTxPool(ethereum) ethereum.txPool = ethchain.NewTxPool(ethereum)
ethereum.blockChain = ethchain.NewBlockChain(ethereum) ethereum.blockChain = ethchain.NewBlockChain(ethereum)
@ -119,7 +120,7 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
return ethereum, nil return ethereum, nil
} }
func (s *Ethereum) Reactor() *ethreact.ReactorEngine { func (s *Ethereum) Reactor() *ethutil.ReactorEngine {
return s.reactor return s.reactor
} }
@ -351,7 +352,6 @@ func (s *Ethereum) ReapDeadPeerHandler() {
// Start the ethereum // Start the ethereum
func (s *Ethereum) Start(seed bool) { func (s *Ethereum) Start(seed bool) {
s.reactor.Start()
// Bind to addr and port // Bind to addr and port
ln, err := net.Listen("tcp", ":"+s.Port) ln, err := net.Listen("tcp", ":"+s.Port)
if err != nil { if err != nil {
@ -418,7 +418,7 @@ func (s *Ethereum) Seed() {
s.ProcessPeerList(peers) s.ProcessPeerList(peers)
} else { } else {
// Fallback to servers.poc3.txt // Fallback to servers.poc3.txt
resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt") resp, err := http.Get(seedTextFileUri)
if err != nil { if err != nil {
ethlogger.Warnln("Fetching seed failed:", err) ethlogger.Warnln("Fetching seed failed:", err)
return return
@ -463,9 +463,6 @@ func (s *Ethereum) Stop() {
s.txPool.Stop() s.txPool.Stop()
s.stateManager.Stop() s.stateManager.Stop()
s.reactor.Flush()
s.reactor.Stop()
ethlogger.Infoln("Server stopped") ethlogger.Infoln("Server stopped")
close(s.shutdownChan) close(s.shutdownChan)
} }

View File

@ -97,7 +97,6 @@ func Reset() {
} }
<-status <-status
} }
logSystems = nil
} }
// waits until log messages are drained (dispatched to log writers) // waits until log messages are drained (dispatched to log writers)
@ -116,6 +115,7 @@ func NewLogger(tag string) *Logger {
} }
func AddLogSystem(logSystem LogSystem) { func AddLogSystem(logSystem LogSystem) {
var mutex = &sync.Mutex{}
mutex.Lock() mutex.Lock()
defer mutex.Unlock() defer mutex.Unlock()
if logSystems == nil { if logSystems == nil {
@ -128,14 +128,16 @@ func AddLogSystem(logSystem LogSystem) {
} }
func (logger *Logger) sendln(level LogLevel, v ...interface{}) { func (logger *Logger) sendln(level LogLevel, v ...interface{}) {
if logSystems != nil { if logMessages != nil {
send(newPrintlnLogMessage(level, logger.tag, v...)) msg := newPrintlnLogMessage(level, logger.tag, v...)
logMessages <- msg
} }
} }
func (logger *Logger) sendf(level LogLevel, format string, v ...interface{}) { func (logger *Logger) sendf(level LogLevel, format string, v ...interface{}) {
if logSystems != nil { if logMessages != nil {
send(newPrintfLogMessage(level, logger.tag, format, v...)) msg := newPrintfLogMessage(level, logger.tag, format, v...)
logMessages <- msg
} }
} }

View File

@ -54,8 +54,9 @@ func TestLoggerPrintln(t *testing.T) {
Flush() Flush()
Reset() Reset()
output := testLogSystem.Output output := testLogSystem.Output
fmt.Println(quote(output))
if output != "[TEST] error\n[TEST] warn\n" { if output != "[TEST] error\n[TEST] warn\n" {
t.Error("Expected logger output '[TEST] error\\n[TEST] warn\\n', got ", testLogSystem.Output) t.Error("Expected logger output '[TEST] error\\n[TEST] warn\\n', got ", output)
} }
} }
@ -71,7 +72,7 @@ func TestLoggerPrintf(t *testing.T) {
Reset() Reset()
output := testLogSystem.Output output := testLogSystem.Output
if output != "[TEST] error to { 2}\n[TEST] warn" { if output != "[TEST] error to { 2}\n[TEST] warn" {
t.Error("Expected logger output '[TEST] error to { 2}\\n[TEST] warn', got ", testLogSystem.Output) t.Error("Expected logger output '[TEST] error to { 2}\\n[TEST] warn', got ", output)
} }
} }
@ -88,10 +89,10 @@ func TestMultipleLogSystems(t *testing.T) {
output0 := testLogSystem0.Output output0 := testLogSystem0.Output
output1 := testLogSystem1.Output output1 := testLogSystem1.Output
if output0 != "[TEST] error\n" { if output0 != "[TEST] error\n" {
t.Error("Expected logger 0 output '[TEST] error\\n', got ", testLogSystem0.Output) t.Error("Expected logger 0 output '[TEST] error\\n', got ", output0)
} }
if output1 != "[TEST] error\n[TEST] warn\n" { if output1 != "[TEST] error\n[TEST] warn\n" {
t.Error("Expected logger 1 output '[TEST] error\\n[TEST] warn\\n', got ", testLogSystem1.Output) t.Error("Expected logger 1 output '[TEST] error\\n[TEST] warn\\n', got ", output1)
} }
} }
@ -104,7 +105,6 @@ func TestFileLogSystem(t *testing.T) {
logger.Errorf("error to %s\n", filename) logger.Errorf("error to %s\n", filename)
logger.Warnln("warn") logger.Warnln("warn")
Flush() Flush()
Reset()
contents, _ := ioutil.ReadFile(filename) contents, _ := ioutil.ReadFile(filename)
output := string(contents) output := string(contents)
if output != "[TEST] error to test.log\n[TEST] warn\n" { if output != "[TEST] error to test.log\n[TEST] warn\n" {
@ -117,7 +117,5 @@ func TestFileLogSystem(t *testing.T) {
func TestNoLogSystem(t *testing.T) { func TestNoLogSystem(t *testing.T) {
logger := NewLogger("TEST") logger := NewLogger("TEST")
logger.Warnln("warn") logger.Warnln("warn")
fmt.Println("1")
Flush() Flush()
Reset()
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"sort" "sort"
) )
@ -15,19 +15,19 @@ type Miner struct {
pow ethchain.PoW pow ethchain.PoW
ethereum ethchain.EthManager ethereum ethchain.EthManager
coinbase []byte coinbase []byte
reactChan chan ethreact.Event reactChan chan ethutil.React
txs ethchain.Transactions txs ethchain.Transactions
uncles []*ethchain.Block uncles []*ethchain.Block
block *ethchain.Block block *ethchain.Block
powChan chan []byte powChan chan []byte
powQuitChan chan ethreact.Event powQuitChan chan ethutil.React
quitChan chan bool quitChan chan bool
} }
func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner { func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner {
reactChan := make(chan ethreact.Event, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in
powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block
powQuitChan := make(chan ethreact.Event, 1) // This is the channel that can exit the miner thread powQuitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread
quitChan := make(chan bool, 1) quitChan := make(chan bool, 1)
ethereum.Reactor().Subscribe("newBlock", reactChan) ethereum.Reactor().Subscribe("newBlock", reactChan)

View File

@ -1,40 +0,0 @@
# ethreact
ethereum event reactor. Component of the ethereum stack.
various events like state change on an account or new block found are broadcast to subscribers.
Broadcasting to subscribers is running on its own routine and globally order preserving.
## Clients
### subscribe
eventChannel := make(chan ethreact.Event)
reactor.Subscribe(event, eventChannel)
The same channel can be subscribed to multiple events but only once for each event. In order to allow order of events to be preserved, broadcast of events is synchronous within the main broadcast loop. Therefore any blocking subscriber channels will be skipped, i.e. missing broadcasting events while they are blocked.
### unsubscribe
reactor.Unsubscribe(event, eventChannel)
### Processing events
event.Resource is of type interface{}. The actual type of event.Resource depends on event.Name and may need to be cast for processing.
var event ethreact.Event
for {
select {
case event = <-eventChannel:
processTransaction(event.Resource.(Transaction))
}
}
## Broadcast
reactor := ethreact.New()
reactor.Start()
reactor.Post(name, resource)
reactor.Flush() // wait till all broadcast messages are dispatched
reactor.Stop() // stop the main broadcast loop immediately (even if there are unbroadcast events left)

View File

@ -1,63 +0,0 @@
package ethreact
import (
"fmt"
"testing"
)
func TestReactorAdd(t *testing.T) {
reactor := New()
ch := make(chan Event)
reactor.Subscribe("test", ch)
if reactor.eventHandlers["test"] == nil {
t.Error("Expected new eventHandler to be created")
}
reactor.Unsubscribe("test", ch)
if reactor.eventHandlers["test"] != nil {
t.Error("Expected eventHandler to be removed")
}
}
func TestReactorEvent(t *testing.T) {
var name string
reactor := New()
// Buffer the channel, so it doesn't block for this test
cap := 20
ch := make(chan Event, cap)
reactor.Subscribe("even", ch)
reactor.Subscribe("odd", ch)
reactor.Post("even", "disappears") // should not broadcast if engine not started
reactor.Start()
for i := 0; i < cap; i++ {
if i%2 == 0 {
name = "even"
} else {
name = "odd"
}
reactor.Post(name, i)
}
reactor.Post("test", cap) // this should not block
i := 0
reactor.Flush()
close(ch)
for event := range ch {
fmt.Printf("%d: %v", i, event)
if i%2 == 0 {
name = "even"
} else {
name = "odd"
}
if val, ok := event.Resource.(int); ok {
if i != val || event.Name != name {
t.Error("Expected event %d to be of type %s and resource %d, got ", i, name, i, val)
}
} else {
t.Error("Unable to cast")
}
i++
}
if i != cap {
t.Error("excpected exactly %d events, got ", i)
}
reactor.Stop()
}

View File

@ -3,8 +3,20 @@ package ethutil
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"runtime"
) )
func IsWindows() bool {
return runtime.GOOS == "windows"
}
func WindonizePath(path string) string {
if string(path[0]) == "/" && IsWindows() {
path = path[1:]
}
return path
}
// The different number of units // The different number of units
var ( var (
Douglas = BigPow(10, 42) Douglas = BigPow(10, 42)

View File

@ -13,6 +13,7 @@ type ConfigManager struct {
ExecPath string ExecPath string
Debug bool Debug bool
Diff bool
Paranoia bool Paranoia bool
conf *globalconf.GlobalConf conf *globalconf.GlobalConf

87
ethutil/reactor.go Normal file
View File

@ -0,0 +1,87 @@
package ethutil
import (
"sync"
)
type ReactorEvent struct {
mut sync.Mutex
event string
chans []chan React
}
// Post the specified reactor resource on the channels
// currently subscribed
func (e *ReactorEvent) Post(react React) {
e.mut.Lock()
defer e.mut.Unlock()
for _, ch := range e.chans {
go func(ch chan React) {
ch <- react
}(ch)
}
}
// Add a subscriber to this event
func (e *ReactorEvent) Add(ch chan React) {
e.mut.Lock()
defer e.mut.Unlock()
e.chans = append(e.chans, ch)
}
// Remove a subscriber
func (e *ReactorEvent) Remove(ch chan React) {
e.mut.Lock()
defer e.mut.Unlock()
for i, c := range e.chans {
if c == ch {
e.chans = append(e.chans[:i], e.chans[i+1:]...)
}
}
}
// Basic reactor resource
type React struct {
Resource interface{}
Event string
}
// The reactor basic engine. Acts as bridge
// between the events and the subscribers/posters
type ReactorEngine struct {
patterns map[string]*ReactorEvent
}
func NewReactorEngine() *ReactorEngine {
return &ReactorEngine{patterns: make(map[string]*ReactorEvent)}
}
// Subscribe a channel to the specified event
func (reactor *ReactorEngine) Subscribe(event string, ch chan React) {
ev := reactor.patterns[event]
// Create a new event if one isn't available
if ev == nil {
ev = &ReactorEvent{event: event}
reactor.patterns[event] = ev
}
// Add the channel to reactor event handler
ev.Add(ch)
}
func (reactor *ReactorEngine) Unsubscribe(event string, ch chan React) {
ev := reactor.patterns[event]
if ev != nil {
ev.Remove(ch)
}
}
func (reactor *ReactorEngine) Post(event string, resource interface{}) {
ev := reactor.patterns[event]
if ev != nil {
ev.Post(React{Resource: resource, Event: event})
}
}

30
ethutil/reactor_test.go Normal file
View File

@ -0,0 +1,30 @@
package ethutil
import "testing"
func TestReactorAdd(t *testing.T) {
engine := NewReactorEngine()
ch := make(chan React)
engine.Subscribe("test", ch)
if len(engine.patterns) != 1 {
t.Error("Expected patterns to be 1, got", len(engine.patterns))
}
}
func TestReactorEvent(t *testing.T) {
engine := NewReactorEngine()
// Buffer 1, so it doesn't block for this test
ch := make(chan React, 1)
engine.Subscribe("test", ch)
engine.Post("test", "hello")
value := <-ch
if val, ok := value.Resource.(string); ok {
if val != "hello" {
t.Error("Expected Resource to be 'hello', got", val)
}
} else {
t.Error("Unable to cast")
}
}

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/obscuren/mutan" "github.com/obscuren/mutan"
"github.com/obscuren/mutan/backends" "github.com/obscuren/mutan/backends"
"github.com/obscuren/serpent-go"
"strings" "strings"
) )
@ -15,13 +14,15 @@ func Compile(script string, silent bool) (ret []byte, err error) {
if len(line) > 1 && line[0:2] == "#!" { if len(line) > 1 && line[0:2] == "#!" {
switch line { switch line {
case "#!serpent": /*
byteCode, err := serpent.Compile(script) case "#!serpent":
if err != nil { byteCode, err := serpent.Compile(script)
return nil, err if err != nil {
} return nil, err
}
return byteCode, nil return byteCode, nil
*/
} }
} else { } else {

View File

@ -279,7 +279,7 @@ func ReadMessages(conn net.Conn) (msgs []*Msg, err error) {
var totalBytes int var totalBytes int
for { for {
// Give buffering some time // Give buffering some time
conn.SetReadDeadline(time.Now().Add(50 * time.Millisecond)) conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
// Create a new temporarily buffer // Create a new temporarily buffer
b := make([]byte, 1440) b := make([]byte, 1440)
// Wait for a message from this peer // Wait for a message from this peer

18
peer.go
View File

@ -21,7 +21,7 @@ const (
// The size of the output buffer for writing messages // The size of the output buffer for writing messages
outputBufferSize = 50 outputBufferSize = 50
// Current protocol version // Current protocol version
ProtocolVersion = 21 ProtocolVersion = 23
// Interval for ping/pong message // Interval for ping/pong message
pingPongTimer = 2 * time.Second pingPongTimer = 2 * time.Second
) )
@ -252,7 +252,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
} }
} }
peerlogger.DebugDetailln("<=", msg.Type, msg.Data) peerlogger.DebugDetailf("(%v) <= %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data)
err := ethwire.WriteMessage(p.conn, msg) err := ethwire.WriteMessage(p.conn, msg)
if err != nil { if err != nil {
@ -326,7 +326,7 @@ func (p *Peer) HandleInbound() {
peerlogger.Debugln(err) peerlogger.Debugln(err)
} }
for _, msg := range msgs { for _, msg := range msgs {
peerlogger.DebugDetailln("=>", msg.Type, msg.Data) peerlogger.DebugDetailf("(%v) => %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data)
switch msg.Type { switch msg.Type {
case ethwire.MsgHandshakeTy: case ethwire.MsgHandshakeTy:
@ -419,6 +419,16 @@ func (p *Peer) HandleInbound() {
if err != nil { if err != nil {
// If the parent is unknown try to catch up with this peer // If the parent is unknown try to catch up with this peer
if ethchain.IsParentErr(err) { if ethchain.IsParentErr(err) {
/*
b := ethchain.NewBlockFromRlpValue(msg.Data.Get(0))
peerlogger.Infof("Attempting to catch (%x). Parent known\n", b.Hash())
p.catchingUp = false
p.CatchupWithPeer(b.Hash())
peerlogger.Infoln(b)
*/
peerlogger.Infoln("Attempting to catch. Parent known") peerlogger.Infoln("Attempting to catch. Parent known")
p.catchingUp = false p.catchingUp = false
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
@ -744,7 +754,7 @@ func (p *Peer) CatchupWithPeer(blockHash []byte) {
if !p.catchingUp { if !p.catchingUp {
// Make sure nobody else is catching up when you want to do this // Make sure nobody else is catching up when you want to do this
p.catchingUp = true p.catchingUp = true
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(50)}) msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(10)})
p.QueueMessage(msg) p.QueueMessage(msg)
peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr()) peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())