Use removeRedeemers correctly

This commit is contained in:
Ori Newman 2023-11-22 22:10:21 +02:00
parent 387fade044
commit 659260f4eb
6 changed files with 104 additions and 23 deletions

View File

@ -147,16 +147,12 @@ func (btb *blockTemplateBuilder) BuildBlockTemplate(
invalidTxsErr := ruleerrors.ErrInvalidTransactionsInNewBlock{} invalidTxsErr := ruleerrors.ErrInvalidTransactionsInNewBlock{}
if errors.As(err, &invalidTxsErr) { if errors.As(err, &invalidTxsErr) {
log.Criticalf("consensusReference.Consensus().BuildBlock returned invalid txs in BuildBlockTemplate") log.Criticalf("consensusReference.Consensus().BuildBlock returned invalid txs in BuildBlockTemplate")
invalidTxs := make([]*consensusexternalapi.DomainTransaction, 0, len(invalidTxsErr.InvalidTransactions)) err = btb.mempool.RemoveInvalidTransactions(&invalidTxsErr)
for _, tx := range invalidTxsErr.InvalidTransactions {
invalidTxs = append(invalidTxs, tx.Transaction)
}
err = btb.mempool.RemoveTransactions(invalidTxs, true)
if err != nil { if err != nil {
// mempool.RemoveTransactions might return errors in situations that are perfectly fine in this context. // mempool.RemoveInvalidTransactions might return errors in situations that are perfectly fine in this context.
// TODO: Once the mempool invariants are clear, this should be converted back `return nil, err`: // TODO: Once the mempool invariants are clear, this should be converted back `return nil, err`:
// https://github.com/kaspanet/kaspad/issues/1553 // https://github.com/kaspanet/kaspad/issues/1553
log.Criticalf("Error from mempool.RemoveTransactions: %+v", err) log.Criticalf("Error from mempool.RemoveInvalidTransactions: %+v", err)
} }
// We can call this recursively without worry because this should almost never happen // We can call this recursively without worry because this should almost never happen
return btb.BuildBlockTemplate(coinbaseData) return btb.BuildBlockTemplate(coinbaseData)

View File

@ -1,6 +1,8 @@
package mempool package mempool
import ( import (
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"sync" "sync"
"github.com/kaspanet/kaspad/domain/consensusreference" "github.com/kaspanet/kaspad/domain/consensusreference"
@ -151,11 +153,29 @@ func (mp *mempool) RevalidateHighPriorityTransactions() (validTransactions []*ex
return mp.revalidateHighPriorityTransactions() return mp.revalidateHighPriorityTransactions()
} }
func (mp *mempool) RemoveTransactions(transactions []*externalapi.DomainTransaction, removeRedeemers bool) error { func (mp *mempool) RemoveInvalidTransactions(err *ruleerrors.ErrInvalidTransactionsInNewBlock) error {
mp.mtx.Lock() mp.mtx.Lock()
defer mp.mtx.Unlock() defer mp.mtx.Unlock()
return mp.removeTransactions(transactions, removeRedeemers) for _, tx := range err.InvalidTransactions {
ruleErr, success := tx.Error.(ruleerrors.RuleError)
if !success {
continue
}
inner := ruleErr.Unwrap()
removeRedeemers := true
if _, ok := inner.(ruleerrors.ErrMissingTxOut); ok {
removeRedeemers = false
}
err := mp.removeTransaction(consensushashing.TransactionID(tx.Transaction), removeRedeemers)
if err != nil {
return err
}
}
return nil
} }
func (mp *mempool) RemoveTransaction(transactionID *externalapi.DomainTransactionID, removeRedeemers bool) error { func (mp *mempool) RemoveTransaction(transactionID *externalapi.DomainTransactionID, removeRedeemers bool) error {

View File

@ -2,20 +2,9 @@ package mempool
import ( import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/miningmanager/mempool/model" "github.com/kaspanet/kaspad/domain/miningmanager/mempool/model"
) )
func (mp *mempool) removeTransactions(transactions []*externalapi.DomainTransaction, removeRedeemers bool) error {
for _, transaction := range transactions {
err := mp.removeTransaction(consensushashing.TransactionID(transaction), removeRedeemers)
if err != nil {
return err
}
}
return nil
}
func (mp *mempool) removeTransaction(transactionID *externalapi.DomainTransactionID, removeRedeemers bool) error { func (mp *mempool) removeTransaction(transactionID *externalapi.DomainTransactionID, removeRedeemers bool) error {
if _, ok := mp.orphansPool.allOrphans[*transactionID]; ok { if _, ok := mp.orphansPool.allOrphans[*transactionID]; ok {
return mp.orphansPool.removeOrphan(transactionID, true) return mp.orphansPool.removeOrphan(transactionID, true)

View File

@ -10,16 +10,83 @@ func (mp *mempool) revalidateHighPriorityTransactions() ([]*externalapi.DomainTr
onEnd := logger.LogAndMeasureExecutionTime(log, "revalidateHighPriorityTransactions") onEnd := logger.LogAndMeasureExecutionTime(log, "revalidateHighPriorityTransactions")
defer onEnd() defer onEnd()
// We revalidate transactions in topological order in case there are dependencies between them
// Naturally transactions points to their dependencies, but since we want to start processing the dependencies
// first, we build the opposite DAG. We initially fill `queue` with transactions with no dependencies.
childrenByID := make(map[externalapi.DomainTransactionID]map[externalapi.DomainTransactionID]struct{})
queue := make([]externalapi.DomainTransactionID, 0, len(mp.transactionsPool.highPriorityTransactions))
for id, transaction := range mp.transactionsPool.highPriorityTransactions {
hasParents := false
for _, input := range transaction.Transaction().Inputs {
if _, ok := mp.transactionsPool.highPriorityTransactions[input.PreviousOutpoint.TransactionID]; !ok {
continue
}
hasParents = true
if _, ok := childrenByID[input.PreviousOutpoint.TransactionID]; !ok {
childrenByID[input.PreviousOutpoint.TransactionID] = make(map[externalapi.DomainTransactionID]struct{})
}
childrenByID[input.PreviousOutpoint.TransactionID][id] = struct{}{}
}
if !hasParents {
queue = append(queue, id)
}
}
invalidTransactions := make(map[externalapi.DomainTransactionID]struct{})
visited := make(map[externalapi.DomainTransactionID]struct{})
validTransactions := []*externalapi.DomainTransaction{} validTransactions := []*externalapi.DomainTransaction{}
for _, transaction := range mp.transactionsPool.highPriorityTransactions {
// Now we iterate the DAG in topological order using BFS
for len(queue) > 0 {
var txID externalapi.DomainTransactionID
txID, queue = queue[0], queue[1:]
if _, ok := visited[txID]; ok {
continue
}
visited[txID] = struct{}{}
if _, ok := invalidTransactions[txID]; ok {
continue
}
transaction := mp.transactionsPool.highPriorityTransactions[txID]
isValid, err := mp.revalidateTransaction(transaction) isValid, err := mp.revalidateTransaction(transaction)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !isValid { if !isValid {
// Invalidate the offspring of this transaction
invalidateQueue := []externalapi.DomainTransactionID{txID}
for len(invalidateQueue) > 0 {
var current externalapi.DomainTransactionID
current, invalidateQueue = invalidateQueue[0], invalidateQueue[1:]
if _, ok := invalidTransactions[current]; ok {
continue
}
invalidTransactions[current] = struct{}{}
if children, ok := childrenByID[current]; ok {
for child := range children {
invalidateQueue = append(invalidateQueue, child)
}
}
}
continue continue
} }
if children, ok := childrenByID[txID]; ok {
for child := range children {
queue = append(queue, child)
}
}
validTransactions = append(validTransactions, transaction.Transaction().Clone()) validTransactions = append(validTransactions, transaction.Transaction().Clone())
} }

View File

@ -2,6 +2,7 @@ package model
import ( import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
) )
// Mempool maintains a set of known transactions that // Mempool maintains a set of known transactions that
@ -11,7 +12,7 @@ type Mempool interface {
BlockCandidateTransactions() []*externalapi.DomainTransaction BlockCandidateTransactions() []*externalapi.DomainTransaction
ValidateAndInsertTransaction(transaction *externalapi.DomainTransaction, isHighPriority bool, allowOrphan bool) ( ValidateAndInsertTransaction(transaction *externalapi.DomainTransaction, isHighPriority bool, allowOrphan bool) (
acceptedTransactions []*externalapi.DomainTransaction, err error) acceptedTransactions []*externalapi.DomainTransaction, err error)
RemoveTransactions(txs []*externalapi.DomainTransaction, removeRedeemers bool) error RemoveInvalidTransactions(err *ruleerrors.ErrInvalidTransactionsInNewBlock) error
GetTransaction( GetTransaction(
transactionID *externalapi.DomainTransactionID, transactionID *externalapi.DomainTransactionID,
includeTransactionPool bool, includeTransactionPool bool,

View File

@ -52,6 +52,8 @@ func (ui *UTXOIndex) Reset() error {
ui.mutex.Lock() ui.mutex.Lock()
defer ui.mutex.Unlock() defer ui.mutex.Unlock()
log.Infof("Starting UTXO index reset")
err := ui.store.deleteAll() err := ui.store.deleteAll()
if err != nil { if err != nil {
return err return err
@ -88,7 +90,13 @@ func (ui *UTXOIndex) Reset() error {
} }
// This has to be done last to mark that the reset went smoothly and no reset has to be called next time. // This has to be done last to mark that the reset went smoothly and no reset has to be called next time.
return ui.store.updateAndCommitVirtualParentsWithoutTransaction(virtualInfo.ParentHashes) err = ui.store.updateAndCommitVirtualParentsWithoutTransaction(virtualInfo.ParentHashes)
if err != nil {
return err
}
log.Infof("Finished UTXO index reset")
return nil
} }
func (ui *UTXOIndex) isSynced() (bool, error) { func (ui *UTXOIndex) isSynced() (bool, error) {