diff --git a/domain/miningmanager/mempool/config.go b/domain/miningmanager/mempool/config.go index 9a34611fa..06cf5a4f7 100644 --- a/domain/miningmanager/mempool/config.go +++ b/domain/miningmanager/mempool/config.go @@ -11,6 +11,9 @@ const ( defaultTransactionExpireScanIntervalSeconds uint64 = 10 defaultOrphanExpireIntervalSeconds uint64 = 60 defaultOrphanExpireScanIntervalSeconds uint64 = 10 + + defaultMaximumOrphanTransactionSize = 100000 + defaultMaximumOrphanTransactions = 50 ) type config struct { @@ -18,6 +21,7 @@ type config struct { transactionExpireScanIntervalDAAScore uint64 orphanExpireIntervalDAAScore uint64 orphanExpireScanIntervalDAAScore uint64 + maximumOrphanTransactionSize int } func defaultConfig(dagParams *dagconfig.Params) *config { @@ -28,5 +32,6 @@ func defaultConfig(dagParams *dagconfig.Params) *config { transactionExpireScanIntervalDAAScore: defaultTransactionExpireScanIntervalSeconds / targetBlocksPerSecond, orphanExpireIntervalDAAScore: defaultOrphanExpireIntervalSeconds / targetBlocksPerSecond, orphanExpireScanIntervalDAAScore: defaultOrphanExpireScanIntervalSeconds / targetBlocksPerSecond, + maximumOrphanTransactionSize: defaultMaximumOrphanTransactionSize, } } diff --git a/domain/miningmanager/mempool/error.go b/domain/miningmanager/mempool/error.go index bc51bb3a0..c86e97d41 100644 --- a/domain/miningmanager/mempool/error.go +++ b/domain/miningmanager/mempool/error.go @@ -39,32 +39,34 @@ type RejectCode uint8 // These constants define the various supported reject codes. const ( - RejectMalformed RejectCode = 0x01 - RejectInvalid RejectCode = 0x10 - RejectObsolete RejectCode = 0x11 - RejectDuplicate RejectCode = 0x12 - RejectNotRequested RejectCode = 0x13 - RejectNonstandard RejectCode = 0x40 - RejectDust RejectCode = 0x41 - RejectInsufficientFee RejectCode = 0x42 - RejectFinality RejectCode = 0x43 - RejectDifficulty RejectCode = 0x44 - RejectImmatureSpend RejectCode = 0x45 + RejectMalformed RejectCode = 0x01 + RejectInvalid RejectCode = 0x10 + RejectObsolete RejectCode = 0x11 + RejectDuplicate RejectCode = 0x12 + RejectNotRequested RejectCode = 0x13 + RejectNonstandard RejectCode = 0x40 + RejectDust RejectCode = 0x41 + RejectInsufficientFee RejectCode = 0x42 + RejectFinality RejectCode = 0x43 + RejectDifficulty RejectCode = 0x44 + RejectImmatureSpend RejectCode = 0x45 + RejectIncompatibleOrphan RejectCode = 0x64 ) // Map of reject codes back strings for pretty printing. var rejectCodeStrings = map[RejectCode]string{ - RejectMalformed: "REJECT_MALFORMED", - RejectInvalid: "REJECT_INVALID", - RejectObsolete: "REJECT_OBSOLETE", - RejectDuplicate: "REJECT_DUPLICATE", - RejectNonstandard: "REJECT_NONSTANDARD", - RejectDust: "REJECT_DUST", - RejectInsufficientFee: "REJECT_INSUFFICIENTFEE", - RejectFinality: "REJECT_FINALITY", - RejectDifficulty: "REJECT_DIFFICULTY", - RejectNotRequested: "REJECT_NOTREQUESTED", - RejectImmatureSpend: "REJECT_IMMATURESPEND", + RejectMalformed: "REJECT_MALFORMED", + RejectInvalid: "REJECT_INVALID", + RejectObsolete: "REJECT_OBSOLETE", + RejectDuplicate: "REJECT_DUPLICATE", + RejectNonstandard: "REJECT_NON_STANDARD", + RejectDust: "REJECT_DUST", + RejectInsufficientFee: "REJECT_INSUFFICIENT_FEE", + RejectFinality: "REJECT_FINALITY", + RejectDifficulty: "REJECT_DIFFICULTY", + RejectNotRequested: "REJECT_NOT_REQUESTED", + RejectImmatureSpend: "REJECT_IMMATURE_SPEND", + RejectIncompatibleOrphan: "REJECT_INCOMPATIBLE_ORPHAN", } // String returns the RejectCode in human-readable form. diff --git a/domain/miningmanager/mempool/orphan_pool.go b/domain/miningmanager/mempool/orphan_pool.go index b97a82e7c..615676eb4 100644 --- a/domain/miningmanager/mempool/orphan_pool.go +++ b/domain/miningmanager/mempool/orphan_pool.go @@ -1,7 +1,10 @@ package mempool import ( + "fmt" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/utils/estimatedsize" "github.com/kaspanet/kaspad/domain/miningmanager/mempool/model" "github.com/pkg/errors" ) @@ -25,10 +28,51 @@ func newOrphansPool(mp *mempool) *orphansPool { } } -func (op *orphansPool) maybeAddOrphan(transaction *externalapi.DomainTransaction, - missingParents []*externalapi.DomainTransactionID, isHighPriority bool) error { +func (op *orphansPool) maybeAddOrphan(transaction *externalapi.DomainTransaction, isHighPriority bool) error { + serializedLength := estimatedsize.TransactionEstimatedSerializedSize(transaction) + if serializedLength > uint64(op.mempool.config.maximumOrphanTransactionSize) { + str := fmt.Sprintf("orphan transaction size of %d bytes is "+ + "larger than max allowed size of %d bytes", + serializedLength, op.mempool.config.maximumOrphanTransactionSize) + return txRuleError(RejectIncompatibleOrphan, str) + } + if op.mempool.config.maximumOrphanTransactionSize <= 0 { + return nil + } + for len(op.allOrphans) >= op.mempool.config.maximumOrphanTransactionSize { + // Don't remove redeemers in the case of a random eviction since + // it is quite possible it might be needed again shortly. + err := op.removeOrphan(op.randomOrphan().TransactionID(), false) + if err != nil { + return err + } + } - panic("orphansPool.maybeAddOrphan not implemented") // TODO (Mike) + return op.addOrphan(transaction, isHighPriority) +} + +func (op *orphansPool) addOrphan(transaction *externalapi.DomainTransaction, isHighPriority bool) error { + virtualDAAScore, err := op.mempool.virtualDAAScore() + if err != nil { + return err + } + orphanTransaction := &model.OrphanTransaction{ + Transaction: transaction, + IsHighPriority: isHighPriority, + AddedAtDAAScore: virtualDAAScore, + } + + op.allOrphans[*orphanTransaction.TransactionID()] = orphanTransaction + for _, input := range transaction.Inputs { + if input.UTXOEntry == nil { + if _, ok := op.orphansByPreviousOutpoint[input.PreviousOutpoint]; !ok { + op.orphansByPreviousOutpoint[input.PreviousOutpoint] = idToOrphan{} + } + op.orphansByPreviousOutpoint[input.PreviousOutpoint][*orphanTransaction.TransactionID()] = orphanTransaction + } + } + + return nil } func (op *orphansPool) processOrphansAfterAcceptedTransaction(acceptedTransaction *model.MempoolTransaction) ( @@ -125,9 +169,7 @@ func (op *orphansPool) expireOrphanTransactions() error { // Remove all transactions whose addedAtDAAScore is older then transactionExpireIntervalDAAScore if virtualDAAScore-orphanTransaction.AddedAtDAAScore > op.mempool.config.orphanExpireIntervalDAAScore { - // Don't remove redeemers in the case of a random eviction since - // it is quite possible it might be needed again shortly. - err = op.removeOrphan(orphanTransaction.TransactionID(), false) + err = op.removeOrphan(orphanTransaction.TransactionID(), true) if err != nil { return err } @@ -137,3 +179,11 @@ func (op *orphansPool) expireOrphanTransactions() error { op.lastExpireScan = virtualDAAScore return nil } + +func (op *orphansPool) randomOrphan() *model.OrphanTransaction { + for _, orphan := range op.allOrphans { + return orphan + } + + return nil +}