@ -19,11 +19,6 @@ var (
jsonlogger = logger . NewJsonLogger ( )
)
type ChainEvent struct {
Block * types . Block
Td * big . Int
}
type StateQuery interface {
GetAccount ( addr [ ] byte ) * state . StateObject
}
@ -93,13 +88,16 @@ type ChainManager struct {
transState * state . StateDB
txState * state . StateDB
quit chan struct { }
}
func NewChainManager ( db ethutil . Database , mux * event . TypeMux ) * ChainManager {
bc := & ChainManager { db : db , genesisBlock : GenesisBlock ( db ) , eventMux : mux }
bc := & ChainManager { db : db , genesisBlock : GenesisBlock ( db ) , eventMux : mux , quit : make ( chan struct { } ) }
bc . setLastBlock ( )
bc . transState = bc . State ( ) . Copy ( )
bc . txState = bc . State ( ) . Copy ( )
go bc . update ( )
return bc
}
@ -388,16 +386,24 @@ func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
}
func ( bc * ChainManager ) Stop ( ) {
if bc . CurrentBlock != nil {
chainlogger . Infoln ( "Stopped" )
}
close ( bc . quit )
}
type queueEvent struct {
queue [ ] interface { }
canonicalCount int
sideCount int
splitCount int
}
func ( self * ChainManager ) InsertChain ( chain types . Blocks ) error {
self . tsmu . Lock ( )
defer self . tsmu . Unlock ( )
//self.tsmu.Lock()
//defer self.tsmu.Unlock()
for _ , block := range chain {
// A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring.
var queue = make ( [ ] interface { } , len ( chain ) )
var queueEvent = queueEvent { queue : queue }
for i , block := range chain {
// Call in to the block processor and check for errors. It's likely that if one block fails
// all others will fail too (unless a known block is returned).
td , err := self . processor . Process ( block )
@ -414,7 +420,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
}
block . Td = td
var canonical , split bool
self . mu . Lock ( )
cblock := self . currentBlock
{
@ -426,41 +431,75 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
if td . Cmp ( self . td ) > 0 {
if block . Header ( ) . Number . Cmp ( new ( big . Int ) . Add ( cblock . Header ( ) . Number , ethutil . Big1 ) ) < 0 {
chainlogger . Infof ( "Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n" , block . Header ( ) . Number , block . Hash ( ) [ : 4 ] , td , cblock . Header ( ) . Number , cblock . Hash ( ) [ : 4 ] , self . td )
split = true
queue [ i ] = ChainSplitEvent { block }
queueEvent . splitCount ++
}
self . setTotalDifficulty ( td )
self . insert ( block )
canonical = true
/ *
jsonlogger . LogJson ( & logger . EthChainNewHead {
BlockHash : ethutil . Bytes2Hex ( block . Hash ( ) ) ,
BlockNumber : block . Number ( ) ,
ChainHeadHash : ethutil . Bytes2Hex ( cblock . Hash ( ) ) ,
BlockPrevHash : ethutil . Bytes2Hex ( block . ParentHash ( ) ) ,
} )
* /
self . setTransState ( state . New ( block . Root ( ) , self . db ) )
queue [ i ] = ChainEvent { block }
queueEvent . canonicalCount ++
} else {
queue [ i ] = ChainSideEvent { block }
queueEvent . sideCount ++
}
}
self . mu . Unlock ( )
if canonical {
/ *
jsonlogger . LogJson ( & logger . EthChainNewHead {
BlockHash : ethutil . Bytes2Hex ( block . Hash ( ) ) ,
BlockNumber : block . Number ( ) ,
ChainHeadHash : ethutil . Bytes2Hex ( cblock . Hash ( ) ) ,
BlockPrevHash : ethutil . Bytes2Hex ( block . ParentHash ( ) ) ,
} )
* /
self . setTransState ( state . New ( block . Root ( ) , self . db ) )
self . eventMux . Post ( ChainEvent { block , td } )
} else {
//self.eventMux.
}
if split {
self . setTxState ( state . New ( block . Root ( ) , self . db ) )
self . eventMux . Post ( ChainSplitEvent { block } )
}
}
// XXX put this in a goroutine?
go self . eventMux . Post ( queueEvent )
return nil
}
func ( self * ChainManager ) update ( ) {
events := self . eventMux . Subscribe ( queueEvent { } )
out :
for {
select {
case ev := <- events . Chan ( ) :
switch ev := ev . ( type ) {
case queueEvent :
for i , event := range ev . queue {
switch event := event . ( type ) {
case ChainEvent :
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
// and in most cases isn't even necessary.
if i == ev . canonicalCount {
self . eventMux . Post ( ChainHeadEvent { event . Block } )
}
case ChainSplitEvent :
// On chain splits we need to reset the transaction state. We can't be sure whether the actual
// state of the accounts are still valid.
if i == ev . splitCount {
self . setTxState ( state . New ( event . Block . Root ( ) , self . db ) )
}
}
self . eventMux . Post ( event )
}
}
case <- self . quit :
break out
}
}
}
// Satisfy state query interface
func ( self * ChainManager ) GetAccount ( addr [ ] byte ) * state . StateObject {
return self . State ( ) . GetAccount ( addr )