Fix race in FetchTransactionStore
Because FetchTransactionStore in GetBlockTemplate occasionally accesses the internal blockchain memory structure while it is being read or modified, a race can occur. To prevent this, FetchTransactionStore is instead routed through the internal channel for blockchain requests.
This commit is contained in:
parent
4a1445a032
commit
4696d16ed4
2 changed files with 31 additions and 2 deletions
|
@ -107,6 +107,20 @@ type processBlockResponse struct {
|
|||
err error
|
||||
}
|
||||
|
||||
// fetchTransactionStoreResponse is a response sent to the reply channel of a
|
||||
// fetchTransactionStoreMsg.
|
||||
type fetchTransactionStoreResponse struct {
|
||||
TxStore blockchain.TxStore
|
||||
err error
|
||||
}
|
||||
|
||||
// fetchTransactionStoreMsg is a message type to be sent across the message
|
||||
// channel fetching the tx input store for some Tx.
|
||||
type fetchTransactionStoreMsg struct {
|
||||
tx *btcutil.Tx
|
||||
reply chan fetchTransactionStoreResponse
|
||||
}
|
||||
|
||||
// processBlockMsg is a message type to be sent across the message channel
|
||||
// for requested a block is processed. Note this call differs from blockMsg
|
||||
// above in that blockMsg is intended for blocks that came from peers and have
|
||||
|
@ -1109,6 +1123,13 @@ out:
|
|||
err: err,
|
||||
}
|
||||
|
||||
case fetchTransactionStoreMsg:
|
||||
txStore, err := b.blockChain.FetchTransactionStore(msg.tx)
|
||||
msg.reply <- fetchTransactionStoreResponse{
|
||||
TxStore: txStore,
|
||||
err: err,
|
||||
}
|
||||
|
||||
case processBlockMsg:
|
||||
isOrphan, err := b.blockChain.ProcessBlock(
|
||||
msg.block, b.server.timeSource,
|
||||
|
@ -1371,6 +1392,15 @@ func (b *blockManager) CalcNextRequiredDifficulty(timestamp time.Time) (uint32,
|
|||
return response.difficulty, response.err
|
||||
}
|
||||
|
||||
// FetchTransactionStore makes use of FetchTransactionStore on an internal
|
||||
// instance of a block chain. It is safe for concurrent access.
|
||||
func (b *blockManager) FetchTransactionStore(tx *btcutil.Tx) (blockchain.TxStore, error) {
|
||||
reply := make(chan fetchTransactionStoreResponse, 1)
|
||||
b.msgChan <- fetchTransactionStoreMsg{tx: tx, reply: reply}
|
||||
response := <-reply
|
||||
return response.TxStore, response.err
|
||||
}
|
||||
|
||||
// ProcessBlock makes use of ProcessBlock on an internal instance of a block
|
||||
// chain. It is funneled through the block manager since btcchain is not safe
|
||||
// for concurrent access.
|
||||
|
|
|
@ -369,7 +369,6 @@ func NewBlockTemplate(mempool *txMemPool, payToAddress btcutil.Address) (*BlockT
|
|||
blockManager := mempool.server.blockManager
|
||||
timeSource := mempool.server.timeSource
|
||||
chainState := &blockManager.chainState
|
||||
chain := blockManager.blockChain
|
||||
|
||||
// Extend the most recently known best block.
|
||||
chainState.Lock()
|
||||
|
@ -458,7 +457,7 @@ mempoolLoop:
|
|||
// inputs from the mempool since a transaction which depends on
|
||||
// other transactions in the mempool must come after those
|
||||
// dependencies in the final generated block.
|
||||
txStore, err := chain.FetchTransactionStore(tx)
|
||||
txStore, err := blockManager.FetchTransactionStore(tx)
|
||||
if err != nil {
|
||||
minrLog.Warnf("Unable to fetch transaction store for "+
|
||||
"tx %s: %v", tx.Sha(), err)
|
||||
|
|
Loading…
Reference in a new issue