Compare commits

..

7 Commits

Author SHA1 Message Date
tal
1175a8afe7 Merge remote-tracking branch 'origin/v0.11.0-dev' into testsCheckSequenceVerify 2021-06-08 17:58:21 +03:00
tal
7e6eaf9299 Delete an unnecessary comment. 2021-06-08 17:57:56 +03:00
tal
b9339e8815 Changed names and comments. 2021-06-08 14:02:51 +03:00
tal
8aadb0bd44 Save 2021-06-06 14:37:55 +03:00
tal
092177d9a5 Save 2021-06-06 11:55:07 +03:00
tal
55ee5f3115 save 2021-05-31 10:22:00 +03:00
tal
3084e3a366 Save 2021-05-30 17:43:35 +03:00
26 changed files with 1458 additions and 2001 deletions

View File

@@ -7,7 +7,7 @@ import (
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
"github.com/kaspanet/kaspad/domain/miningmanager/mempool"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
@@ -175,15 +175,15 @@ func (flow *handleRelayedTransactionsFlow) receiveTransactions(requestedTransact
err = flow.Domain().MiningManager().ValidateAndInsertTransaction(tx, true)
if err != nil {
ruleErr := &mempool_old.RuleError{}
ruleErr := &mempool.RuleError{}
if !errors.As(err, ruleErr) {
return errors.Wrapf(err, "failed to process transaction %s", txID)
}
shouldBan := false
if txRuleErr := (&mempool_old.TxRuleError{}); errors.As(ruleErr.Err, txRuleErr) {
if txRuleErr.RejectCode == mempool_old.RejectInvalid {
shouldBan = true
shouldBan := true
if txRuleErr := (&mempool.TxRuleError{}); errors.As(ruleErr.Err, txRuleErr) {
if txRuleErr.RejectCode != mempool.RejectInvalid {
shouldBan = false
}
}

View File

@@ -4,7 +4,7 @@ import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
"github.com/kaspanet/kaspad/domain/miningmanager/mempool"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
)
@@ -23,7 +23,7 @@ func HandleSubmitTransaction(context *rpccontext.Context, _ *router.Router, requ
transactionID := consensushashing.TransactionID(domainTransaction)
err = context.ProtocolManager.AddTransaction(domainTransaction)
if err != nil {
if !errors.As(err, &mempool_old.RuleError{}) {
if !errors.As(err, &mempool.RuleError{}) {
return nil, err
}

View File

@@ -0,0 +1,424 @@
package consensus_test
import (
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
"github.com/pkg/errors"
"testing"
)
// TestCheckSequenceVerifyConditionedByBlockHeight verifies that locked output (by CSV script) is spendable
// only after a certain number of blocks have been added relative to the time the UTXO was mined.
// CSV - check sequence verify.
func TestCheckSequenceVerifyConditionedByBlockHeight(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
consensusConfig.BlockCoinbaseMaturity = 0
factory := consensus.NewFactory()
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckSequenceVerifyConditionedByBlockHeight")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
blockAHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{testConsensus.DAGParams().GenesisHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockA: %v", err)
}
blockBHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockAHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockB: %v", err)
}
blockCHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockBHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockC: %v", err)
}
blockC, err := testConsensus.GetBlock(blockCHash)
if err != nil {
t.Fatalf("Failed getting blockC: %v", err)
}
fees := uint64(1)
fundingTransaction, err := testutils.CreateTransaction(blockC.Transactions[transactionhelper.CoinbaseTransactionIndex], fees)
if err != nil {
t.Fatalf("Error creating foundingTransaction: %v", err)
}
blockDHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockCHash}, nil,
[]*externalapi.DomainTransaction{fundingTransaction})
if err != nil {
t.Fatalf("Failed creating blockD: %v", err)
}
//create a CSV script
numOfBlocksToWait := int64(10)
if numOfBlocksToWait > 0xffff {
t.Fatalf("More than the maximum number of blocks allowed.")
}
redeemScriptCSV, err := createCheckSequenceVerifyScript(numOfBlocksToWait)
if err != nil {
t.Fatalf("Failed to create a script using createCheckSequenceVerifyScript: %v", err)
}
p2shScriptCSV, err := txscript.PayToScriptHashScript(redeemScriptCSV)
if err != nil {
t.Fatalf("Failed to create a pay-to-script-hash script : %v", err)
}
scriptPublicKeyCSV := externalapi.ScriptPublicKey{
Version: constants.MaxScriptPublicKeyVersion,
Script: p2shScriptCSV,
}
transactionWithLockedOutput, err := createTransactionWithLockedOutput(fundingTransaction, fees, &scriptPublicKeyCSV)
if err != nil {
t.Fatalf("Error in createTransactionWithLockedOutput: %v", err)
}
// BlockE contains the locked output (locked by CSV).
// This block should be valid since CSV script locked only the output.
blockEHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockDHash}, nil,
[]*externalapi.DomainTransaction{transactionWithLockedOutput})
if err != nil {
t.Fatalf("Error creating blockE: %v", err)
}
// The 23-bit of sequence defines if it's conditioned by block height(set to 0) or by time (set to 1).
sequenceFlag := 0
// Create a transaction that tries to spend the locked output.
transactionThatSpentTheLockedOutput, err := createTransactionThatSpentTheLockedOutput(transactionWithLockedOutput,
fees, redeemScriptCSV, uint64(numOfBlocksToWait), sequenceFlag, blockEHash, &testConsensus)
if err != nil {
t.Fatalf("Error creating transactionThatSpentTheLockedOutput: %v", err)
}
// Add a block that contains a transaction that spends the locked output before the time, and therefore should be failed.
_, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{blockEHash}, nil,
[]*externalapi.DomainTransaction{transactionThatSpentTheLockedOutput})
if err == nil || !errors.Is(err, ruleerrors.ErrUnfinalizedTx) {
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrUnfinalizedTx, err)
}
//Add x blocks to release the locked output, where x = 'numOfBlocksToWait'.
tipHash := blockEHash
for i := int64(0); i < numOfBlocksToWait; i++ {
tipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating tip: %v", err)
}
}
// Tries to spend the output that should be no longer locked.
_, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil,
[]*externalapi.DomainTransaction{transactionThatSpentTheLockedOutput})
if err != nil {
t.Fatalf("The block should be valid since the output is not locked anymore. but got an error: %v", err)
}
})
}
// TestCheckSequenceVerifyConditionedByRelativeTime verifies that locked output (by CSV script) is spendable only after
// the time is reached to the set target relative to the time the UTXO was mined (compared to the past median time).
func TestCheckSequenceVerifyConditionedByRelativeTime(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
consensusConfig.BlockCoinbaseMaturity = 0
factory := consensus.NewFactory()
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckSequenceVerifyConditionedByRelativeTime")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
blockAHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{testConsensus.DAGParams().GenesisHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockA: %v", err)
}
blockBHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockAHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockB: %v", err)
}
blockCHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockBHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockC: %v", err)
}
blockC, err := testConsensus.GetBlock(blockCHash)
if err != nil {
t.Fatalf("Failed getting blockC: %v", err)
}
fees := uint64(1)
fundingTransaction, err := testutils.CreateTransaction(blockC.Transactions[transactionhelper.CoinbaseTransactionIndex], fees)
if err != nil {
t.Fatalf("Error creating foundingTransaction: %v", err)
}
blockDHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockCHash}, nil,
[]*externalapi.DomainTransaction{fundingTransaction})
if err != nil {
t.Fatalf("Failed creating blockD: %v", err)
}
//create a CSV script
timeToWait := int64(14 * 1000)
if timeToWait > 0xffff {
t.Fatalf("More than the allowed time to set.")
}
redeemScriptCSV, err := createCheckSequenceVerifyScript(timeToWait)
if err != nil {
t.Fatalf("Failed to create a script using createCheckSequenceVerifyScript: %v", err)
}
p2shScriptCSV, err := txscript.PayToScriptHashScript(redeemScriptCSV)
if err != nil {
t.Fatalf("Failed to create a pay-to-script-hash script : %v", err)
}
scriptPublicKeyCSV := externalapi.ScriptPublicKey{
Version: constants.MaxScriptPublicKeyVersion,
Script: p2shScriptCSV,
}
transactionWithLockedOutput, err := createTransactionWithLockedOutput(fundingTransaction, fees, &scriptPublicKeyCSV)
if err != nil {
t.Fatalf("Error in createTransactionWithLockedOutput: %v", err)
}
// BlockE contains the locked output (locked by CSV).
// This block should be valid since CSV script locked only the output.
blockEHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockDHash}, nil,
[]*externalapi.DomainTransaction{transactionWithLockedOutput})
if err != nil {
t.Fatalf("Error creating blockE: %v", err)
}
// The 23-bit of sequence defines if it's conditioned by block height(set to 0) or by time (set to 1).
sequenceFlag := 1
// Create a transaction that tries to spend the locked output.
transactionThatSpentTheLockedOutput, err := createTransactionThatSpentTheLockedOutput(transactionWithLockedOutput,
fees, redeemScriptCSV, uint64(timeToWait), sequenceFlag, blockEHash, &testConsensus)
if err != nil {
t.Fatalf("Error creating transactionThatSpentTheLockedOutput: %v", err)
}
// Add a block that contains a transaction that spends the locked output before the time, and therefore should be failed.
_, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{blockEHash}, nil,
[]*externalapi.DomainTransaction{transactionThatSpentTheLockedOutput})
if err == nil || !errors.Is(err, ruleerrors.ErrUnfinalizedTx) {
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrUnfinalizedTx, err)
}
emptyCoinbase := externalapi.DomainCoinbaseData{
ScriptPublicKey: &externalapi.ScriptPublicKey{
Script: nil,
Version: 0,
},
}
var tipHash *externalapi.DomainHash
blockE, err := testConsensus.GetBlock(blockEHash)
if err != nil {
t.Fatalf("Failed to get blockE: %v", err)
}
timeStampBlockE := blockE.Header.TimeInMilliseconds()
stagingArea := model.NewStagingArea()
// Make sure the time limitation has passed.
lockTimeTarget := blockE.Header.TimeInMilliseconds() + timeToWait
for i := int64(0); ; i++ {
tipBlock, err := testConsensus.BuildBlock(&emptyCoinbase, nil)
if err != nil {
t.Fatalf("Error creating tip using BuildBlock: %v", err)
}
blockHeader := tipBlock.Header.ToMutable()
blockHeader.SetTimeInMilliseconds(timeStampBlockE + i*1000)
tipBlock.Header = blockHeader.ToImmutable()
_, err = testConsensus.ValidateAndInsertBlock(tipBlock)
if err != nil {
t.Fatalf("Error validating and inserting tip block: %v", err)
}
tipHash = consensushashing.BlockHash(tipBlock)
pastMedianTime, err := testConsensus.PastMedianTimeManager().PastMedianTime(stagingArea, tipHash)
if err != nil {
t.Fatalf("Failed getting pastMedianTime: %v", err)
}
if pastMedianTime > lockTimeTarget {
break
}
}
// Tries to spend the output that should be no longer locked
_, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil,
[]*externalapi.DomainTransaction{transactionThatSpentTheLockedOutput})
if err != nil {
t.Fatalf("The block should be valid since the output is not locked anymore. but got an error: %v", err)
}
})
}
//TestRelativeTimeOnCheckSequenceVerify verifies that if the relative target is set to X blocks to wait, and the absolute height
// will be X before adding all the blocks, then the output will remain locked.
func TestRelativeTimeOnCheckSequenceVerify(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
consensusConfig.BlockCoinbaseMaturity = 0
factory := consensus.NewFactory()
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestRelativeTimeOnCheckSequenceVerify")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
currentNumOfBlocks := int64(0)
blockAHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{testConsensus.DAGParams().GenesisHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockA: %v", err)
}
currentNumOfBlocks++
blockBHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockAHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockB: %v", err)
}
currentNumOfBlocks++
blockCHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockBHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating blockC: %v", err)
}
currentNumOfBlocks++
blockC, err := testConsensus.GetBlock(blockCHash)
if err != nil {
t.Fatalf("Failed getting blockC: %v", err)
}
fees := uint64(1)
fundingTransaction, err := testutils.CreateTransaction(blockC.Transactions[transactionhelper.CoinbaseTransactionIndex], fees)
if err != nil {
t.Fatalf("Error creating foundingTransaction: %v", err)
}
blockDHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockCHash}, nil,
[]*externalapi.DomainTransaction{fundingTransaction})
if err != nil {
t.Fatalf("Failed creating blockD: %v", err)
}
currentNumOfBlocks++
//create a CSV script
numOfBlocksToWait := int64(10)
if numOfBlocksToWait > 0xffff {
t.Fatalf("More than the max number of blocks that allowed to set.")
}
redeemScriptCSV, err := createCheckSequenceVerifyScript(numOfBlocksToWait)
if err != nil {
t.Fatalf("Failed to create a script using createCheckSequenceVerifyScript: %v", err)
}
p2shScriptCSV, err := txscript.PayToScriptHashScript(redeemScriptCSV)
if err != nil {
t.Fatalf("Failed to create a pay-to-script-hash script : %v", err)
}
scriptPublicKeyCSV := externalapi.ScriptPublicKey{
Version: constants.MaxScriptPublicKeyVersion,
Script: p2shScriptCSV,
}
transactionWithLockedOutput, err := createTransactionWithLockedOutput(fundingTransaction, fees, &scriptPublicKeyCSV)
if err != nil {
t.Fatalf("Error in createTransactionWithLockedOutput: %v", err)
}
// BlockE contains the locked output (locked by CSV).
// This block should be valid since CSV script locked only the output.
blockEHash, _, err := testConsensus.AddBlock([]*externalapi.DomainHash{blockDHash}, nil,
[]*externalapi.DomainTransaction{transactionWithLockedOutput})
if err != nil {
t.Fatalf("Error creating blockE: %v", err)
}
currentNumOfBlocks++
// The 23-bit of sequence defines if it's conditioned by block height(set to 0) or by time (set to 1).
sequenceFlag := 0
// Create a transaction that tries to spend the locked output.
transactionThatSpentTheLockedOutput, err := createTransactionThatSpentTheLockedOutput(transactionWithLockedOutput,
fees, redeemScriptCSV, uint64(numOfBlocksToWait), sequenceFlag, blockEHash, &testConsensus)
if err != nil {
t.Fatalf("Error creating transactionThatSpentTheLockedOutput: %v", err)
}
// Mines blocks so the block height will be the same as the relative number(but not enough to reach the relative target)
// and verify that the output is still locked.
// For unlocked the output the blocks should be count from the block that contains the locked output and not as an absolute height.
tipHash := blockEHash
for currentNumOfBlocks == numOfBlocksToWait {
tipHash, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
if err != nil {
t.Fatalf("Error creating tip: %v", err)
}
currentNumOfBlocks++
}
_, _, err = testConsensus.AddBlock([]*externalapi.DomainHash{tipHash}, nil,
[]*externalapi.DomainTransaction{transactionThatSpentTheLockedOutput})
if err == nil || !errors.Is(err, ruleerrors.ErrUnfinalizedTx) {
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrUnfinalizedTx, err)
}
})
}
func createCheckSequenceVerifyScript(numOfBlocks int64) ([]byte, error) {
scriptBuilder := txscript.NewScriptBuilder()
scriptBuilder.AddOp(txscript.OpCheckSequenceVerify)
scriptBuilder.AddInt64(numOfBlocks)
scriptBuilder.AddOp(txscript.OpTrue)
return scriptBuilder.Script()
}
func createTransactionWithLockedOutput(txToSpend *externalapi.DomainTransaction, fee uint64,
scriptPublicKeyCSV *externalapi.ScriptPublicKey) (*externalapi.DomainTransaction, error) {
_, redeemScript := testutils.OpTrueScript()
signatureScript, err := txscript.PayToScriptHashSignatureScript(redeemScript, nil)
if err != nil {
return nil, err
}
input := &externalapi.DomainTransactionInput{
PreviousOutpoint: externalapi.DomainOutpoint{
TransactionID: *consensushashing.TransactionID(txToSpend),
Index: 0,
},
SignatureScript: signatureScript,
Sequence: constants.MaxTxInSequenceNum,
}
output := &externalapi.DomainTransactionOutput{
ScriptPublicKey: scriptPublicKeyCSV,
Value: txToSpend.Outputs[0].Value - fee,
}
return &externalapi.DomainTransaction{
Version: constants.MaxTransactionVersion,
Inputs: []*externalapi.DomainTransactionInput{input},
Outputs: []*externalapi.DomainTransactionOutput{output},
Payload: []byte{},
}, nil
}
func createTransactionThatSpentTheLockedOutput(txToSpend *externalapi.DomainTransaction, fee uint64,
redeemScript []byte, lockTime uint64, sequenceFlag23Bit int, lockedOutputBlockHash *externalapi.DomainHash,
testConsensus *testapi.TestConsensus) (*externalapi.DomainTransaction, error) {
// the 31bit is off since its relative timelock.
sequence := uint64(0)
sequence |= lockTime
// conditioned by absolute time:
if sequenceFlag23Bit == 1 {
sequence |= 1 << 23
lockedOutputBlock, err := (*testConsensus).GetBlock(lockedOutputBlockHash)
if err != nil {
return nil, err
}
lockTime += uint64(lockedOutputBlock.Header.TimeInMilliseconds())
} else {
// conditioned by block height:
blockDAAScore, err := (*testConsensus).DAABlocksStore().DAAScore((*testConsensus).DatabaseContext(),
model.NewStagingArea(), lockedOutputBlockHash)
if err != nil {
return nil, err
}
lockTime += blockDAAScore
}
signatureScript, err := txscript.PayToScriptHashSignatureScript(redeemScript, []byte{})
if err != nil {
return nil, err
}
scriptPublicKeyOutput, _ := testutils.OpTrueScript()
input := &externalapi.DomainTransactionInput{
PreviousOutpoint: externalapi.DomainOutpoint{
TransactionID: *consensushashing.TransactionID(txToSpend),
Index: 0,
},
SignatureScript: signatureScript,
Sequence: sequence,
}
output := &externalapi.DomainTransactionOutput{
ScriptPublicKey: scriptPublicKeyOutput,
Value: txToSpend.Outputs[0].Value - fee,
}
return &externalapi.DomainTransaction{
Version: constants.MaxTransactionVersion,
Inputs: []*externalapi.DomainTransactionInput{input},
Outputs: []*externalapi.DomainTransactionOutput{output},
Payload: []byte{},
LockTime: lockTime,
}, nil
}

