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
|
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
|
// processBlockMsg is a message type to be sent across the message channel
|
||||||
// for requested a block is processed. Note this call differs from blockMsg
|
// 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
|
// above in that blockMsg is intended for blocks that came from peers and have
|
||||||
|
@ -1109,6 +1123,13 @@ out:
|
||||||
err: err,
|
err: err,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case fetchTransactionStoreMsg:
|
||||||
|
txStore, err := b.blockChain.FetchTransactionStore(msg.tx)
|
||||||
|
msg.reply <- fetchTransactionStoreResponse{
|
||||||
|
TxStore: txStore,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
|
||||||
case processBlockMsg:
|
case processBlockMsg:
|
||||||
isOrphan, err := b.blockChain.ProcessBlock(
|
isOrphan, err := b.blockChain.ProcessBlock(
|
||||||
msg.block, b.server.timeSource,
|
msg.block, b.server.timeSource,
|
||||||
|
@ -1371,6 +1392,15 @@ func (b *blockManager) CalcNextRequiredDifficulty(timestamp time.Time) (uint32,
|
||||||
return response.difficulty, response.err
|
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
|
// 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
|
// chain. It is funneled through the block manager since btcchain is not safe
|
||||||
// for concurrent access.
|
// for concurrent access.
|
||||||
|
|
|
@ -369,7 +369,6 @@ func NewBlockTemplate(mempool *txMemPool, payToAddress btcutil.Address) (*BlockT
|
||||||
blockManager := mempool.server.blockManager
|
blockManager := mempool.server.blockManager
|
||||||
timeSource := mempool.server.timeSource
|
timeSource := mempool.server.timeSource
|
||||||
chainState := &blockManager.chainState
|
chainState := &blockManager.chainState
|
||||||
chain := blockManager.blockChain
|
|
||||||
|
|
||||||
// Extend the most recently known best block.
|
// Extend the most recently known best block.
|
||||||
chainState.Lock()
|
chainState.Lock()
|
||||||
|
@ -458,7 +457,7 @@ mempoolLoop:
|
||||||
// inputs from the mempool since a transaction which depends on
|
// inputs from the mempool since a transaction which depends on
|
||||||
// other transactions in the mempool must come after those
|
// other transactions in the mempool must come after those
|
||||||
// dependencies in the final generated block.
|
// dependencies in the final generated block.
|
||||||
txStore, err := chain.FetchTransactionStore(tx)
|
txStore, err := blockManager.FetchTransactionStore(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
minrLog.Warnf("Unable to fetch transaction store for "+
|
minrLog.Warnf("Unable to fetch transaction store for "+
|
||||||
"tx %s: %v", tx.Sha(), err)
|
"tx %s: %v", tx.Sha(), err)
|
||||||
|
|
Loading…
Reference in a new issue