mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-21 06:16:45 +00:00

* Added model and stubs for all main methods * Add constructors to all main objects * Implement BlockCandidateTransactions * implement expireOldTransactions and expireOrphanTransactions * Rename isHighPriority to neverExpires * Add stub for checkDoubleSpends * Revert "Rename isHighPriority to neverExpires" This reverts commit b2da9a4a00c02fb380d2518cf54fa16257bd8423. * Imeplement transactionsOrderedByFeeRate * Orphan maps should be idToOrphan * Add error.go to mempool * Invert the condition for banning when mempool rejects a transaction * Move all model objects to model package * Implement getParentsInPool * Implemented mempoolUTXOSet.addTransaction * Implement removeTransaction, remove sanity checks * Implemented mempoolUTXOSet.checkDoubleSpends * Implemented removeOrphan * Implement removeOrphan * Implement maybeAddOrphan and AddOrphan * Implemented processOrphansAfterAcceptedTransaction * Implement transactionsPool.addTransaction * Implement RemoveTransaction * If a transaction was removed from the mempool - update it's redeemers in orphan pool as well * Use maximumOrphanTransactionCount * Add allowOrphans to ValidateAndInsertTransaction stub * Implement validateTransaction functions * Implement fillInputs * Implement ValidateAndInsertTransaction * Implement HandleNewBlockTransactions * Implement missing mempool interface methods * Add comments to exported functions * Call ValidateTransactionInIsolation where needed * Implement RevalidateHighPriorityTransactions * Rewire kaspad to use new mempool, and fix compilation errors * Update rebroadcast logic to use new structure * Handle non-standard transaction errors properly * Add mutex to mempool * bugfix: GetTransaction panics when ok is false * properly calculate targetBlocksPerSecond in config.go * Fix various lint errors and tests * Fix expected text in test for duplicate transactions * Skip the coinbase transaction in HandleNewBlockTransactions * Unorphan the correct transactions * Call ValidateTransactionAndPopulateWithConsensusData on unorphanTransaction * Re-apply policy_test as check_transactions_standard_test * removeTransaction: Remove redeemers in orphan pool as well * Remove redundant check for uint64 < 0 * Export and rename isDust -> IsTransactionOutputDust to allow usage by rothschild * Add allowOrphan to SubmitTransaction RPC request * Remove all implementation from mempool.go * tidy go mod * Don't pass acceptedOrphans to handleNewBlockTransactions * Use t.Errorf in stead of t.Fatalf * Remove minimum relay fee from TestDust, as it's no longer configurable * Add separate VirtualDAASCore method for faster retrieval where it's repeated multiple times * Broadcast all transactions that were accepted * Don't re-use GetVirtualDAAScore in GetVirtualInfo - this causes a deadlock * Use real transaction count, and not Orphan * Get mempool config from outside, incorporating values received from cli * Use MinRelayFee and MaxOrphanTxs from global kaspad config * Add explanation for the seemingly redundant check for transaction version in checkTransactionStandard * Update some comment * Convert creation of acceptedTransactions to a single line * Move mempoolUTXOSet out of checkDoubleSpends * Add test for attempt to insert double spend into mempool * fillInputs: Skip check for coinbase - it's always false in mempool * Clarify comment about removeRedeemers when removing random orphan * Don't remove high-priority transactions in limitTransactionCount * Use mempool.removeTransaction in limitTransactionCount * Add mutex comment to handleNewBlockTransactions * Return error from limitTransactionCount * Pluralize the map types * mempoolUTXOSet.removeTransaction: Don't restore utxo if it was not created in mempool * Don't evacuate from orphanPool high-priority transactions * Disallow double-spends in orphan pool * Don't use exported (and locking) methods from inside mempool * Check for double spends in mempool during revalidateTransaction * Add checkOrphanDuplicate * Add orphan to acceptedOrphans, not current * Add TestHighPriorityTransactions * Fix off-by-one error in limitTransactionCount * Add TestRevalidateHighPriorityTransactions * Remove checkDoubleSpends from revalidateTransaction * Fix TestRevalidateHighPriorityTransactions * Move check for MaximumOrphanCount to beggining of maybeAddOrphan * Rename all map type to singulateToSingularMap * limitOrphanPool only after the orphan was added * TestDoubleSpendInMempool: use createChildTxWhenParentTxWasAddedByConsensus instead of createTransactionWithUTXOEntry * Fix some comments * Have separate min/max transaction versions for mempool * Add comment on defaultMaximumOrphanTransactionCount to keep it small as long as we have recursion * Fix comment * Rename: createChildTxWhenParentTxWasAddedByConsensus -> createChildTxWhereParentTxWasAddedByConsensus * Handle error from createChildTxWhereParentTxWasAddedByConsensus * Rename createChildTxWhereParentTxWasAddedByConsensus -> createChildAndParentTxsAndAddParentToConsensus * Convert all MaximumXXX constants to uint64 * Add comment * remove mutex comments
204 lines
6.3 KiB
Go
204 lines
6.3 KiB
Go
package mempool
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/miningmanager/mempool/model"
|
|
)
|
|
|
|
type transactionsPool struct {
|
|
mempool *mempool
|
|
allTransactions model.IDToTransactionMap
|
|
highPriorityTransactions model.IDToTransactionMap
|
|
chainedTransactionsByPreviousOutpoint model.OutpointToTransactionMap
|
|
transactionsOrderedByFeeRate model.TransactionsOrderedByFeeRate
|
|
lastExpireScan uint64
|
|
}
|
|
|
|
func newTransactionsPool(mp *mempool) *transactionsPool {
|
|
return &transactionsPool{
|
|
mempool: mp,
|
|
allTransactions: model.IDToTransactionMap{},
|
|
highPriorityTransactions: model.IDToTransactionMap{},
|
|
chainedTransactionsByPreviousOutpoint: model.OutpointToTransactionMap{},
|
|
transactionsOrderedByFeeRate: model.TransactionsOrderedByFeeRate{},
|
|
lastExpireScan: 0,
|
|
}
|
|
}
|
|
|
|
func (tp *transactionsPool) addTransaction(transaction *externalapi.DomainTransaction,
|
|
parentTransactionsInPool model.OutpointToTransactionMap, isHighPriority bool) (*model.MempoolTransaction, error) {
|
|
|
|
virtualDAAScore, err := tp.mempool.consensus.GetVirtualDAAScore()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mempoolTransaction := model.NewMempoolTransaction(
|
|
transaction, parentTransactionsInPool, isHighPriority, virtualDAAScore)
|
|
|
|
err = tp.addMempoolTransaction(mempoolTransaction)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mempoolTransaction, nil
|
|
}
|
|
|
|
func (tp *transactionsPool) addMempoolTransaction(transaction *model.MempoolTransaction) error {
|
|
tp.allTransactions[*transaction.TransactionID()] = transaction
|
|
|
|
for outpoint, parentTransactionInPool := range transaction.ParentTransactionsInPool() {
|
|
tp.chainedTransactionsByPreviousOutpoint[outpoint] = parentTransactionInPool
|
|
}
|
|
|
|
tp.mempool.mempoolUTXOSet.addTransaction(transaction)
|
|
|
|
err := tp.transactionsOrderedByFeeRate.Push(transaction)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if transaction.IsHighPriority() {
|
|
tp.highPriorityTransactions[*transaction.TransactionID()] = transaction
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (tp *transactionsPool) removeTransaction(transaction *model.MempoolTransaction) error {
|
|
delete(tp.allTransactions, *transaction.TransactionID())
|
|
|
|
err := tp.transactionsOrderedByFeeRate.Remove(transaction)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
delete(tp.highPriorityTransactions, *transaction.TransactionID())
|
|
|
|
for outpoint := range transaction.ParentTransactionsInPool() {
|
|
delete(tp.chainedTransactionsByPreviousOutpoint, outpoint)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (tp *transactionsPool) expireOldTransactions() error {
|
|
virtualDAAScore, err := tp.mempool.consensus.GetVirtualDAAScore()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if virtualDAAScore-tp.lastExpireScan < tp.mempool.config.TransactionExpireScanIntervalDAAScore {
|
|
return nil
|
|
}
|
|
|
|
for _, mempoolTransaction := range tp.allTransactions {
|
|
// Never expire high priority transactions
|
|
if mempoolTransaction.IsHighPriority() {
|
|
continue
|
|
}
|
|
|
|
// Remove all transactions whose addedAtDAAScore is older then TransactionExpireIntervalDAAScore
|
|
if virtualDAAScore-mempoolTransaction.AddedAtDAAScore() > tp.mempool.config.TransactionExpireIntervalDAAScore {
|
|
err = tp.mempool.removeTransaction(mempoolTransaction.TransactionID(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
tp.lastExpireScan = virtualDAAScore
|
|
return nil
|
|
}
|
|
|
|
func (tp *transactionsPool) allReadyTransactions() []*externalapi.DomainTransaction {
|
|
result := []*externalapi.DomainTransaction{}
|
|
|
|
for _, mempoolTransaction := range tp.allTransactions {
|
|
if len(mempoolTransaction.ParentTransactionsInPool()) == 0 {
|
|
result = append(result, mempoolTransaction.Transaction())
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (tp *transactionsPool) getParentTransactionsInPool(
|
|
transaction *externalapi.DomainTransaction) model.OutpointToTransactionMap {
|
|
|
|
parentsTransactionsInPool := model.OutpointToTransactionMap{}
|
|
|
|
for _, input := range transaction.Inputs {
|
|
if transaction, ok := tp.allTransactions[input.PreviousOutpoint.TransactionID]; ok {
|
|
parentsTransactionsInPool[input.PreviousOutpoint] = transaction
|
|
}
|
|
}
|
|
|
|
return parentsTransactionsInPool
|
|
}
|
|
|
|
func (tp *transactionsPool) getRedeemers(transaction *model.MempoolTransaction) []*model.MempoolTransaction {
|
|
queue := []*model.MempoolTransaction{transaction}
|
|
redeemers := []*model.MempoolTransaction{}
|
|
for len(queue) > 0 {
|
|
var current *model.MempoolTransaction
|
|
current, queue = queue[0], queue[1:]
|
|
|
|
outpoint := externalapi.DomainOutpoint{TransactionID: *current.TransactionID()}
|
|
for i := range current.Transaction().Outputs {
|
|
outpoint.Index = uint32(i)
|
|
if redeemerTransaction, ok := tp.chainedTransactionsByPreviousOutpoint[outpoint]; ok {
|
|
queue = append(queue, redeemerTransaction)
|
|
redeemers = append(redeemers, redeemerTransaction)
|
|
}
|
|
}
|
|
}
|
|
return redeemers
|
|
}
|
|
|
|
func (tp *transactionsPool) limitTransactionCount() error {
|
|
currentIndex := 0
|
|
|
|
for uint64(len(tp.allTransactions)) > tp.mempool.config.MaximumTransactionCount {
|
|
var transactionToRemove *model.MempoolTransaction
|
|
for {
|
|
transactionToRemove = tp.transactionsOrderedByFeeRate.GetByIndex(currentIndex)
|
|
if !transactionToRemove.IsHighPriority() {
|
|
break
|
|
}
|
|
currentIndex++
|
|
if currentIndex >= len(tp.allTransactions) {
|
|
log.Warnf(
|
|
"Number of high-priority transactions in mempool (%d) is higher than maximum allowed (%d)",
|
|
len(tp.allTransactions), tp.mempool.config.MaximumTransactionCount)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
err := tp.mempool.removeTransaction(transactionToRemove.TransactionID(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tp *transactionsPool) getTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
|
if mempoolTransaction, ok := tp.allTransactions[*transactionID]; ok {
|
|
return mempoolTransaction.Transaction(), true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func (tp *transactionsPool) getAllTransactions() []*externalapi.DomainTransaction {
|
|
allTransactions := make([]*externalapi.DomainTransaction, 0, len(tp.allTransactions))
|
|
for _, mempoolTransaction := range tp.allTransactions {
|
|
allTransactions = append(allTransactions, mempoolTransaction.Transaction())
|
|
}
|
|
return allTransactions
|
|
}
|
|
|
|
func (tp *transactionsPool) transactionCount() int {
|
|
return len(tp.allTransactions)
|
|
}
|