View File

@@ -4,7 +4,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/domain/miningmanager/blocktemplatebuilder"
mempoolpkg "github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
mempoolpkg "github.com/kaspanet/kaspad/domain/miningmanager/mempool"
)
// Factory instantiates new mining managers

View File

@@ -1,39 +0,0 @@
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,
}
}

View File

@@ -59,10 +59,10 @@ be an exhaustive list.
Errors
Errors returned by this package are either the raw errors provided by underlying
calls or of type mempool_old.RuleError. Since there are two classes of rules
calls or of type mempool.RuleError. Since there are two classes of rules
(mempool acceptance rules and blockDAG (consensus) acceptance rules), the
mempool_old.RuleError type contains a single Err field which will, in turn, either
be a mempool_old.TxRuleError or a ruleerrors.RuleError. The first indicates a
mempool.RuleError type contains a single Err field which will, in turn, either
be a mempool.TxRuleError or a ruleerrors.RuleError. The first indicates a
violation of mempool acceptance rules while the latter indicates a violation of
consensus acceptance rules. This allows the caller to easily differentiate
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
the aforementioned types and examining their underlying ErrorCode field.
*/
package mempool_old
package mempool

View File

@@ -39,34 +39,32 @@ 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
RejectIncompatibleOrphan RejectCode = 0x64
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_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",
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.

