mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-28 09:46:50 +00:00

* Replace the old blockSubsidy parameters with the new ones. * Return subsidyGenesisReward if blockHash is the genesis hash. * Traverse a block's past for the subsidy calculation. * Partially implement SubsidyStore. * Refer to SubsidyStore from CoinbaseManager. * Wrap calcBlockSubsidy in getBlockSubsidy, which first checks the database. * Fix finalityStore not calling GenerateShardingID. * Implement calculateAveragePastSubsidy. * Implement calculateMergeSetSubsidySum. * Implement calculateSubsidyRandomVariable. * Implement calcBlockSubsidy. * Add a TODO about floats. * Update the calcBlockSubsidy TODO. * Use binary.LittleEndian in calculateSubsidyRandomVariable. * Fix bad range in calculateSubsidyRandomVariable. * Replace float64 with big.Rat everywhere except for subsidyRandomVariable. * Fix a nil dereference. * Use a random walk to approximate the normal distribution. * In order to avoid unsupported fractional results from powInt64, flip the numerator and the denominator manually. * Set standardDeviation to 0.25, MaxSompi to 10_000_000_000 * SompiPerKaspa and defaultSubsidyGenesisReward to 1_000. * Set the standard deviation to 0.2. * Use a binomial distribution instead of trying to estimate the normal distribution. * Change some values around. * Clamp the block subsidy. * Remove the fake duplicate constants in the util package. * Reduce MaxSompi to only 100m Kaspa to avoid hitting the uint64 ceiling. * Lower MaxSompi further to avoid new and exciting ways for the uint64 ceiling to be hit. * Remove debug logs. * Fix a couple of failing tests. * Fix TestBlockWindow. * Fix limitTransactionCount sometimes crashing on index-out-of-bounds. * In TrustedDataDataDAABlock, replace BlockHeader with DomainBlock * In calculateAveragePastSubsidy, use blockWindow instead of doing a BFS manually. * Remove the reference to DAGTopologyManager in coinbaseManager. * Add subsidy to the coinbase payload. * Get rid of the subsidy store and extract subsidies out of coinbase transactions. * Keep a blockWindow amount of blocks under the virtual for IBD purposes. * Manually remove the virtual genesis from the merge set. * Fix simnet genesis. * Fix TestPruning. * Fix TestCheckBlockIsNotPruned. * Fix TestBlockWindow. * Fix TestCalculateSignatureHashSchnorr. * Fix TestCalculateSignatureHashECDSA. * Fix serializing the wrong value into the coinbase payload. * Rename coinbaseOutputForBlueBlock to coinbaseOutputAndSubsidyForBlueBlock. * Add a TODO about optimizing trusted data DAA window blocks. * Expand on a comment in TestCheckBlockIsNotPruned. * In calcBlockSubsidy, divide the big.Int numerator by the big.Int denominator instead of converting to float64. * Clarify a comment. * Rename SubsidyMinGenesisReward to MinSubsidy. * Properly handle trusted data blocks in calculateMergeSetSubsidySum. * Use the first two bytes of the selected parent's hash for randomness instead of math/rand. * Restore maxSompi to what it used to be. * Fix TestPruning. * Fix TestAmountCreation. * Fix TestBlockWindow. * Fix TestAmountUnitConversions. * Increase the timeout in many-tips to 30 minutes. * Check coinbase subsidy for every block * Re-rename functions * Use shift instead of powInt64 to determine subsidyRandom Co-authored-by: Ori Newman <orinewman1@gmail.com>
218 lines
7.2 KiB
Go
218 lines
7.2 KiB
Go
package mempool
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/miningmanager/mempool/model"
|
|
"time"
|
|
)
|
|
|
|
type transactionsPool struct {
|
|
mempool *mempool
|
|
allTransactions model.IDToTransactionMap
|
|
highPriorityTransactions model.IDToTransactionMap
|
|
chainedTransactionsByPreviousOutpoint model.OutpointToTransactionMap
|
|
transactionsOrderedByFeeRate model.TransactionsOrderedByFeeRate
|
|
lastExpireScanDAAScore uint64
|
|
lastExpireScanTime time.Time
|
|
}
|
|
|
|
func newTransactionsPool(mp *mempool) *transactionsPool {
|
|
return &transactionsPool{
|
|
mempool: mp,
|
|
allTransactions: model.IDToTransactionMap{},
|
|
highPriorityTransactions: model.IDToTransactionMap{},
|
|
chainedTransactionsByPreviousOutpoint: model.OutpointToTransactionMap{},
|
|
transactionsOrderedByFeeRate: model.TransactionsOrderedByFeeRate{},
|
|
lastExpireScanDAAScore: 0,
|
|
lastExpireScanTime: time.Now(),
|
|
}
|
|
}
|
|
|
|
func (tp *transactionsPool) addTransaction(transaction *externalapi.DomainTransaction,
|
|
parentTransactionsInPool model.OutpointToTransactionMap, isHighPriority bool) (*model.MempoolTransaction, error) {
|
|
|
|
virtualDAAScore, err := tp.mempool.consensusReference.Consensus().GetVirtualDAAScore()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mempoolTransaction := model.NewMempoolTransaction(
|
|
transaction, parentTransactionsInPool, isHighPriority, virtualDAAScore)
|
|
|
|
err = tp.addMempoolTransaction(mempoolTransaction)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mempoolTransaction, nil
|
|
}
|
|
|
|
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.consensusReference.Consensus().GetVirtualDAAScore()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if virtualDAAScore-tp.lastExpireScanDAAScore < tp.mempool.config.TransactionExpireScanIntervalDAAScore ||
|
|
time.Since(tp.lastExpireScanTime).Seconds() < float64(tp.mempool.config.TransactionExpireScanIntervalSeconds) {
|
|
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
|
|
daaScoreSinceAdded := virtualDAAScore - mempoolTransaction.AddedAtDAAScore()
|
|
if daaScoreSinceAdded > tp.mempool.config.TransactionExpireIntervalDAAScore {
|
|
log.Debugf("Removing transaction %s, because it expired. DAAScore moved by %d, expire interval: %d",
|
|
mempoolTransaction.TransactionID(), daaScoreSinceAdded, tp.mempool.config.TransactionExpireIntervalDAAScore)
|
|
err = tp.mempool.removeTransaction(mempoolTransaction.TransactionID(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
tp.lastExpireScanDAAScore = virtualDAAScore
|
|
tp.lastExpireScanTime = time.Now()
|
|
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.OutpointToTransactionMap {
|
|
|
|
parentsTransactionsInPool := model.OutpointToTransactionMap{}
|
|
|
|
for _, input := range transaction.Inputs {
|
|
if transaction, ok := tp.allTransactions[input.PreviousOutpoint.TransactionID]; ok {
|
|
parentsTransactionsInPool[input.PreviousOutpoint] = transaction
|
|
}
|
|
}
|
|
|
|
return parentsTransactionsInPool
|
|
}
|
|
|
|
func (tp *transactionsPool) getRedeemers(transaction *model.MempoolTransaction) []*model.MempoolTransaction {
|
|
stack := []*model.MempoolTransaction{transaction}
|
|
redeemers := []*model.MempoolTransaction{}
|
|
for len(stack) > 0 {
|
|
var current *model.MempoolTransaction
|
|
last := len(stack) - 1
|
|
current, stack = stack[last], stack[:last]
|
|
|
|
outpoint := externalapi.DomainOutpoint{TransactionID: *current.TransactionID()}
|
|
for i := range current.Transaction().Outputs {
|
|
outpoint.Index = uint32(i)
|
|
if redeemerTransaction, ok := tp.chainedTransactionsByPreviousOutpoint[outpoint]; ok {
|
|
stack = append(stack, redeemerTransaction)
|
|
redeemers = append(redeemers, redeemerTransaction)
|
|
}
|
|
}
|
|
}
|
|
return redeemers
|
|
}
|
|
|
|
func (tp *transactionsPool) limitTransactionCount() error {
|
|
currentIndex := 0
|
|
|
|
for uint64(len(tp.allTransactions)) > tp.mempool.config.MaximumTransactionCount {
|
|
var transactionToRemove *model.MempoolTransaction
|
|
for {
|
|
transactionToRemove = tp.transactionsOrderedByFeeRate.GetByIndex(currentIndex)
|
|
if !transactionToRemove.IsHighPriority() {
|
|
break
|
|
}
|
|
currentIndex++
|
|
if currentIndex >= len(tp.allTransactions) {
|
|
log.Warnf(
|
|
"Number of high-priority transactions in mempool (%d) is higher than maximum allowed (%d)",
|
|
len(tp.allTransactions), tp.mempool.config.MaximumTransactionCount)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
log.Debugf("Removing transaction %s, because mempoolTransaction count (%d) exceeded the limit (%d)",
|
|
transactionToRemove.TransactionID(), len(tp.allTransactions), tp.mempool.config.MaximumTransactionCount)
|
|
err := tp.mempool.removeTransaction(transactionToRemove.TransactionID(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if currentIndex >= len(tp.allTransactions) {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tp *transactionsPool) getTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
|
|
if mempoolTransaction, ok := tp.allTransactions[*transactionID]; ok {
|
|
return mempoolTransaction.Transaction(), true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func (tp *transactionsPool) getAllTransactions() []*externalapi.DomainTransaction {
|
|
allTransactions := make([]*externalapi.DomainTransaction, 0, len(tp.allTransactions))
|
|
for _, mempoolTransaction := range tp.allTransactions {
|
|
allTransactions = append(allTransactions, mempoolTransaction.Transaction())
|
|
}
|
|
return allTransactions
|
|
}
|
|
|
|
func (tp *transactionsPool) transactionCount() int {
|
|
return len(tp.allTransactions)
|
|
}
|