From 11ca88933e9ff04d871c5e74c9d1629221c851ca Mon Sep 17 00:00:00 2001 From: Ori Newman Date: Fri, 1 Dec 2023 09:20:22 +0200 Subject: [PATCH 1/2] Apply new dust prevention KIP to miner's selection policy --- domain/dagconfig/consensus_defaults.go | 2 ++ domain/dagconfig/params.go | 6 +++++ .../blocktemplatebuilder.go | 23 +++++++++++++++++-- .../blocktemplatebuilder/policy.go | 2 ++ .../blocktemplatebuilder/txselection.go | 10 ++++---- domain/miningmanager/factory.go | 2 +- version/version.go | 2 +- 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/domain/dagconfig/consensus_defaults.go b/domain/dagconfig/consensus_defaults.go index 8bc914431f..d38265a7f3 100644 --- a/domain/dagconfig/consensus_defaults.go +++ b/domain/dagconfig/consensus_defaults.go @@ -84,4 +84,6 @@ const ( defaultDeflationaryPhaseDaaScore = 15778800 - 259200 defaultMergeDepth = 3600 + + defaultDustConst = 1e12 // TODO: Determine the right value ) diff --git a/domain/dagconfig/params.go b/domain/dagconfig/params.go index 0340763ef3..5dc6ac5e21 100644 --- a/domain/dagconfig/params.go +++ b/domain/dagconfig/params.go @@ -188,6 +188,8 @@ type Params struct { MaxBlockLevel int MergeDepth uint64 + + DustConst uint64 } // NormalizeRPCServerAddress returns addr with the current network default @@ -288,6 +290,7 @@ var MainnetParams = Params{ // This means that any block that has a level lower or equal to genesis will be level 0. MaxBlockLevel: 225, MergeDepth: defaultMergeDepth, + DustConst: defaultDustConst, } // TestnetParams defines the network parameters for the test Kaspa network. @@ -354,6 +357,7 @@ var TestnetParams = Params{ MaxBlockLevel: 250, MergeDepth: defaultMergeDepth, + DustConst: defaultDustConst, } // SimnetParams defines the network parameters for the simulation test Kaspa @@ -420,6 +424,7 @@ var SimnetParams = Params{ MaxBlockLevel: 250, MergeDepth: defaultMergeDepth, + DustConst: defaultDustConst, } // DevnetParams defines the network parameters for the development Kaspa network. @@ -482,6 +487,7 @@ var DevnetParams = Params{ MaxBlockLevel: 250, MergeDepth: defaultMergeDepth, + DustConst: defaultDustConst, } // ErrDuplicateNet describes an error where the parameters for a Kaspa diff --git a/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go b/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go index 564542db8a..e21afe1275 100644 --- a/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go +++ b/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go @@ -22,6 +22,7 @@ type candidateTx struct { *consensusexternalapi.DomainTransaction txValue float64 gasLimit uint64 + mass uint64 p float64 start float64 @@ -41,11 +42,11 @@ type blockTemplateBuilder struct { // New creates a new blockTemplateBuilder func New(consensusReference consensusreference.ConsensusReference, mempool miningmanagerapi.Mempool, - blockMaxMass uint64, coinbasePayloadScriptPublicKeyMaxLength uint8) miningmanagerapi.BlockTemplateBuilder { + blockMaxMass uint64, coinbasePayloadScriptPublicKeyMaxLength uint8, dustConst uint64) miningmanagerapi.BlockTemplateBuilder { return &blockTemplateBuilder{ consensusReference: consensusReference, mempool: mempool, - policy: policy{BlockMaxMass: blockMaxMass}, + policy: policy{BlockMaxMass: blockMaxMass, DustConst: dustConst}, coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength, } @@ -130,6 +131,7 @@ func (btb *blockTemplateBuilder) BuildBlockTemplate( DomainTransaction: tx, txValue: btb.calcTxValue(tx), gasLimit: gasLimit, + mass: btb.calcMass(tx), }) } @@ -172,6 +174,23 @@ func (btb *blockTemplateBuilder) BuildBlockTemplate( return blockTemplate, nil } +func (btb *blockTemplateBuilder) calcMass(tx *consensusexternalapi.DomainTransaction) uint64 { + // TODO: Insert link for relevant KIP + addedMass := uint64(0) + sumOutsValue := uint64(0) + for _, output := range tx.Outputs { + addedMass += btb.policy.DustConst / output.Value + sumOutsValue += output.Value + } + + reducedMass := uint64(len(tx.Inputs)*len(tx.Inputs)) * btb.policy.DustConst / sumOutsValue + if addedMass < reducedMass { + return tx.Mass + } + + return tx.Mass + addedMass - reducedMass +} + // ModifyBlockTemplate modifies an existing block template to the requested coinbase data and updates the timestamp func (btb *blockTemplateBuilder) ModifyBlockTemplate(newCoinbaseData *consensusexternalapi.DomainCoinbaseData, blockTemplateToModify *consensusexternalapi.DomainBlockTemplate) (*consensusexternalapi.DomainBlockTemplate, error) { diff --git a/domain/miningmanager/blocktemplatebuilder/policy.go b/domain/miningmanager/blocktemplatebuilder/policy.go index 58980f8ed0..8413e36dfb 100644 --- a/domain/miningmanager/blocktemplatebuilder/policy.go +++ b/domain/miningmanager/blocktemplatebuilder/policy.go @@ -11,4 +11,6 @@ type policy struct { // BlockMaxMass is the maximum block mass to be used when generating a // block template. BlockMaxMass uint64 + + DustConst uint64 } diff --git a/domain/miningmanager/blocktemplatebuilder/txselection.go b/domain/miningmanager/blocktemplatebuilder/txselection.go index 6ebfec6897..93079fd076 100644 --- a/domain/miningmanager/blocktemplatebuilder/txselection.go +++ b/domain/miningmanager/blocktemplatebuilder/txselection.go @@ -101,8 +101,8 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx) // Enforce maximum transaction mass per block. Also check // for overflow. - if txsForBlockTemplate.totalMass+selectedTx.Mass < txsForBlockTemplate.totalMass || - txsForBlockTemplate.totalMass+selectedTx.Mass > btb.policy.BlockMaxMass { + if txsForBlockTemplate.totalMass+selectedTx.mass < txsForBlockTemplate.totalMass || + txsForBlockTemplate.totalMass+selectedTx.mass > btb.policy.BlockMaxMass { log.Tracef("Tx %s would exceed the max block mass. "+ "As such, stopping.", consensushashing.TransactionID(tx)) break @@ -143,11 +143,11 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx) // save the masses, fees, and signature operation counts to the // result. selectedTxs = append(selectedTxs, selectedTx) - txsForBlockTemplate.totalMass += selectedTx.Mass + txsForBlockTemplate.totalMass += selectedTx.mass txsForBlockTemplate.totalFees += selectedTx.Fee log.Tracef("Adding tx %s (feePerMegaGram %d)", - consensushashing.TransactionID(tx), selectedTx.Fee*1e6/selectedTx.Mass) + consensushashing.TransactionID(tx), selectedTx.Fee*1e6/selectedTx.mass) markCandidateTxForDeletion(selectedTx) } @@ -157,7 +157,7 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx) }) for _, selectedTx := range selectedTxs { txsForBlockTemplate.selectedTxs = append(txsForBlockTemplate.selectedTxs, selectedTx.DomainTransaction) - txsForBlockTemplate.txMasses = append(txsForBlockTemplate.txMasses, selectedTx.Mass) + txsForBlockTemplate.txMasses = append(txsForBlockTemplate.txMasses, selectedTx.mass) txsForBlockTemplate.txFees = append(txsForBlockTemplate.txFees, selectedTx.Fee) } return txsForBlockTemplate diff --git a/domain/miningmanager/factory.go b/domain/miningmanager/factory.go index 16b50fb29a..d1bdd6920d 100644 --- a/domain/miningmanager/factory.go +++ b/domain/miningmanager/factory.go @@ -21,7 +21,7 @@ func (f *factory) NewMiningManager(consensusReference consensusreference.Consens mempoolConfig *mempoolpkg.Config) MiningManager { mempool := mempoolpkg.New(mempoolConfig, consensusReference) - blockTemplateBuilder := blocktemplatebuilder.New(consensusReference, mempool, params.MaxBlockMass, params.CoinbasePayloadScriptPublicKeyMaxLength) + blockTemplateBuilder := blocktemplatebuilder.New(consensusReference, mempool, params.MaxBlockMass, params.CoinbasePayloadScriptPublicKeyMaxLength, params.DustConst) return &miningManager{ consensusReference: consensusReference, diff --git a/version/version.go b/version/version.go index c04cd9643b..8224f7faf6 100644 --- a/version/version.go +++ b/version/version.go @@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs const ( appMajor uint = 0 appMinor uint = 12 - appPatch uint = 13 + appPatch uint = 15 ) // appBuild is defined as a variable so it can be overridden during the build From 42da3cd0a0d3218beade91864ebcf5fa951de283 Mon Sep 17 00:00:00 2001 From: Ori Newman Date: Fri, 1 Dec 2023 10:09:38 +0200 Subject: [PATCH 2/2] Calculate at mempool --- .../blocktemplatebuilder.go | 38 ++++++------------- domain/miningmanager/mempool/config.go | 3 ++ domain/miningmanager/mempool/mempool.go | 3 +- .../mempool/model/mempool_transaction.go | 17 +++++++++ domain/miningmanager/mempool/orphan_pool.go | 1 + .../mempool/transactions_pool.go | 29 ++++++++++++-- .../miningmanager/model/interface_mempool.go | 3 +- 7 files changed, 62 insertions(+), 32 deletions(-) diff --git a/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go b/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go index e21afe1275..00153ea627 100644 --- a/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go +++ b/domain/miningmanager/blocktemplatebuilder/blocktemplatebuilder.go @@ -5,6 +5,7 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/utils/merkle" "github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper" "github.com/kaspanet/kaspad/domain/consensusreference" + "github.com/kaspanet/kaspad/domain/miningmanager/mempool/model" "github.com/kaspanet/kaspad/util/mstime" "math" "sort" @@ -122,16 +123,18 @@ func (btb *blockTemplateBuilder) BuildBlockTemplate( mempoolTransactions := btb.mempool.BlockCandidateTransactions() candidateTxs := make([]*candidateTx, 0, len(mempoolTransactions)) for _, tx := range mempoolTransactions { - // Calculate the tx value + if tx.Mass() == 0 { + panic(errors.Errorf("BuildBlockTemplate expects transactions with populated mass: %s has 0 mass", tx.TransactionID())) + } gasLimit := uint64(0) - if !subnetworks.IsBuiltInOrNative(tx.SubnetworkID) { + if !subnetworks.IsBuiltInOrNative(tx.Transaction().SubnetworkID) { panic("We currently don't support non native subnetworks") } candidateTxs = append(candidateTxs, &candidateTx{ - DomainTransaction: tx, + DomainTransaction: tx.Transaction(), txValue: btb.calcTxValue(tx), gasLimit: gasLimit, - mass: btb.calcMass(tx), + mass: tx.Mass(), }) } @@ -174,23 +177,6 @@ func (btb *blockTemplateBuilder) BuildBlockTemplate( return blockTemplate, nil } -func (btb *blockTemplateBuilder) calcMass(tx *consensusexternalapi.DomainTransaction) uint64 { - // TODO: Insert link for relevant KIP - addedMass := uint64(0) - sumOutsValue := uint64(0) - for _, output := range tx.Outputs { - addedMass += btb.policy.DustConst / output.Value - sumOutsValue += output.Value - } - - reducedMass := uint64(len(tx.Inputs)*len(tx.Inputs)) * btb.policy.DustConst / sumOutsValue - if addedMass < reducedMass { - return tx.Mass - } - - return tx.Mass + addedMass - reducedMass -} - // ModifyBlockTemplate modifies an existing block template to the requested coinbase data and updates the timestamp func (btb *blockTemplateBuilder) ModifyBlockTemplate(newCoinbaseData *consensusexternalapi.DomainCoinbaseData, blockTemplateToModify *consensusexternalapi.DomainBlockTemplate) (*consensusexternalapi.DomainBlockTemplate, error) { @@ -227,15 +213,15 @@ func (btb *blockTemplateBuilder) ModifyBlockTemplate(newCoinbaseData *consensuse // calcTxValue calculates a value to be used in transaction selection. // The higher the number the more likely it is that the transaction will be // included in the block. -func (btb *blockTemplateBuilder) calcTxValue(tx *consensusexternalapi.DomainTransaction) float64 { +func (btb *blockTemplateBuilder) calcTxValue(tx *model.MempoolTransaction) float64 { massLimit := btb.policy.BlockMaxMass - mass := tx.Mass - fee := tx.Fee - if subnetworks.IsBuiltInOrNative(tx.SubnetworkID) { + mass := tx.Mass() + fee := tx.Transaction().Fee + if subnetworks.IsBuiltInOrNative(tx.Transaction().SubnetworkID) { return float64(fee) / (float64(mass) / float64(massLimit)) } // TODO: Replace with real gas once implemented gasLimit := uint64(math.MaxUint64) - return float64(fee) / (float64(mass)/float64(massLimit) + float64(tx.Gas)/float64(gasLimit)) + return float64(fee) / (float64(mass)/float64(massLimit) + float64(tx.Transaction().Gas)/float64(gasLimit)) } diff --git a/domain/miningmanager/mempool/config.go b/domain/miningmanager/mempool/config.go index dafa127165..9cb68c96a7 100644 --- a/domain/miningmanager/mempool/config.go +++ b/domain/miningmanager/mempool/config.go @@ -33,6 +33,7 @@ const ( // as consensus. defaultMinimumStandardTransactionVersion = constants.MaxTransactionVersion defaultMaximumStandardTransactionVersion = constants.MaxTransactionVersion + defaultDustConst = 1e12 // TODO: Determine real value ) // Config represents a mempool configuration @@ -50,6 +51,7 @@ type Config struct { MinimumRelayTransactionFee util.Amount MinimumStandardTransactionVersion uint16 MaximumStandardTransactionVersion uint16 + DustConst uint64 } // DefaultConfig returns the default mempool configuration @@ -70,5 +72,6 @@ func DefaultConfig(dagParams *dagconfig.Params) *Config { MinimumRelayTransactionFee: defaultMinimumRelayTransactionFee, MinimumStandardTransactionVersion: defaultMinimumStandardTransactionVersion, MaximumStandardTransactionVersion: defaultMaximumStandardTransactionVersion, + DustConst: defaultDustConst, } } diff --git a/domain/miningmanager/mempool/mempool.go b/domain/miningmanager/mempool/mempool.go index f4d6ef7bf3..2733ff5fb0 100644 --- a/domain/miningmanager/mempool/mempool.go +++ b/domain/miningmanager/mempool/mempool.go @@ -1,6 +1,7 @@ package mempool import ( + "github.com/kaspanet/kaspad/domain/miningmanager/mempool/model" "sync" "github.com/kaspanet/kaspad/domain/consensusreference" @@ -137,7 +138,7 @@ func (mp *mempool) HandleNewBlockTransactions(transactions []*externalapi.Domain return mp.handleNewBlockTransactions(transactions) } -func (mp *mempool) BlockCandidateTransactions() []*externalapi.DomainTransaction { +func (mp *mempool) BlockCandidateTransactions() []*model.MempoolTransaction { mp.mtx.RLock() defer mp.mtx.RUnlock() diff --git a/domain/miningmanager/mempool/model/mempool_transaction.go b/domain/miningmanager/mempool/model/mempool_transaction.go index 017e13c1fd..cc19a15972 100644 --- a/domain/miningmanager/mempool/model/mempool_transaction.go +++ b/domain/miningmanager/mempool/model/mempool_transaction.go @@ -11,6 +11,7 @@ type MempoolTransaction struct { parentTransactionsInPool IDToTransactionMap isHighPriority bool addedAtDAAScore uint64 + mass uint64 } // NewMempoolTransaction constructs a new MempoolTransaction @@ -19,12 +20,14 @@ func NewMempoolTransaction( parentTransactionsInPool IDToTransactionMap, isHighPriority bool, addedAtDAAScore uint64, + mass uint64, ) *MempoolTransaction { return &MempoolTransaction{ transaction: transaction, parentTransactionsInPool: parentTransactionsInPool, isHighPriority: isHighPriority, addedAtDAAScore: addedAtDAAScore, + mass: mass, } } @@ -57,3 +60,17 @@ func (mt *MempoolTransaction) IsHighPriority() bool { func (mt *MempoolTransaction) AddedAtDAAScore() uint64 { return mt.addedAtDAAScore } + +func (mt *MempoolTransaction) Mass() uint64 { + return mt.mass +} + +func (mt *MempoolTransaction) Clone() *MempoolTransaction { + return &MempoolTransaction{ + transaction: mt.transaction.Clone(), + parentTransactionsInPool: mt.parentTransactionsInPool, + isHighPriority: mt.isHighPriority, + addedAtDAAScore: mt.addedAtDAAScore, + mass: mt.mass, + } +} diff --git a/domain/miningmanager/mempool/orphan_pool.go b/domain/miningmanager/mempool/orphan_pool.go index 59767d5014..012b84074f 100644 --- a/domain/miningmanager/mempool/orphan_pool.go +++ b/domain/miningmanager/mempool/orphan_pool.go @@ -218,6 +218,7 @@ func (op *orphansPool) unorphanTransaction(transaction *model.OrphanTransaction) op.mempool.transactionsPool.getParentTransactionsInPool(transaction.Transaction()), false, virtualDAAScore, + 0, ) err = op.mempool.transactionsPool.addMempoolTransaction(mempoolTransaction) if err != nil { diff --git a/domain/miningmanager/mempool/transactions_pool.go b/domain/miningmanager/mempool/transactions_pool.go index 63908ee770..640590db83 100644 --- a/domain/miningmanager/mempool/transactions_pool.go +++ b/domain/miningmanager/mempool/transactions_pool.go @@ -41,7 +41,7 @@ func (tp *transactionsPool) addTransaction(transaction *externalapi.DomainTransa } mempoolTransaction := model.NewMempoolTransaction( - transaction, parentTransactionsInPool, isHighPriority, virtualDAAScore) + transaction, parentTransactionsInPool, isHighPriority, virtualDAAScore, tp.calcMass(transaction)) err = tp.addMempoolTransaction(mempoolTransaction) if err != nil { @@ -51,6 +51,27 @@ func (tp *transactionsPool) addTransaction(transaction *externalapi.DomainTransa return mempoolTransaction, nil } +func (tp *transactionsPool) calcMass(tx *externalapi.DomainTransaction) uint64 { + // TODO: Insert link for relevant KIP + if tx.Mass == 0 { + panic(errors.Errorf("tx %s is expected to have a populated mass", consensushashing.TransactionID(tx))) + } + + addedMass := uint64(0) + sumOutsValue := uint64(0) + for _, output := range tx.Outputs { + addedMass += tp.mempool.config.DustConst / output.Value + sumOutsValue += output.Value + } + + reducedMass := uint64(len(tx.Inputs)*len(tx.Inputs)) * tp.mempool.config.DustConst / sumOutsValue + if addedMass < reducedMass { + return tx.Mass + } + + return tx.Mass + addedMass - reducedMass +} + func (tp *transactionsPool) addMempoolTransaction(transaction *model.MempoolTransaction) error { tp.allTransactions[*transaction.TransactionID()] = transaction @@ -131,12 +152,12 @@ func (tp *transactionsPool) expireOldTransactions() error { return nil } -func (tp *transactionsPool) allReadyTransactions() []*externalapi.DomainTransaction { - result := []*externalapi.DomainTransaction{} +func (tp *transactionsPool) allReadyTransactions() []*model.MempoolTransaction { + result := []*model.MempoolTransaction{} for _, mempoolTransaction := range tp.allTransactions { if len(mempoolTransaction.ParentTransactionsInPool()) == 0 { - result = append(result, mempoolTransaction.Transaction().Clone()) //this pointer leaves the mempool, and gets its utxo set to nil, hence we clone. + result = append(result, mempoolTransaction.Clone()) //this pointer leaves the mempool, and gets its utxo set to nil, hence we clone. } } diff --git a/domain/miningmanager/model/interface_mempool.go b/domain/miningmanager/model/interface_mempool.go index fe9be9b5c5..bea3112d39 100644 --- a/domain/miningmanager/model/interface_mempool.go +++ b/domain/miningmanager/model/interface_mempool.go @@ -2,13 +2,14 @@ package model import ( "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/miningmanager/mempool/model" ) // Mempool maintains a set of known transactions that // are intended to be mined into new blocks type Mempool interface { HandleNewBlockTransactions(txs []*externalapi.DomainTransaction) ([]*externalapi.DomainTransaction, error) - BlockCandidateTransactions() []*externalapi.DomainTransaction + BlockCandidateTransactions() []*model.MempoolTransaction ValidateAndInsertTransaction(transaction *externalapi.DomainTransaction, isHighPriority bool, allowOrphan bool) ( acceptedTransactions []*externalapi.DomainTransaction, err error) RemoveTransactions(txs []*externalapi.DomainTransaction, removeRedeemers bool) error