Skip to content

Commit

Permalink
Anti-spam measurements against dust attack (#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 authored Sep 26, 2023
1 parent bd14202 commit 92574e6
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 {

This comment has been minimized.

Copy link
@coderofstuff

coderofstuff Sep 28, 2023

Contributor

@someone235 in the case where {len(inputs): 1, len(outputs): 2} and there's no coinbase input, this means that the fee must be 1.00000001 KAS for it to not enter into the spamTx else condition below. Is this expected?

A tx with 1 input and 2 outputs doesn't seem to be spam

This comment has been minimized.

Copy link
@michaelsutton

michaelsutton Sep 28, 2023

Collaborator

It will enter the else case just below which will mark it as spamTx, and if no other competition it will be added out of the for loop at line 193

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 92574e6

Please sign in to comment.