all: use (blocking) event package instead of ethreact

This commit is contained in:
Felix Lange 2014-10-14 01:58:31 +02:00
parent 6906904896
commit 36cdab2068
8 changed files with 147 additions and 158 deletions

View File

@ -8,7 +8,6 @@ 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"
) )
@ -16,7 +15,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, stop <-chan struct{}) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool Verify(hash []byte, diff *big.Int, nonce []byte) bool
GetHashrate() int64 GetHashrate() int64
Turbo(bool) Turbo(bool)
@ -36,7 +35,7 @@ func (pow *EasyPow) Turbo(on bool) {
pow.turbo = on pow.turbo = on
} }
func (pow *EasyPow) Search(block *Block, reactChan chan ethreact.Event) []byte { func (pow *EasyPow) Search(block *Block, stop <-chan struct{}) []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
@ -46,7 +45,7 @@ func (pow *EasyPow) Search(block *Block, reactChan chan ethreact.Event) []byte {
for { for {
select { select {
case <-reactChan: case <-stop:
powlogger.Infoln("Breaking from mining") powlogger.Infoln("Breaking from mining")
return nil return nil
default: default:

10
ethchain/events.go Normal file
View File

@ -0,0 +1,10 @@
package ethchain
type TxEvent struct {
Type int // TxPre || TxPost
Tx *Transaction
}
type NewBlockEvent struct {
Block *Block
}

View File

@ -11,11 +11,10 @@ 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/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"github.com/ethereum/eth-go/eventer" "github.com/ethereum/eth-go/event"
) )
var statelogger = ethlog.NewLogger("STATE") var statelogger = ethlog.NewLogger("STATE")
@ -37,7 +36,6 @@ 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
PeerCount() int PeerCount() int
IsMining() bool IsMining() bool
IsListening() bool IsListening() bool
@ -45,7 +43,7 @@ type EthManager interface {
KeyManager() *ethcrypto.KeyManager KeyManager() *ethcrypto.KeyManager
ClientIdentity() ethwire.ClientIdentity ClientIdentity() ethwire.ClientIdentity
Db() ethutil.Database Db() ethutil.Database
Eventer() *eventer.EventMachine EventMux() *event.TypeMux
} }
type StateManager struct { type StateManager struct {
@ -73,17 +71,15 @@ type StateManager struct {
// 'Process' & canonical validation. // 'Process' & canonical validation.
lastAttemptedBlock *Block lastAttemptedBlock *Block
// Quit chan events event.Subscription
quit chan bool
} }
func NewStateManager(ethereum EthManager) *StateManager { func NewStateManager(ethereum EthManager) *StateManager {
sm := &StateManager{ sm := &StateManager{
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: &EasyPow{}, Pow: &EasyPow{},
eth: ethereum, eth: ethereum,
bc: ethereum.BlockChain(), bc: ethereum.BlockChain(),
quit: make(chan bool),
} }
sm.transState = ethereum.BlockChain().CurrentBlock.State().Copy() sm.transState = ethereum.BlockChain().CurrentBlock.State().Copy()
sm.miningState = ethereum.BlockChain().CurrentBlock.State().Copy() sm.miningState = ethereum.BlockChain().CurrentBlock.State().Copy()
@ -93,36 +89,25 @@ func NewStateManager(ethereum EthManager) *StateManager {
func (self *StateManager) Start() { func (self *StateManager) Start() {
statelogger.Debugln("Starting state manager") statelogger.Debugln("Starting state manager")
self.events = self.eth.EventMux().Subscribe(Blocks(nil))
go self.updateThread() go self.updateThread()
} }
func (self *StateManager) Stop() { func (self *StateManager) Stop() {
statelogger.Debugln("Stopping state manager") statelogger.Debugln("Stopping state manager")
self.events.Unsubscribe()
close(self.quit)
} }
func (self *StateManager) updateThread() { func (self *StateManager) updateThread() {
blockChan := self.eth.Eventer().Register("blocks") for ev := range self.events.Chan() {
for _, block := range ev.(Blocks) {
out: err := self.Process(block, false)
for { if err != nil {
select { statelogger.Infoln(err)
case event := <-blockChan: statelogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
blocks := event.Data.(Blocks) statelogger.Debugln(block)
for _, block := range blocks { break
err := self.Process(block, false)
if err != nil {
statelogger.Infoln(err)
statelogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
statelogger.Debugln(block)
break
}
} }
case <-self.quit:
break out
} }
} }
} }
@ -202,7 +187,7 @@ done:
} }
// Notify all subscribers // Notify all subscribers
self.eth.Reactor().Post("newTx:post", tx) self.eth.EventMux().Post(TxEvent{TxPost, tx})
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
handled = append(handled, tx) handled = append(handled, tx)
@ -293,7 +278,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
statelogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4]) statelogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
if dontReact == false { if dontReact == false {
sm.eth.Reactor().Post("newBlock", block) sm.eth.EventMux().Post(NewBlockEvent{block})
state.Manifest().Reset() state.Manifest().Reset()
} }
@ -434,7 +419,7 @@ func (sm *StateManager) createBloomFilter(state *ethstate.State) *BloomFilter {
bloomf.Set(msg.From) bloomf.Set(msg.From)
} }
sm.eth.Reactor().Post("messages", state.Manifest().Messages) sm.eth.EventMux().Post(state.Manifest().Messages)
return bloomf return bloomf
} }

