mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-02-27 21:53:21 +00:00
Compare commits
26 Commits
patch3
...
mempool-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45ba3bec43 | ||
|
|
befc0172ee | ||
|
|
82e807cf5a | ||
|
|
bfa804b300 | ||
|
|
7115b54d3f | ||
|
|
25cc6184d6 | ||
|
|
4e46be67a4 | ||
|
|
35eb4b9d87 | ||
|
|
2402d48c3e | ||
|
|
9a1d548e7f | ||
|
|
65da2462b8 | ||
|
|
4335a8eaf8 | ||
|
|
d88fe305c5 | ||
|
|
099f023b5b | ||
|
|
2294559198 | ||
|
|
71875c99d6 | ||
|
|
c099fd2986 | ||
|
|
17357a0fca | ||
|
|
01edc43e36 | ||
|
|
b412cd9bbc | ||
|
|
e5e266ff0b | ||
|
|
b2da9a4a00 | ||
|
|
ff10ce145a | ||
|
|
619b7ab8cd | ||
|
|
04c98ac6df | ||
|
|
94ddf0aab0 |
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/domain"
|
"github.com/kaspanet/kaspad/domain"
|
||||||
"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/consensus/utils/consensushashing"
|
||||||
"github.com/kaspanet/kaspad/domain/miningmanager/mempool"
|
"github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -175,15 +175,15 @@ func (flow *handleRelayedTransactionsFlow) receiveTransactions(requestedTransact
|
|||||||
|
|
||||||
err = flow.Domain().MiningManager().ValidateAndInsertTransaction(tx, true)
|
err = flow.Domain().MiningManager().ValidateAndInsertTransaction(tx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ruleErr := &mempool.RuleError{}
|
ruleErr := &mempool_old.RuleError{}
|
||||||
if !errors.As(err, ruleErr) {
|
if !errors.As(err, ruleErr) {
|
||||||
return errors.Wrapf(err, "failed to process transaction %s", txID)
|
return errors.Wrapf(err, "failed to process transaction %s", txID)
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBan := true
|
shouldBan := false
|
||||||
if txRuleErr := (&mempool.TxRuleError{}); errors.As(ruleErr.Err, txRuleErr) {
|
if txRuleErr := (&mempool_old.TxRuleError{}); errors.As(ruleErr.Err, txRuleErr) {
|
||||||
if txRuleErr.RejectCode != mempool.RejectInvalid {
|
if txRuleErr.RejectCode == mempool_old.RejectInvalid {
|
||||||
shouldBan = false
|
shouldBan = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
"github.com/kaspanet/kaspad/domain/miningmanager/mempool"
|
"github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@@ -23,7 +23,7 @@ func HandleSubmitTransaction(context *rpccontext.Context, _ *router.Router, requ
|
|||||||
transactionID := consensushashing.TransactionID(domainTransaction)
|
transactionID := consensushashing.TransactionID(domainTransaction)
|
||||||
err = context.ProtocolManager.AddTransaction(domainTransaction)
|
err = context.ProtocolManager.AddTransaction(domainTransaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.As(err, &mempool.RuleError{}) {
|
if !errors.As(err, &mempool_old.RuleError{}) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/domain/miningmanager/blocktemplatebuilder"
|
"github.com/kaspanet/kaspad/domain/miningmanager/blocktemplatebuilder"
|
||||||
mempoolpkg "github.com/kaspanet/kaspad/domain/miningmanager/mempool"
|
mempoolpkg "github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Factory instantiates new mining managers
|
// Factory instantiates new mining managers
|
||||||
|
|||||||
39
domain/miningmanager/mempool/config.go
Normal file
39
domain/miningmanager/mempool/config.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package mempool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultTransactionExpireIntervalSeconds uint64 = 60
|
||||||
|
defaultTransactionExpireScanIntervalSeconds uint64 = 10
|
||||||
|
defaultOrphanExpireIntervalSeconds uint64 = 60
|
||||||
|
defaultOrphanExpireScanIntervalSeconds uint64 = 10
|
||||||
|
|
||||||
|
defaultMaximumOrphanTransactionSize = 100000
|
||||||
|
defaultMaximumOrphanTransactionCount = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
transactionExpireIntervalDAAScore uint64
|
||||||
|
transactionExpireScanIntervalDAAScore uint64
|
||||||
|
orphanExpireIntervalDAAScore uint64
|
||||||
|
orphanExpireScanIntervalDAAScore uint64
|
||||||
|
maximumOrphanTransactionSize int
|
||||||
|
maximumOrphanTransactionCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfig(dagParams *dagconfig.Params) *config {
|
||||||
|
targetBlocksPerSecond := uint64(dagParams.TargetTimePerBlock / time.Second)
|
||||||
|
|
||||||
|
return &config{
|
||||||
|
transactionExpireIntervalDAAScore: defaultTransactionExpireIntervalSeconds / targetBlocksPerSecond,
|
||||||
|
transactionExpireScanIntervalDAAScore: defaultTransactionExpireScanIntervalSeconds / targetBlocksPerSecond,
|
||||||
|
orphanExpireIntervalDAAScore: defaultOrphanExpireIntervalSeconds / targetBlocksPerSecond,
|
||||||
|
orphanExpireScanIntervalDAAScore: defaultOrphanExpireScanIntervalSeconds / targetBlocksPerSecond,
|
||||||
|
maximumOrphanTransactionSize: defaultMaximumOrphanTransactionSize,
|
||||||
|
maximumOrphanTransactionCount: defaultMaximumOrphanTransactionCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,32 +39,34 @@ type RejectCode uint8
|
|||||||
|
|
||||||
// These constants define the various supported reject codes.
|
// These constants define the various supported reject codes.
|
||||||
const (
|
const (
|
||||||
RejectMalformed RejectCode = 0x01
|
RejectMalformed RejectCode = 0x01
|
||||||
RejectInvalid RejectCode = 0x10
|
RejectInvalid RejectCode = 0x10
|
||||||
RejectObsolete RejectCode = 0x11
|
RejectObsolete RejectCode = 0x11
|
||||||
RejectDuplicate RejectCode = 0x12
|
RejectDuplicate RejectCode = 0x12
|
||||||
RejectNotRequested RejectCode = 0x13
|
RejectNotRequested RejectCode = 0x13
|
||||||
RejectNonstandard RejectCode = 0x40
|
RejectNonstandard RejectCode = 0x40
|
||||||
RejectDust RejectCode = 0x41
|
RejectDust RejectCode = 0x41
|
||||||
RejectInsufficientFee RejectCode = 0x42
|
RejectInsufficientFee RejectCode = 0x42
|
||||||
RejectFinality RejectCode = 0x43
|
RejectFinality RejectCode = 0x43
|
||||||
RejectDifficulty RejectCode = 0x44
|
RejectDifficulty RejectCode = 0x44
|
||||||
RejectImmatureSpend RejectCode = 0x45
|
RejectImmatureSpend RejectCode = 0x45
|
||||||
|
RejectIncompatibleOrphan RejectCode = 0x64
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map of reject codes back strings for pretty printing.
|
// Map of reject codes back strings for pretty printing.
|
||||||
var rejectCodeStrings = map[RejectCode]string{
|
var rejectCodeStrings = map[RejectCode]string{
|
||||||
RejectMalformed: "REJECT_MALFORMED",
|
RejectMalformed: "REJECT_MALFORMED",
|
||||||
RejectInvalid: "REJECT_INVALID",
|
RejectInvalid: "REJECT_INVALID",
|
||||||
RejectObsolete: "REJECT_OBSOLETE",
|
RejectObsolete: "REJECT_OBSOLETE",
|
||||||
RejectDuplicate: "REJECT_DUPLICATE",
|
RejectDuplicate: "REJECT_DUPLICATE",
|
||||||
RejectNonstandard: "REJECT_NONSTANDARD",
|
RejectNonstandard: "REJECT_NON_STANDARD",
|
||||||
RejectDust: "REJECT_DUST",
|
RejectDust: "REJECT_DUST",
|
||||||
RejectInsufficientFee: "REJECT_INSUFFICIENTFEE",
|
RejectInsufficientFee: "REJECT_INSUFFICIENT_FEE",
|
||||||
RejectFinality: "REJECT_FINALITY",
|
RejectFinality: "REJECT_FINALITY",
|
||||||
RejectDifficulty: "REJECT_DIFFICULTY",
|
RejectDifficulty: "REJECT_DIFFICULTY",
|
||||||
RejectNotRequested: "REJECT_NOTREQUESTED",
|
RejectNotRequested: "REJECT_NOT_REQUESTED",
|
||||||
RejectImmatureSpend: "REJECT_IMMATURESPEND",
|
RejectImmatureSpend: "REJECT_IMMATURE_SPEND",
|
||||||
|
RejectIncompatibleOrphan: "REJECT_INCOMPATIBLE_ORPHAN",
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the RejectCode in human-readable form.
|
// String returns the RejectCode in human-readable form.
|
||||||
|
|||||||
9
domain/miningmanager/mempool/helper_functions.go
Normal file
9
domain/miningmanager/mempool/helper_functions.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package mempool
|
||||||
|
|
||||||
|
func (mp *mempool) virtualDAAScore() (uint64, error) {
|
||||||
|
virtualInfo, err := mp.consensus.GetVirtualInfo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return virtualInfo.DAAScore, nil
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
71
domain/miningmanager/mempool/mempool_utxo_set.go
Normal file
71
domain/miningmanager/mempool/mempool_utxo_set.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package mempool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||||
|
"github.com/kaspanet/kaspad/domain/miningmanager/mempool/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mempoolUTXOSet struct {
|
||||||
|
mempool *mempool
|
||||||
|
poolUnspentOutputs model.OutpointToUTXOEntry
|
||||||
|
transactionsByPreviousOutpoint model.OutpointToTransaction
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMempoolUTXOSet(mp *mempool) *mempoolUTXOSet {
|
||||||
|
return &mempoolUTXOSet{
|
||||||
|
mempool: mp,
|
||||||
|
poolUnspentOutputs: model.OutpointToUTXOEntry{},
|
||||||
|
transactionsByPreviousOutpoint: model.OutpointToTransaction{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mpus *mempoolUTXOSet) addTransaction(transaction *model.MempoolTransaction) {
|
||||||
|
outpoint := &externalapi.DomainOutpoint{TransactionID: *transaction.TransactionID()}
|
||||||
|
|
||||||
|
for i, input := range transaction.Transaction().Inputs {
|
||||||
|
outpoint.Index = uint32(i)
|
||||||
|
|
||||||
|
delete(mpus.poolUnspentOutputs, *outpoint)
|
||||||
|
mpus.transactionsByPreviousOutpoint[input.PreviousOutpoint] = transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, output := range transaction.Transaction().Outputs {
|
||||||
|
outpoint := externalapi.DomainOutpoint{TransactionID: *transaction.TransactionID(), Index: uint32(i)}
|
||||||
|
|
||||||
|
mpus.poolUnspentOutputs[outpoint] =
|
||||||
|
utxo.NewUTXOEntry(output.Value, output.ScriptPublicKey, false, model.UnacceptedDAAScore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mpus *mempoolUTXOSet) removeTransaction(transaction *model.MempoolTransaction) {
|
||||||
|
for _, input := range transaction.Transaction().Inputs {
|
||||||
|
mpus.poolUnspentOutputs[input.PreviousOutpoint] = input.UTXOEntry
|
||||||
|
delete(mpus.transactionsByPreviousOutpoint, input.PreviousOutpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
outpoint := externalapi.DomainOutpoint{TransactionID: *transaction.TransactionID()}
|
||||||
|
for i := range transaction.Transaction().Outputs {
|
||||||
|
outpoint.Index = uint32(i)
|
||||||
|
|
||||||
|
delete(mpus.poolUnspentOutputs, outpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mpus *mempoolUTXOSet) checkDoubleSpends(transaction *model.MempoolTransaction) error {
|
||||||
|
outpoint := externalapi.DomainOutpoint{TransactionID: *transaction.TransactionID()}
|
||||||
|
|
||||||
|
for i, input := range transaction.Transaction().Inputs {
|
||||||
|
outpoint.Index = uint32(i)
|
||||||
|
|
||||||
|
if existingTransaction, exists := mpus.transactionsByPreviousOutpoint[input.PreviousOutpoint]; exists {
|
||||||
|
str := fmt.Sprintf("output %s already spent by transaction %s in the memory pool",
|
||||||
|
input.PreviousOutpoint, existingTransaction.TransactionID())
|
||||||
|
return txRuleError(RejectDuplicate, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
6
domain/miningmanager/mempool/model/constants.go
Normal file
6
domain/miningmanager/mempool/model/constants.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// UnacceptedDAAScore is used to for UTXOEntries that were created by transactions in the mempool.
|
||||||
|
const UnacceptedDAAScore = math.MaxUint64
|
||||||
14
domain/miningmanager/mempool/model/map_types.go
Normal file
14
domain/miningmanager/mempool/model/map_types.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IDToTransaction maps transactionID to a MempoolTransaction
|
||||||
|
type IDToTransaction map[externalapi.DomainTransactionID]*MempoolTransaction
|
||||||
|
|
||||||
|
// OutpointToUTXOEntry maps an outpoint to a UTXOEntry
|
||||||
|
type OutpointToUTXOEntry map[externalapi.DomainOutpoint]externalapi.UTXOEntry
|
||||||
|
|
||||||
|
// OutpointToTransaction maps an outpoint to a MempoolTransaction
|
||||||
|
type OutpointToTransaction map[externalapi.DomainOutpoint]*MempoolTransaction
|
||||||
49
domain/miningmanager/mempool/model/mempool_transaction.go
Normal file
49
domain/miningmanager/mempool/model/mempool_transaction.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MempoolTransaction represents a transaction inside the main TransactionPool
|
||||||
|
type MempoolTransaction struct {
|
||||||
|
transaction *externalapi.DomainTransaction
|
||||||
|
parentTransactionsInPool OutpointToTransaction
|
||||||
|
isHighPriority bool
|
||||||
|
addedAtDAAScore uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMempoolTransaction(
|
||||||
|
transaction *externalapi.DomainTransaction,
|
||||||
|
parentTransactionsInPool OutpointToTransaction,
|
||||||
|
isHighPriority bool,
|
||||||
|
addedAtDAAScore uint64,
|
||||||
|
) *MempoolTransaction {
|
||||||
|
return &MempoolTransaction{
|
||||||
|
transaction: transaction,
|
||||||
|
parentTransactionsInPool: parentTransactionsInPool,
|
||||||
|
isHighPriority: isHighPriority,
|
||||||
|
addedAtDAAScore: addedAtDAAScore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionID returns the ID of this MempoolTransaction
|
||||||
|
func (mt *MempoolTransaction) TransactionID() *externalapi.DomainTransactionID {
|
||||||
|
return consensushashing.TransactionID(mt.transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *MempoolTransaction) Transaction() *externalapi.DomainTransaction {
|
||||||
|
return mt.transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *MempoolTransaction) ParentTransactionsInPool() OutpointToTransaction {
|
||||||
|
return mt.parentTransactionsInPool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *MempoolTransaction) IsHighPriority() bool {
|
||||||
|
return mt.isHighPriority
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *MempoolTransaction) AddedAtDAAScore() uint64 {
|
||||||
|
return mt.addedAtDAAScore
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransactionsOrderedByFeeRate represents a set of MempoolTransactions ordered by their fee / mass rate
|
||||||
|
type TransactionsOrderedByFeeRate struct {
|
||||||
|
slice []*MempoolTransaction
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push inserts a transaction into the set, placing it in the correct place to preserve order
|
||||||
|
func (tobf *TransactionsOrderedByFeeRate) Push(transaction *MempoolTransaction) error {
|
||||||
|
index, err := tobf.findTransactionIndex(transaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tobf.slice = append(tobf.slice[:index],
|
||||||
|
append([]*MempoolTransaction{transaction}, tobf.slice[index:]...)...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the given transaction from the set.
|
||||||
|
// Returns an error if transaction does not exist in the set, or if the given transaction does not have mass
|
||||||
|
// and fee filled in.
|
||||||
|
func (tobf *TransactionsOrderedByFeeRate) Remove(transaction *MempoolTransaction) error {
|
||||||
|
index, err := tobf.findTransactionIndex(transaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
txID := transaction.TransactionID()
|
||||||
|
if !tobf.slice[index].TransactionID().Equal(txID) {
|
||||||
|
return errors.Errorf("Couldn't find %s in mp.orderedTransactionsByFeeRate", txID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tobf.RemoveAtIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAtIndex removes the transaction at the given index.
|
||||||
|
// Returns an error in case of out-of-bounds index.
|
||||||
|
func (tobf *TransactionsOrderedByFeeRate) RemoveAtIndex(index int) error {
|
||||||
|
if index < 0 || index > len(tobf.slice)-1 {
|
||||||
|
return errors.Errorf("Index %d is out of bound of this TransactionsOrderedByFeeRate", index)
|
||||||
|
}
|
||||||
|
tobf.slice = append(tobf.slice[:index], tobf.slice[index+1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tobf *TransactionsOrderedByFeeRate) findTransactionIndex(transaction *MempoolTransaction) (int, error) {
|
||||||
|
if transaction.Transaction().Fee == 0 || transaction.Transaction().Mass == 0 {
|
||||||
|
return 0, errors.Errorf("findTxIndexInOrderedTransactionsByFeeRate expects a transaction with " +
|
||||||
|
"populated fee and mass")
|
||||||
|
}
|
||||||
|
txID := transaction.TransactionID()
|
||||||
|
txFeeRate := float64(transaction.Transaction().Fee) / float64(transaction.Transaction().Mass)
|
||||||
|
|
||||||
|
return sort.Search(len(tobf.slice), func(i int) bool {
|
||||||
|
iElement := tobf.slice[i]
|
||||||
|
elementFeeRate := float64(iElement.Transaction().Fee) / float64(iElement.Transaction().Mass)
|
||||||
|
if elementFeeRate > txFeeRate {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if elementFeeRate == txFeeRate && txID.LessOrEqual(iElement.TransactionID()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
41
domain/miningmanager/mempool/model/orphan_transaction.go
Normal file
41
domain/miningmanager/mempool/model/orphan_transaction.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OrphanTransaction represents a transaction in the OrphanPool
|
||||||
|
type OrphanTransaction struct {
|
||||||
|
transaction *externalapi.DomainTransaction
|
||||||
|
isHighPriority bool
|
||||||
|
addedAtDAAScore uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrphanTransaction(
|
||||||
|
transaction *externalapi.DomainTransaction,
|
||||||
|
isHighPriority bool,
|
||||||
|
addedAtDAAScore uint64,
|
||||||
|
) *OrphanTransaction {
|
||||||
|
return &OrphanTransaction{
|
||||||
|
transaction: transaction,
|
||||||
|
isHighPriority: isHighPriority,
|
||||||
|
addedAtDAAScore: addedAtDAAScore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionID returns the ID of this OrphanTransaction
|
||||||
|
func (ot *OrphanTransaction) TransactionID() *externalapi.DomainTransactionID {
|
||||||
|
return consensushashing.TransactionID(ot.transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ot *OrphanTransaction) Transaction() *externalapi.DomainTransaction {
|
||||||
|
return ot.transaction
|
||||||
|
}
|
||||||
|
func (ot *OrphanTransaction) IsHighPriority() bool {
|
||||||
|
return ot.isHighPriority
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ot *OrphanTransaction) AddedAtDAAScore() uint64 {
|
||||||
|
return ot.addedAtDAAScore
|
||||||
|
}
|
||||||
8
domain/miningmanager/mempool/model/transaction.go
Normal file
8
domain/miningmanager/mempool/model/transaction.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
|
||||||
|
type Transaction interface {
|
||||||
|
TransactionID() *externalapi.DomainTransactionID
|
||||||
|
Transaction() *externalapi.DomainTransaction
|
||||||
|
}
|
||||||
260
domain/miningmanager/mempool/orphan_pool.go
Normal file
260
domain/miningmanager/mempool/orphan_pool.go
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
package mempool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type idToOrphan map[externalapi.DomainTransactionID]*model.OrphanTransaction
|
||||||
|
type previousOutpointToOrphans map[externalapi.DomainOutpoint]idToOrphan
|
||||||
|
|
||||||
|
type orphansPool struct {
|
||||||
|
mempool *mempool
|
||||||
|
allOrphans idToOrphan
|
||||||
|
orphansByPreviousOutpoint previousOutpointToOrphans
|
||||||
|
lastExpireScan uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOrphansPool(mp *mempool) *orphansPool {
|
||||||
|
return &orphansPool{
|
||||||
|
mempool: mp,
|
||||||
|
allOrphans: idToOrphan{},
|
||||||
|
orphansByPreviousOutpoint: previousOutpointToOrphans{},
|
||||||
|
lastExpireScan: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.maximumOrphanTransactionCount <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for len(op.allOrphans) >= op.mempool.config.maximumOrphanTransactionCount {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.NewOrphanTransaction(transaction, isHighPriority, 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) (
|
||||||
|
acceptedOrphans []*model.MempoolTransaction, err error) {
|
||||||
|
|
||||||
|
acceptedOrphans = []*model.MempoolTransaction{}
|
||||||
|
queue := []*model.MempoolTransaction{acceptedTransaction}
|
||||||
|
|
||||||
|
for len(queue) > 0 {
|
||||||
|
var current *model.MempoolTransaction
|
||||||
|
current, queue = queue[0], queue[1:]
|
||||||
|
|
||||||
|
outpoint := externalapi.DomainOutpoint{TransactionID: *current.TransactionID()}
|
||||||
|
for i, output := range current.Transaction().Outputs {
|
||||||
|
outpoint.Index = uint32(i)
|
||||||
|
orphans, ok := op.orphansByPreviousOutpoint[outpoint]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, orphan := range orphans {
|
||||||
|
for _, input := range orphan.Transaction().Inputs {
|
||||||
|
if input.PreviousOutpoint.Equal(&outpoint) {
|
||||||
|
input.UTXOEntry = utxo.NewUTXOEntry(output.Value, output.ScriptPublicKey, false,
|
||||||
|
model.UnacceptedDAAScore)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if countUnfilledInputs(orphan) == 0 {
|
||||||
|
unorphanedTransaction, err := op.unorphanTransaction(current)
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &RuleError{}) {
|
||||||
|
log.Infof("Failed to unorphan transaction %s due to rule error: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
acceptedOrphans = append(acceptedOrphans, unorphanedTransaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acceptedOrphans, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func countUnfilledInputs(orphan *model.OrphanTransaction) int {
|
||||||
|
unfilledInputs := 0
|
||||||
|
for _, input := range orphan.Transaction().Inputs {
|
||||||
|
if input.UTXOEntry == nil {
|
||||||
|
unfilledInputs++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unfilledInputs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *orphansPool) unorphanTransaction(orphanTransaction *model.MempoolTransaction) (*model.MempoolTransaction, error) {
|
||||||
|
err := op.removeOrphan(orphanTransaction.TransactionID(), false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = op.mempool.validateTransactionInContext(orphanTransaction.Transaction())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
virtualDAAScore, err := op.mempool.virtualDAAScore()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mempoolTransaction := model.NewMempoolTransaction(
|
||||||
|
orphanTransaction.Transaction(),
|
||||||
|
op.mempool.transactionsPool.getParentTransactionsInPool(orphanTransaction.Transaction()),
|
||||||
|
false,
|
||||||
|
virtualDAAScore,
|
||||||
|
)
|
||||||
|
err = op.mempool.transactionsPool.addMempoolTransaction(mempoolTransaction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mempoolTransaction, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *orphansPool) removeOrphan(orphanTransactionID *externalapi.DomainTransactionID, removeRedeemers bool) error {
|
||||||
|
orphanTransaction, ok := op.allOrphans[*orphanTransactionID]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(op.allOrphans, *orphanTransactionID)
|
||||||
|
|
||||||
|
for i, input := range orphanTransaction.Transaction().Inputs {
|
||||||
|
orphans, ok := op.orphansByPreviousOutpoint[input.PreviousOutpoint]
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("Input No. %d of %s (%s) doesn't exist in orphansByPreviousOutpoint",
|
||||||
|
i, orphanTransactionID, input.PreviousOutpoint)
|
||||||
|
}
|
||||||
|
delete(orphans, *orphanTransactionID)
|
||||||
|
if len(orphans) == 0 {
|
||||||
|
delete(op.orphansByPreviousOutpoint, input.PreviousOutpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if removeRedeemers {
|
||||||
|
err := op.removeRedeemersOf(orphanTransaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *orphansPool) removeRedeemersOf(transaction model.Transaction) error {
|
||||||
|
outpoint := externalapi.DomainOutpoint{TransactionID: *transaction.TransactionID()}
|
||||||
|
for i := range transaction.Transaction().Outputs {
|
||||||
|
outpoint.Index = uint32(i)
|
||||||
|
for _, orphan := range op.orphansByPreviousOutpoint[outpoint] {
|
||||||
|
// Recursive call is bound by size of orphan pool (which is very small)
|
||||||
|
err := op.removeOrphan(orphan.TransactionID(), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *orphansPool) expireOrphanTransactions() error {
|
||||||
|
virtualDAAScore, err := op.mempool.virtualDAAScore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if virtualDAAScore-op.lastExpireScan < op.mempool.config.orphanExpireScanIntervalDAAScore {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, orphanTransaction := range op.allOrphans {
|
||||||
|
// Never expire high priority transactions
|
||||||
|
if orphanTransaction.IsHighPriority() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all transactions whose addedAtDAAScore is older then transactionExpireIntervalDAAScore
|
||||||
|
if virtualDAAScore-orphanTransaction.AddedAtDAAScore() > op.mempool.config.orphanExpireIntervalDAAScore {
|
||||||
|
err = op.removeOrphan(orphanTransaction.TransactionID(), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
op.lastExpireScan = virtualDAAScore
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *orphansPool) updateOrphansAfterTransactionRemoved(
|
||||||
|
removedTransaction *model.MempoolTransaction, removeRedeemers bool) error {
|
||||||
|
|
||||||
|
if removeRedeemers {
|
||||||
|
return op.removeRedeemersOf(removedTransaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
outpoint := externalapi.DomainOutpoint{TransactionID: *removedTransaction.TransactionID()}
|
||||||
|
for i := range removedTransaction.Transaction().Outputs {
|
||||||
|
outpoint.Index = uint32(i)
|
||||||
|
for _, orphan := range op.orphansByPreviousOutpoint[outpoint] {
|
||||||
|
for _, input := range orphan.Transaction().Inputs {
|
||||||
|
if input.PreviousOutpoint.TransactionID.Equal(removedTransaction.TransactionID()) {
|
||||||
|
input.UTXOEntry = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *orphansPool) randomOrphan() *model.OrphanTransaction {
|
||||||
|
for _, orphan := range op.allOrphans {
|
||||||
|
return orphan
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
151
domain/miningmanager/mempool/transactions_pool.go
Normal file
151
domain/miningmanager/mempool/transactions_pool.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
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.IDToTransaction
|
||||||
|
highPriorityTransactions model.IDToTransaction
|
||||||
|
chainedTransactionsByPreviousOutpoint model.OutpointToTransaction
|
||||||
|
transactionsOrderedByFeeRate model.TransactionsOrderedByFeeRate
|
||||||
|
lastExpireScan uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTransactionsPool(mp *mempool) *transactionsPool {
|
||||||
|
return &transactionsPool{
|
||||||
|
mempool: mp,
|
||||||
|
allTransactions: model.IDToTransaction{},
|
||||||
|
highPriorityTransactions: model.IDToTransaction{},
|
||||||
|
chainedTransactionsByPreviousOutpoint: model.OutpointToTransaction{},
|
||||||
|
transactionsOrderedByFeeRate: model.TransactionsOrderedByFeeRate{},
|
||||||
|
lastExpireScan: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp *transactionsPool) addTransaction(transaction *externalapi.DomainTransaction,
|
||||||
|
parentTransactionsInPool model.OutpointToTransaction, isHighPriority bool) error {
|
||||||
|
|
||||||
|
virtualDAAScore, err := tp.mempool.virtualDAAScore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mempoolTransaction := model.NewMempoolTransaction(
|
||||||
|
transaction, parentTransactionsInPool, isHighPriority, virtualDAAScore)
|
||||||
|
|
||||||
|
return tp.addMempoolTransaction(mempoolTransaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.virtualDAAScore()
|
||||||
|
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.OutpointToTransaction {
|
||||||
|
|
||||||
|
parentsTransactionsInPool := model.OutpointToTransaction{}
|
||||||
|
|
||||||
|
for _, input := range transaction.Inputs {
|
||||||
|
transaction := tp.allTransactions[input.PreviousOutpoint.TransactionID]
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -59,10 +59,10 @@ be an exhaustive list.
|
|||||||
Errors
|
Errors
|
||||||
|
|
||||||
Errors returned by this package are either the raw errors provided by underlying
|
Errors returned by this package are either the raw errors provided by underlying
|
||||||
calls or of type mempool.RuleError. Since there are two classes of rules
|
calls or of type mempool_old.RuleError. Since there are two classes of rules
|
||||||
(mempool acceptance rules and blockDAG (consensus) acceptance rules), the
|
(mempool acceptance rules and blockDAG (consensus) acceptance rules), the
|
||||||
mempool.RuleError type contains a single Err field which will, in turn, either
|
mempool_old.RuleError type contains a single Err field which will, in turn, either
|
||||||
be a mempool.TxRuleError or a ruleerrors.RuleError. The first indicates a
|
be a mempool_old.TxRuleError or a ruleerrors.RuleError. The first indicates a
|
||||||
violation of mempool acceptance rules while the latter indicates a violation of
|
violation of mempool acceptance rules while the latter indicates a violation of
|
||||||
consensus acceptance rules. This allows the caller to easily differentiate
|
consensus acceptance rules. This allows the caller to easily differentiate
|
||||||
between unexpected errors, such as database errors, versus errors due to rule
|
between unexpected errors, such as database errors, versus errors due to rule
|
||||||
@@ -70,4 +70,4 @@ violations through type assertions. In addition, callers can programmatically
|
|||||||
determine the specific rule violation by type asserting the Err field to one of
|
determine the specific rule violation by type asserting the Err field to one of
|
||||||
the aforementioned types and examining their underlying ErrorCode field.
|
the aforementioned types and examining their underlying ErrorCode field.
|
||||||
*/
|
*/
|
||||||
package mempool
|
package mempool_old
|
||||||
124
domain/miningmanager/mempool_old/error.go
Normal file
124
domain/miningmanager/mempool_old/error.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright (c) 2014-2016 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package mempool_old
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RuleError identifies a rule violation. It is used to indicate that
|
||||||
|
// processing of a transaction failed due to one of the many validation
|
||||||
|
// rules. The caller can use type assertions to determine if a failure was
|
||||||
|
// specifically due to a rule violation and use the Err field to access the
|
||||||
|
// underlying error, which will be either a TxRuleError or a
|
||||||
|
// ruleerrors.RuleError.
|
||||||
|
type RuleError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the error interface and prints human-readable errors.
|
||||||
|
func (e RuleError) Error() string {
|
||||||
|
if e.Err == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap unwraps the wrapped error
|
||||||
|
func (e RuleError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RejectCode represents a numeric value by which a remote peer indicates
|
||||||
|
// why a message was rejected.
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the RejectCode in human-readable form.
|
||||||
|
func (code RejectCode) String() string {
|
||||||
|
if s, ok := rejectCodeStrings[code]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Unknown RejectCode (%d)", uint8(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxRuleError identifies a rule violation. It is used to indicate that
|
||||||
|
// processing of a transaction failed due to one of the many validation
|
||||||
|
// rules. The caller can use type assertions to determine if a failure was
|
||||||
|
// specifically due to a rule violation and access the ErrorCode field to
|
||||||
|
// ascertain the specific reason for the rule violation.
|
||||||
|
type TxRuleError struct {
|
||||||
|
RejectCode RejectCode // The code to send with reject messages
|
||||||
|
Description string // Human readable description of the issue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the error interface and prints human-readable errors.
|
||||||
|
func (e TxRuleError) Error() string {
|
||||||
|
return e.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
// txRuleError creates an underlying TxRuleError with the given a set of
|
||||||
|
// arguments and returns a RuleError that encapsulates it.
|
||||||
|
func txRuleError(c RejectCode, desc string) RuleError {
|
||||||
|
return RuleError{
|
||||||
|
Err: TxRuleError{RejectCode: c, Description: desc},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRuleError(err error) RuleError {
|
||||||
|
return RuleError{
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractRejectCode attempts to return a relevant reject code for a given error
|
||||||
|
// by examining the error for known types. It will return true if a code
|
||||||
|
// was successfully extracted.
|
||||||
|
func extractRejectCode(err error) (RejectCode, bool) {
|
||||||
|
// Pull the underlying error out of a RuleError.
|
||||||
|
var ruleErr RuleError
|
||||||
|
if ok := errors.As(err, &ruleErr); ok {
|
||||||
|
err = ruleErr.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var trErr TxRuleError
|
||||||
|
if errors.As(err, &trErr) {
|
||||||
|
return trErr.RejectCode, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return RejectInvalid, false
|
||||||
|
}
|
||||||
11
domain/miningmanager/mempool_old/log.go
Normal file
11
domain/miningmanager/mempool_old/log.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) 2013-2016 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package mempool_old
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logger.RegisterSubSystem("TXMP")
|
||||||
1045
domain/miningmanager/mempool_old/mempool.go
Normal file
1045
domain/miningmanager/mempool_old/mempool.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
package mempool
|
package mempool_old
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
@@ -2,10 +2,11 @@
|
|||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package mempool
|
package mempool_old
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||||
|
|
||||||
consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package mempool
|
package mempool_old
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package miningmanager_test
|
package miningmanager_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/miningmanager/mempool"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus"
|
"github.com/kaspanet/kaspad/domain/consensus"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||||
@@ -84,8 +85,8 @@ func TestImmatureSpend(t *testing.T) {
|
|||||||
miningManager := miningFactory.NewMiningManager(tc, &consensusConfig.Params)
|
miningManager := miningFactory.NewMiningManager(tc, &consensusConfig.Params)
|
||||||
tx := createTransactionWithUTXOEntry(t, 0)
|
tx := createTransactionWithUTXOEntry(t, 0)
|
||||||
err = miningManager.ValidateAndInsertTransaction(tx, false)
|
err = miningManager.ValidateAndInsertTransaction(tx, false)
|
||||||
txRuleError := &mempool.TxRuleError{}
|
txRuleError := &mempool_old.TxRuleError{}
|
||||||
if !errors.As(err, txRuleError) || txRuleError.RejectCode != mempool.RejectImmatureSpend {
|
if !errors.As(err, txRuleError) || txRuleError.RejectCode != mempool_old.RejectImmatureSpend {
|
||||||
t.Fatalf("Unexpected error %+v", err)
|
t.Fatalf("Unexpected error %+v", err)
|
||||||
}
|
}
|
||||||
transactionsFromMempool := miningManager.AllTransactions()
|
transactionsFromMempool := miningManager.AllTransactions()
|
||||||
|
|||||||
Reference in New Issue
Block a user