View File

@@ -1,9 +0,0 @@
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

View File

@@ -1,71 +0,0 @@
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
}

View File

@@ -1,4 +1,4 @@
package mempool_old
package mempool
import (
"math"

View File

@@ -1,6 +0,0 @@
package model
import "math"
// UnacceptedDAAScore is used to for UTXOEntries that were created by transactions in the mempool.
const UnacceptedDAAScore = math.MaxUint64

View File

@@ -1,14 +0,0 @@
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

View File

@@ -1,49 +0,0 @@
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
}

View File

@@ -1,75 +0,0 @@
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
}

View File

@@ -1,41 +0,0 @@
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
}

View File

@@ -1,8 +0,0 @@
package model
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type Transaction interface {
TransactionID() *externalapi.DomainTransactionID
Transaction() *externalapi.DomainTransaction
}

View File

@@ -1,260 +0,0 @@
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
}

View File

@@ -2,11 +2,10 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package mempool_old
package mempool
import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package mempool_old
package mempool
import (
"bytes"

View File

@@ -1,151 +0,0 @@
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
}

View File

@@ -1,124 +0,0 @@
// 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
}

View File

@@ -1,11 +0,0 @@
// 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")

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,10 @@
package miningmanager_test
import (
"github.com/kaspanet/kaspad/domain/miningmanager/mempool"
"strings"
"testing"
"github.com/kaspanet/kaspad/domain/miningmanager/mempool_old"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
@@ -85,8 +84,8 @@ func TestImmatureSpend(t *testing.T) {
miningManager := miningFactory.NewMiningManager(tc, &consensusConfig.Params)
tx := createTransactionWithUTXOEntry(t, 0)
err = miningManager.ValidateAndInsertTransaction(tx, false)
txRuleError := &mempool_old.TxRuleError{}
if !errors.As(err, txRuleError) || txRuleError.RejectCode != mempool_old.RejectImmatureSpend {
txRuleError := &mempool.TxRuleError{}
if !errors.As(err, txRuleError) || txRuleError.RejectCode != mempool.RejectImmatureSpend {
t.Fatalf("Unexpected error %+v", err)
}
transactionsFromMempool := miningManager.AllTransactions()