Skip to content

Commit

Permalink
Anti-spam measurements against dust attack (kaspanet#2223)
Browse files Browse the repository at this point in the history
* BlockCandidateTransactions patch

* Fix condition

* Fix fee

* Fix bug

* Reject from mempool

* Fix hasCoinbaseInput

* Fix position

* Bump version to v0.12.14
  • Loading branch information
someone235 committed Nov 22, 2023
1 parent 387fade commit b335fa5
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 3 deletions.
1 change: 1 addition & 0 deletions domain/miningmanager/mempool/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const (
RejectDifficulty RejectCode = 0x44
RejectImmatureSpend RejectCode = 0x45
RejectBadOrphan RejectCode = 0x64
RejectSpamTx RejectCode = 0x65
)

// Map of reject codes back strings for pretty printing.
Expand Down
54 changes: 53 additions & 1 deletion domain/miningmanager/mempool/mempool.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package mempool

import (
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"sync"

"github.com/kaspanet/kaspad/domain/consensusreference"
Expand Down Expand Up @@ -141,7 +143,57 @@ func (mp *mempool) BlockCandidateTransactions() []*externalapi.DomainTransaction
mp.mtx.RLock()
defer mp.mtx.RUnlock()

return mp.transactionsPool.allReadyTransactions()
readyTxs := mp.transactionsPool.allReadyTransactions()
var candidateTxs []*externalapi.DomainTransaction
var spamTx *externalapi.DomainTransaction
var spamTxNewestUTXODaaScore uint64
for _, tx := range readyTxs {
if len(tx.Outputs) > len(tx.Inputs) {
hasCoinbaseInput := false
for _, input := range tx.Inputs {
if input.UTXOEntry.IsCoinbase() {
hasCoinbaseInput = true
break
}
}

numExtraOuts := len(tx.Outputs) - len(tx.Inputs)
if !hasCoinbaseInput && numExtraOuts > 2 && tx.Fee < uint64(numExtraOuts)*constants.SompiPerKaspa {
log.Debugf("Filtered spam tx %s", consensushashing.TransactionID(tx))
continue
}

if hasCoinbaseInput || tx.Fee > uint64(numExtraOuts)*constants.SompiPerKaspa {
candidateTxs = append(candidateTxs, tx)
} else {
txNewestUTXODaaScore := tx.Inputs[0].UTXOEntry.BlockDAAScore()
for _, input := range tx.Inputs {
if input.UTXOEntry.BlockDAAScore() > txNewestUTXODaaScore {
txNewestUTXODaaScore = input.UTXOEntry.BlockDAAScore()
}
}

if spamTx != nil {
if txNewestUTXODaaScore < spamTxNewestUTXODaaScore {
spamTx = tx
spamTxNewestUTXODaaScore = txNewestUTXODaaScore
}
} else {
spamTx = tx
spamTxNewestUTXODaaScore = txNewestUTXODaaScore
}
}
} else {
candidateTxs = append(candidateTxs, tx)
}
}

if spamTx != nil {
log.Debugf("Adding spam tx candidate %s", consensushashing.TransactionID(spamTx))
candidateTxs = append(candidateTxs, spamTx)
}

return candidateTxs
}

func (mp *mempool) RevalidateHighPriorityTransactions() (validTransactions []*externalapi.DomainTransaction, err error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package mempool

import (
"fmt"

"github.com/kaspanet/kaspad/infrastructure/logger"

"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
Expand Down
15 changes: 15 additions & 0 deletions domain/miningmanager/mempool/validate_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mempool

import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"

"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
Expand Down Expand Up @@ -44,6 +45,20 @@ func (mp *mempool) validateTransactionInIsolation(transaction *externalapi.Domai
}

func (mp *mempool) validateTransactionInContext(transaction *externalapi.DomainTransaction) error {
hasCoinbaseInput := false
for _, input := range transaction.Inputs {
if input.UTXOEntry.IsCoinbase() {
hasCoinbaseInput = true
break
}
}

numExtraOuts := len(transaction.Outputs) - len(transaction.Inputs)
if !hasCoinbaseInput && numExtraOuts > 2 && transaction.Fee < uint64(numExtraOuts)*constants.SompiPerKaspa {
log.Warnf("Rejected spam tx %s from mempool (%d outputs)", consensushashing.TransactionID(transaction), len(transaction.Outputs))
return transactionRuleError(RejectSpamTx, fmt.Sprintf("Rejected spam tx %s from mempool", consensushashing.TransactionID(transaction)))
}

if !mp.config.AcceptNonStandard {
err := mp.checkTransactionStandardInContext(transaction)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs
const (
appMajor uint = 0
appMinor uint = 12
appPatch uint = 13
appPatch uint = 14
)

// appBuild is defined as a variable so it can be overridden during the build
Expand Down

0 comments on commit b335fa5

Please sign in to comment.