View File

@ -24,6 +24,7 @@ type TxMsgTy byte
const ( const (
TxPre = iota TxPre = iota
TxPost TxPost
minGasPrice = 1000000 minGasPrice = 1000000
) )
@ -160,7 +161,7 @@ out:
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash()) txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
// Notify the subscribers // Notify the subscribers
pool.Ethereum.Reactor().Post("newTx:pre", tx) pool.Ethereum.EventMux().Post(TxEvent{TxPre, tx})
} }
case <-pool.quit: case <-pool.quit:
break out break out

View File

@ -17,12 +17,11 @@ 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/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"github.com/ethereum/eth-go/eventer" "github.com/ethereum/eth-go/event"
) )
const ( const (
@ -60,7 +59,7 @@ type Ethereum struct {
// The block pool // The block pool
blockPool *BlockPool blockPool *BlockPool
// Eventer // Eventer
eventer *eventer.EventMachine eventMux *event.TypeMux
// Peers // Peers
peers *list.List peers *list.List
// Nonce // Nonce
@ -85,8 +84,6 @@ type Ethereum struct {
listening bool listening bool
reactor *ethreact.ReactorEngine
RpcServer *ethrpc.JsonRpcServer RpcServer *ethrpc.JsonRpcServer
keyManager *ethcrypto.KeyManager keyManager *ethcrypto.KeyManager
@ -129,8 +126,7 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
isUpToDate: true, isUpToDate: true,
filters: make(map[int]*ethchain.Filter), filters: make(map[int]*ethchain.Filter),
} }
ethereum.reactor = ethreact.New() ethereum.eventMux = event.NewTypeMux()
ethereum.eventer = eventer.New()
ethereum.blockPool = NewBlockPool(ethereum) ethereum.blockPool = NewBlockPool(ethereum)
ethereum.txPool = ethchain.NewTxPool(ethereum) ethereum.txPool = ethchain.NewTxPool(ethereum)
@ -143,10 +139,6 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
return ethereum, nil return ethereum, nil
} }
func (s *Ethereum) Reactor() *ethreact.ReactorEngine {
return s.reactor
}
func (s *Ethereum) KeyManager() *ethcrypto.KeyManager { func (s *Ethereum) KeyManager() *ethcrypto.KeyManager {
return s.keyManager return s.keyManager
} }
@ -169,8 +161,8 @@ func (s *Ethereum) TxPool() *ethchain.TxPool {
func (s *Ethereum) BlockPool() *BlockPool { func (s *Ethereum) BlockPool() *BlockPool {
return s.blockPool return s.blockPool
} }
func (s *Ethereum) Eventer() *eventer.EventMachine { func (s *Ethereum) EventMux() *event.TypeMux {
return s.eventer return s.eventMux
} }
func (self *Ethereum) Db() ethutil.Database { func (self *Ethereum) Db() ethutil.Database {
return self.db return self.db
@ -376,7 +368,7 @@ func (s *Ethereum) removePeerElement(e *list.Element) {
s.peers.Remove(e) s.peers.Remove(e)
s.reactor.Post("peerList", s.peers) s.eventMux.Post(PeerListEvent{s.peers})
} }
func (s *Ethereum) RemovePeer(p *Peer) { func (s *Ethereum) RemovePeer(p *Peer) {
@ -400,7 +392,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()
s.blockPool.Start() s.blockPool.Start()
s.stateManager.Start() s.stateManager.Start()
@ -524,8 +515,7 @@ func (s *Ethereum) Stop() {
} }
s.txPool.Stop() s.txPool.Stop()
s.stateManager.Stop() s.stateManager.Stop()
s.reactor.Flush() s.eventMux.Stop()
s.reactor.Stop()
s.blockPool.Stop() s.blockPool.Stop()
ethlogger.Infoln("Server stopped") ethlogger.Infoln("Server stopped")
@ -584,10 +574,10 @@ out:
select { select {
case <-upToDateTimer.C: case <-upToDateTimer.C:
if self.IsUpToDate() && !self.isUpToDate { if self.IsUpToDate() && !self.isUpToDate {
self.reactor.Post("chainSync", false) self.eventMux.Post(ChainSyncEvent{false})
self.isUpToDate = true self.isUpToDate = true
} else if !self.IsUpToDate() && self.isUpToDate { } else if !self.IsUpToDate() && self.isUpToDate {
self.reactor.Post("chainSync", true) self.eventMux.Post(ChainSyncEvent{true})
self.isUpToDate = false self.isUpToDate = false
} }
case <-self.quit: case <-self.quit:
@ -623,40 +613,30 @@ func (self *Ethereum) GetFilter(id int) *ethchain.Filter {
} }
func (self *Ethereum) filterLoop() { func (self *Ethereum) filterLoop() {
blockChan := make(chan ethreact.Event, 5)
messageChan := make(chan ethreact.Event, 5)
// Subscribe to events // Subscribe to events
reactor := self.Reactor() events := self.eventMux.Subscribe(ethchain.NewBlockEvent{}, ethstate.Messages(nil))
reactor.Subscribe("newBlock", blockChan) for event := range events.Chan() {
reactor.Subscribe("messages", messageChan) switch event := event.(type) {
out: case ethchain.NewBlockEvent:
for { self.filterMu.RLock()
select { for _, filter := range self.filters {
case <-self.quit: if filter.BlockCallback != nil {
break out filter.BlockCallback(event.Block)
case block := <-blockChan: }
if block, ok := block.Resource.(*ethchain.Block); ok { }
self.filterMu.RLock() self.filterMu.RUnlock()
for _, filter := range self.filters {
if filter.BlockCallback != nil { case ethstate.Messages:
filter.BlockCallback(block) self.filterMu.RLock()
for _, filter := range self.filters {
if filter.MessageCallback != nil {
msgs := filter.FilterMessages(event)
if len(msgs) > 0 {
filter.MessageCallback(msgs)
} }
} }
self.filterMu.RUnlock()
}
case msg := <-messageChan:
if messages, ok := msg.Resource.(ethstate.Messages); ok {
self.filterMu.RLock()
for _, filter := range self.filters {
if filter.MessageCallback != nil {
msgs := filter.FilterMessages(messages)
if len(msgs) > 0 {
filter.MessageCallback(msgs)
}
}
}
self.filterMu.RUnlock()
} }
self.filterMu.RUnlock()
} }
} }
} }

View File

@ -6,27 +6,37 @@ import (
"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/ethwire" "github.com/ethereum/eth-go/ethwire"
"github.com/ethereum/eth-go/event"
) )
var logger = ethlog.NewLogger("MINER") var logger = ethlog.NewLogger("MINER")
type Miner struct { type Miner struct {
pow ethchain.PoW pow ethchain.PoW
ethereum ethchain.EthManager ethereum ethchain.EthManager
coinbase []byte coinbase []byte
reactChan chan ethreact.Event txs ethchain.Transactions
txs ethchain.Transactions uncles []*ethchain.Block
uncles []*ethchain.Block block *ethchain.Block
block *ethchain.Block
powChan chan []byte events event.Subscription
powQuitChan chan ethreact.Event powQuitChan chan struct{}
quitChan chan chan error powDone chan struct{}
turbo bool turbo bool
} }
const (
Started = iota
Stopped
)
type Event struct {
Type int // Started || Stopped
Miner *Miner
}
func (self *Miner) GetPow() ethchain.PoW { func (self *Miner) GetPow() ethchain.PoW {
return self.pow return self.pow
} }
@ -48,46 +58,42 @@ func (self *Miner) ToggleTurbo() {
} }
func (miner *Miner) Start() { func (miner *Miner) Start() {
miner.reactChan = make(chan ethreact.Event, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in
miner.powChan = make(chan []byte, 1) // This is the channel that receives valid sha hashes for a given block
miner.powQuitChan = make(chan ethreact.Event, 1) // This is the channel that can exit the miner thread
miner.quitChan = make(chan chan error, 1)
// Insert initial TXs in our little miner 'pool' // Insert initial TXs in our little miner 'pool'
miner.txs = miner.ethereum.TxPool().Flush() miner.txs = miner.ethereum.TxPool().Flush()
miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase) miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase)
mux := miner.ethereum.EventMux()
miner.events = mux.Subscribe(ethchain.NewBlockEvent{}, ethchain.TxEvent{})
// Prepare inital block // Prepare inital block
//miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) //miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
go miner.listener() go miner.listener()
reactor := miner.ethereum.Reactor()
reactor.Subscribe("newBlock", miner.reactChan)
reactor.Subscribe("newTx:pre", miner.reactChan)
// We need the quit chan to be a Reactor event.
// The POW search method is actually blocking and if we don't
// listen to the reactor events inside of the pow itself
// The miner overseer will never get the reactor events themselves
// Only after the miner will find the sha
reactor.Subscribe("newBlock", miner.powQuitChan)
reactor.Subscribe("newTx:pre", miner.powQuitChan)
logger.Infoln("Started") logger.Infoln("Started")
mux.Post(Event{Started, miner})
}
reactor.Post("miner:start", miner) func (miner *Miner) Stop() {
logger.Infoln("Stopping...")
miner.events.Unsubscribe()
miner.ethereum.EventMux().Post(Event{Stopped, miner})
} }
func (miner *Miner) listener() { func (miner *Miner) listener() {
for { for {
select { miner.startMining()
case status := <-miner.quitChan:
logger.Infoln("Stopped")
status <- nil
return
case chanMessage := <-miner.reactChan:
if block, ok := chanMessage.Resource.(*ethchain.Block); ok { select {
case event, isopen := <-miner.events.Chan():
miner.stopMining()
if !isopen {
return
}
switch event := event.(type) {
case ethchain.NewBlockEvent:
block := event.Block
//logger.Infoln("Got new block via Reactor") //logger.Infoln("Got new block via Reactor")
if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 { if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 {
// TODO: Perhaps continue mining to get some uncle rewards // TODO: Perhaps continue mining to get some uncle rewards
@ -117,49 +123,44 @@ func (miner *Miner) listener() {
miner.uncles = append(miner.uncles, block) miner.uncles = append(miner.uncles, block)
} }
} }
}
if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok { case ethchain.TxEvent:
found := false if event.Type == ethchain.TxPre {
for _, ctx := range miner.txs { found := false
if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found { for _, ctx := range miner.txs {
break if found = bytes.Compare(ctx.Hash(), event.Tx.Hash()) == 0; found {
break
}
}
if found == false {
// Undo all previous commits
miner.block.Undo()
// Apply new transactions
miner.txs = append(miner.txs, event.Tx)
} }
}
if found == false {
// Undo all previous commits
miner.block.Undo()
// Apply new transactions
miner.txs = append(miner.txs, tx)
} }
} }
default:
miner.mineNewBlock() case <-miner.powDone:
// next iteration will start mining again
} }
} }
} }
func (miner *Miner) Stop() { func (miner *Miner) startMining() {
logger.Infoln("Stopping...") if miner.powDone == nil {
miner.powDone = make(chan struct{})
}
miner.powQuitChan = make(chan struct{})
go miner.mineNewBlock()
}
miner.powQuitChan <- ethreact.Event{} func (miner *Miner) stopMining() {
close(miner.powQuitChan)
status := make(chan error) <-miner.powDone
miner.quitChan <- status
<-status
reactor := miner.ethereum.Reactor()
reactor.Unsubscribe("newBlock", miner.powQuitChan)
reactor.Unsubscribe("newTx:pre", miner.powQuitChan)
reactor.Unsubscribe("newBlock", miner.reactChan)
reactor.Unsubscribe("newTx:pre", miner.reactChan)
reactor.Post("miner:stop", miner)
} }
func (self *Miner) mineNewBlock() { func (self *Miner) mineNewBlock() {
stateManager := self.ethereum.StateManager() stateManager := self.ethereum.StateManager()
self.block = self.ethereum.BlockChain().NewBlock(self.coinbase) self.block = self.ethereum.BlockChain().NewBlock(self.coinbase)
@ -195,8 +196,9 @@ func (self *Miner) mineNewBlock() {
logger.Infof("Mining on block. Includes %v transactions", len(self.txs)) logger.Infof("Mining on block. Includes %v transactions", len(self.txs))
// Find a valid nonce // Find a valid nonce
self.block.Nonce = self.pow.Search(self.block, self.powQuitChan) nonce := self.pow.Search(self.block, self.powQuitChan)
if self.block.Nonce != nil { if nonce != nil {
self.block.Nonce = nonce
err := self.ethereum.StateManager().Process(self.block, false) err := self.ethereum.StateManager().Process(self.block, false)
if err != nil { if err != nil {
logger.Infoln(err) logger.Infoln(err)
@ -208,4 +210,5 @@ func (self *Miner) mineNewBlock() {
self.txs = self.ethereum.TxPool().CurrentTransactions() self.txs = self.ethereum.TxPool().CurrentTransactions()
} }
} }
self.powDone <- struct{}{}
} }

11
events.go Normal file
View File

@ -0,0 +1,11 @@
package eth
import "container/list"
type PeerListEvent struct {
Peers *list.List
}
type ChainSyncEvent struct {
InSync bool
}

View File

@ -802,7 +802,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
p.versionKnown = true p.versionKnown = true
p.ethereum.PushPeer(p) p.ethereum.PushPeer(p)
p.ethereum.reactor.Post("peerList", p.ethereum.Peers()) p.ethereum.eventMux.Post(PeerListEvent{p.ethereum.Peers()})
p.protocolCaps = caps p.protocolCaps = caps
capsIt := caps.NewIterator() capsIt := caps.NewIterator()