mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-11-24 06:25:55 +00:00
Merge branch 'dev' into test-block-delay
This commit is contained in:
commit
0f7d66686f
@ -159,7 +159,7 @@ func (msg *MsgTx) IsCoinBase() bool {
|
||||
|
||||
// TxHash generates the Hash for the transaction.
|
||||
func (msg *MsgTx) TxHash() *externalapi.DomainHash {
|
||||
return consensushashing.TransactionHash(MsgTxToDomainTransaction(msg))
|
||||
return consensushashing.TransactionHash(MsgTxToDomainTransaction(msg), false)
|
||||
}
|
||||
|
||||
// TxID generates the Hash for the transaction without the signature script, gas and payload fields.
|
||||
|
||||
@ -134,7 +134,7 @@ func TestTx(t *testing.T) {
|
||||
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
||||
func TestTxHashAndID(t *testing.T) {
|
||||
txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
|
||||
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7"
|
||||
txID1Str := "e20225c3d065ee41743607ee627db44d01ef396dc9779b05b2caf55bac50e12d"
|
||||
wantTxID1, err := transactionid.FromString(txID1Str)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
||||
|
||||
@ -122,9 +122,20 @@ func (ctx *Context) PopulateTransactionWithVerboseData(
|
||||
}
|
||||
|
||||
ctx.Domain.Consensus().PopulateMass(domainTransaction)
|
||||
|
||||
var daaScore uint64
|
||||
if domainBlockHeader != nil {
|
||||
daaScore = domainBlockHeader.DAAScore()
|
||||
} else {
|
||||
daaScore, err = ctx.Domain.Consensus().GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
transaction.VerboseData = &appmessage.RPCTransactionVerboseData{
|
||||
TransactionID: consensushashing.TransactionID(domainTransaction).String(),
|
||||
Hash: consensushashing.TransactionHash(domainTransaction).String(),
|
||||
Hash: consensushashing.TransactionHash(domainTransaction, daaScore >= ctx.Config.NetParams().HFDAAScore).String(),
|
||||
Mass: domainTransaction.Mass,
|
||||
}
|
||||
if domainBlockHeader != nil {
|
||||
|
||||
@ -158,7 +158,7 @@ func (s *server) splitAndInputPerSplitCounts(transaction *serialization.Partiall
|
||||
// to calculate how much mass do all the inputs have
|
||||
transactionWithoutInputs := transaction.Tx.Clone()
|
||||
transactionWithoutInputs.Inputs = []*externalapi.DomainTransactionInput{}
|
||||
massWithoutInputs := s.txMassCalculator.CalculateTransactionMass(transactionWithoutInputs)
|
||||
massWithoutInputs := s.txMassCalculator.CalculateTransactionMass(transactionWithoutInputs, true)
|
||||
|
||||
massOfAllInputs := transactionMass - massWithoutInputs
|
||||
|
||||
@ -177,7 +177,7 @@ func (s *server) splitAndInputPerSplitCounts(transaction *serialization.Partiall
|
||||
return 0, 0, err
|
||||
}
|
||||
massForEverythingExceptInputsInSplitTransaction :=
|
||||
s.txMassCalculator.CalculateTransactionMass(splitTransactionWithoutInputs.Tx)
|
||||
s.txMassCalculator.CalculateTransactionMass(splitTransactionWithoutInputs.Tx, true)
|
||||
massForInputsInSplitTransaction := mempool.MaximumStandardTransactionMass - massForEverythingExceptInputsInSplitTransaction
|
||||
|
||||
inputsPerSplitCount = int(massForInputsInSplitTransaction / massPerInput)
|
||||
@ -245,7 +245,7 @@ func (s *server) estimateMassAfterSignatures(transaction *serialization.Partiall
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s.txMassCalculator.CalculateTransactionMass(transactionWithSignatures), nil
|
||||
return s.txMassCalculator.CalculateTransactionMass(transactionWithSignatures, true), nil
|
||||
}
|
||||
|
||||
func (s *server) moreUTXOsForMergeTransaction(alreadySelectedUTXOs []*libkaspawallet.UTXO, requiredAmount uint64) (
|
||||
|
||||
@ -58,7 +58,7 @@ func TestEstimateMassAfterSignatures(t *testing.T) {
|
||||
t.Fatalf("ExtractTransaction: %+v", err)
|
||||
}
|
||||
|
||||
actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionMass(extractedSignedTx)
|
||||
actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionMass(extractedSignedTx, true)
|
||||
|
||||
if estimatedMassAfterSignatures != actualMassAfterSignatures {
|
||||
t.Errorf("Estimated mass after signatures: %d but actually got %d",
|
||||
|
||||
@ -184,7 +184,7 @@ func createSplitTransactionsWithSchnorrPrivteKey(
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
}
|
||||
|
||||
if massCalculater.CalculateTransactionMass(currentTx)+extraMass >= mempool.MaximumStandardTransactionMass {
|
||||
if massCalculater.CalculateTransactionMass(currentTx, true)+extraMass >= mempool.MaximumStandardTransactionMass {
|
||||
|
||||
//in this loop we assume a transaction with one input and one output cannot violate max transaction mass, hence a sanity check.
|
||||
if len(currentTx.Inputs) == 1 {
|
||||
|
||||
@ -21,6 +21,7 @@ type consensus struct {
|
||||
|
||||
genesisBlock *externalapi.DomainBlock
|
||||
genesisHash *externalapi.DomainHash
|
||||
hfDAAScore uint64
|
||||
|
||||
expectedDAAWindowDurationInMilliseconds int64
|
||||
|
||||
@ -917,7 +918,7 @@ func (s *consensus) EstimateNetworkHashesPerSecond(startHash *externalapi.Domain
|
||||
}
|
||||
|
||||
func (s *consensus) PopulateMass(transaction *externalapi.DomainTransaction) {
|
||||
s.transactionValidator.PopulateMass(transaction)
|
||||
s.transactionValidator.PopulateMass(transaction, s.hfDAAScore)
|
||||
}
|
||||
|
||||
func (s *consensus) ResolveVirtual(progressReportCallback func(uint64, uint64)) error {
|
||||
|
||||
@ -216,6 +216,9 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
transactionValidator := transactionvalidator.New(config.BlockCoinbaseMaturity,
|
||||
config.EnableNonNativeSubnetworks,
|
||||
config.MaxCoinbasePayloadLength,
|
||||
config.HFDAAScore,
|
||||
config.K,
|
||||
config.CoinbasePayloadScriptPublicKeyMaxLength,
|
||||
dbManager,
|
||||
pastMedianTimeManager,
|
||||
ghostdagDataStore,
|
||||
@ -237,12 +240,15 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
config.GenesisBlock.Header.Bits())
|
||||
coinbaseManager := coinbasemanager.New(
|
||||
dbManager,
|
||||
|
||||
config.SubsidyGenesisReward,
|
||||
config.PreDeflationaryPhaseBaseSubsidy,
|
||||
config.CoinbasePayloadScriptPublicKeyMaxLength,
|
||||
config.GenesisHash,
|
||||
config.DeflationaryPhaseDaaScore,
|
||||
config.DeflationaryPhaseBaseSubsidy,
|
||||
config.HFDAAScore,
|
||||
|
||||
dagTraversalManager,
|
||||
ghostdagDataStore,
|
||||
acceptanceDataStore,
|
||||
@ -278,6 +284,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
config.MaxBlockParents,
|
||||
config.MergeSetSizeLimit,
|
||||
genesisHash,
|
||||
config.HFDAAScore,
|
||||
|
||||
ghostdagManager,
|
||||
dagTopologyManager,
|
||||
@ -346,6 +353,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
config.TimestampDeviationTolerance,
|
||||
config.TargetTimePerBlock,
|
||||
config.MaxBlockLevel,
|
||||
config.HFDAAScore,
|
||||
|
||||
dbManager,
|
||||
difficultyManager,
|
||||
@ -393,6 +401,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
blockBuilder := blockbuilder.New(
|
||||
dbManager,
|
||||
genesisHash,
|
||||
config.HFDAAScore,
|
||||
|
||||
difficultyManager,
|
||||
pastMedianTimeManager,
|
||||
@ -474,6 +483,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
|
||||
genesisBlock: config.GenesisBlock,
|
||||
genesisHash: config.GenesisHash,
|
||||
hfDAAScore: config.HFDAAScore,
|
||||
|
||||
expectedDAAWindowDurationInMilliseconds: config.TargetTimePerBlock.Milliseconds() *
|
||||
int64(config.DifficultyAdjustmentWindowSize),
|
||||
|
||||
@ -8,5 +8,5 @@ type CoinbaseManager interface {
|
||||
ExpectedCoinbaseTransaction(stagingArea *StagingArea, blockHash *externalapi.DomainHash,
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (expectedTransaction *externalapi.DomainTransaction, hasRedReward bool, err error)
|
||||
CalcBlockSubsidy(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (uint64, error)
|
||||
ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *externalapi.DomainTransaction) (blueScore uint64, coinbaseData *externalapi.DomainCoinbaseData, subsidy uint64, err error)
|
||||
ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *externalapi.DomainTransaction, postHF bool) (blueScore uint64, coinbaseData *externalapi.DomainCoinbaseData, subsidy uint64, err error)
|
||||
}
|
||||
|
||||
@ -12,5 +12,5 @@ type TransactionValidator interface {
|
||||
povBlockHash *externalapi.DomainHash, povBlockPastMedianTime int64) error
|
||||
ValidateTransactionInContextAndPopulateFee(stagingArea *StagingArea,
|
||||
tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash) error
|
||||
PopulateMass(transaction *externalapi.DomainTransaction)
|
||||
PopulateMass(transaction *externalapi.DomainTransaction, daaScore uint64)
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import (
|
||||
type blockBuilder struct {
|
||||
databaseContext model.DBManager
|
||||
genesisHash *externalapi.DomainHash
|
||||
hfDAAScore uint64
|
||||
|
||||
difficultyManager model.DifficultyManager
|
||||
pastMedianTimeManager model.PastMedianTimeManager
|
||||
@ -42,6 +43,7 @@ type blockBuilder struct {
|
||||
func New(
|
||||
databaseContext model.DBManager,
|
||||
genesisHash *externalapi.DomainHash,
|
||||
hfDAAScore uint64,
|
||||
|
||||
difficultyManager model.DifficultyManager,
|
||||
pastMedianTimeManager model.PastMedianTimeManager,
|
||||
@ -63,6 +65,7 @@ func New(
|
||||
return &blockBuilder{
|
||||
databaseContext: databaseContext,
|
||||
genesisHash: genesisHash,
|
||||
hfDAAScore: hfDAAScore,
|
||||
|
||||
difficultyManager: difficultyManager,
|
||||
pastMedianTimeManager: pastMedianTimeManager,
|
||||
@ -206,7 +209,7 @@ func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions)
|
||||
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions, daaScore >= bb.hfDAAScore)
|
||||
acceptedIDMerkleRoot, err := bb.newBlockAcceptedIDMerkleRoot(stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -279,8 +282,8 @@ func (bb *blockBuilder) newBlockDifficulty(stagingArea *model.StagingArea) (uint
|
||||
return bb.difficultyManager.RequiredDifficulty(stagingArea, model.VirtualBlockHash)
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) newBlockHashMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
return merkle.CalculateHashMerkleRoot(transactions)
|
||||
func (bb *blockBuilder) newBlockHashMerkleRoot(transactions []*externalapi.DomainTransaction, postHF bool) *externalapi.DomainHash {
|
||||
return merkle.CalculateHashMerkleRoot(transactions, postHF)
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) newBlockAcceptedIDMerkleRoot(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
||||
|
||||
@ -76,7 +76,7 @@ func (bb *testBlockBuilder) buildUTXOInvalidHeader(stagingArea *model.StagingAre
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions)
|
||||
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions, daaScore >= bb.hfDAAScore)
|
||||
|
||||
pruningPoint, err := bb.newBlockPruningPoint(stagingArea, tempBlockHash)
|
||||
if err != nil {
|
||||
@ -120,7 +120,7 @@ func (bb *testBlockBuilder) buildHeaderWithParents(stagingArea *model.StagingAre
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions)
|
||||
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions, daaScore >= bb.hfDAAScore)
|
||||
acceptedIDMerkleRoot, err := bb.calculateAcceptedIDMerkleRoot(acceptanceData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -94,7 +94,7 @@ func TestBlockStatus(t *testing.T) {
|
||||
invalidBlock.Header = blockheader.NewImmutableBlockHeader(
|
||||
disqualifiedBlock.Header.Version(),
|
||||
disqualifiedBlock.Header.Parents(),
|
||||
merkle.CalculateHashMerkleRoot(invalidBlock.Transactions),
|
||||
merkle.CalculateHashMerkleRoot(invalidBlock.Transactions, invalidBlock.Header.DAAScore() >= consensusConfig.HFDAAScore),
|
||||
disqualifiedBlock.Header.AcceptedIDMerkleRoot(),
|
||||
disqualifiedBlock.Header.UTXOCommitment(),
|
||||
disqualifiedBlock.Header.TimeInMilliseconds(),
|
||||
|
||||
@ -187,7 +187,7 @@ func (v *blockValidator) checkCoinbaseSubsidy(
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, subsidy, err := v.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(block.Transactions[transactionhelper.CoinbaseTransactionIndex])
|
||||
_, _, subsidy, err := v.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(block.Transactions[transactionhelper.CoinbaseTransactionIndex], block.Header.DAAScore() >= v.hfDAAScore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"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/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
|
||||
@ -88,11 +89,21 @@ func (v *blockValidator) ValidateBodyInIsolation(stagingArea *model.StagingArea,
|
||||
return err
|
||||
}
|
||||
|
||||
if block.Header.DAAScore() < v.hfDAAScore {
|
||||
totalInputs := 0
|
||||
for _, tx := range block.Transactions {
|
||||
totalInputs += len(tx.Inputs)
|
||||
if totalInputs > constants.MaxBlockInputsPreHF {
|
||||
return errors.Wrapf(ruleerrors.ErrOverMaxBlockInputsPreHF, "block has more than %d inputs", constants.MaxBlockInputsPreHF)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *blockValidator) checkCoinbaseBlueScore(block *externalapi.DomainBlock) error {
|
||||
coinbaseBlueScore, _, _, err := v.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(block.Transactions[transactionhelper.CoinbaseTransactionIndex])
|
||||
coinbaseBlueScore, _, _, err := v.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(block.Transactions[transactionhelper.CoinbaseTransactionIndex], block.Header.DAAScore() >= v.hfDAAScore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -151,7 +162,7 @@ func (v *blockValidator) checkTransactionsInIsolation(block *externalapi.DomainB
|
||||
}
|
||||
|
||||
func (v *blockValidator) checkBlockHashMerkleRoot(block *externalapi.DomainBlock) error {
|
||||
calculatedHashMerkleRoot := merkle.CalculateHashMerkleRoot(block.Transactions)
|
||||
calculatedHashMerkleRoot := merkle.CalculateHashMerkleRoot(block.Transactions, block.Header.DAAScore() >= v.hfDAAScore)
|
||||
if !block.Header.HashMerkleRoot().Equal(calculatedHashMerkleRoot) {
|
||||
return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block hash merkle root is invalid - block "+
|
||||
"header indicates %s, but calculated value is %s",
|
||||
@ -221,7 +232,7 @@ func (v *blockValidator) validateGasLimit(block *externalapi.DomainBlock) error
|
||||
func (v *blockValidator) checkBlockMass(block *externalapi.DomainBlock) error {
|
||||
mass := uint64(0)
|
||||
for _, transaction := range block.Transactions {
|
||||
v.transactionValidator.PopulateMass(transaction)
|
||||
v.transactionValidator.PopulateMass(transaction, block.Header.DAAScore())
|
||||
|
||||
massBefore := mass
|
||||
mass += transaction.Mass
|
||||
|
||||
@ -34,6 +34,7 @@ func TestBlockValidator_ValidateBodyInIsolation(t *testing.T) {
|
||||
CheckFirstBlockTransactionIsCoinbase,
|
||||
}
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.HFDAAScore = 10
|
||||
tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestChainedTransactions")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
@ -1309,7 +1310,7 @@ func initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig *consens
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
constants.BlockVersion,
|
||||
[]externalapi.BlockLevelParents{[]*externalapi.DomainHash{consensusConfig.GenesisHash}},
|
||||
merkle.CalculateHashMerkleRoot([]*externalapi.DomainTransaction{tx}),
|
||||
merkle.CalculateHashMerkleRoot([]*externalapi.DomainTransaction{tx}, consensusConfig.HFDAAScore == 0),
|
||||
&externalapi.DomainHash{},
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95,
|
||||
|
||||
@ -25,6 +25,7 @@ type blockValidator struct {
|
||||
timestampDeviationTolerance int
|
||||
targetTimePerBlock time.Duration
|
||||
maxBlockLevel int
|
||||
hfDAAScore uint64
|
||||
|
||||
databaseContext model.DBReader
|
||||
difficultyManager model.DifficultyManager
|
||||
@ -64,6 +65,7 @@ func New(powMax *big.Int,
|
||||
timestampDeviationTolerance int,
|
||||
targetTimePerBlock time.Duration,
|
||||
maxBlockLevel int,
|
||||
hfDAAScore uint64,
|
||||
|
||||
databaseContext model.DBReader,
|
||||
|
||||
@ -103,6 +105,7 @@ func New(powMax *big.Int,
|
||||
mergeSetSizeLimit: mergeSetSizeLimit,
|
||||
maxBlockParents: maxBlockParents,
|
||||
maxBlockLevel: maxBlockLevel,
|
||||
hfDAAScore: hfDAAScore,
|
||||
|
||||
timestampDeviationTolerance: timestampDeviationTolerance,
|
||||
targetTimePerBlock: targetTimePerBlock,
|
||||
|
||||
@ -181,7 +181,7 @@ func TestCheckParentHeadersExist(t *testing.T) {
|
||||
invalidBlock.Header = blockheader.NewImmutableBlockHeader(
|
||||
invalidBlock.Header.Version(),
|
||||
invalidBlock.Header.Parents(),
|
||||
merkle.CalculateHashMerkleRoot(invalidBlock.Transactions),
|
||||
merkle.CalculateHashMerkleRoot(invalidBlock.Transactions, orphanBlock.Header.DAAScore() >= consensusConfig.HFDAAScore),
|
||||
orphanBlock.Header.AcceptedIDMerkleRoot(),
|
||||
orphanBlock.Header.UTXOCommitment(),
|
||||
orphanBlock.Header.TimeInMilliseconds(),
|
||||
|
||||
@ -19,6 +19,7 @@ type coinbaseManager struct {
|
||||
genesisHash *externalapi.DomainHash
|
||||
deflationaryPhaseDaaScore uint64
|
||||
deflationaryPhaseBaseSubsidy uint64
|
||||
hfDAAScore uint64
|
||||
|
||||
databaseContext model.DBReader
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
@ -127,7 +128,8 @@ func (c *coinbaseManager) coinbaseOutputForBlueBlock(stagingArea *model.StagingA
|
||||
}
|
||||
|
||||
// the ScriptPublicKey for the coinbase is parsed from the coinbase payload
|
||||
_, coinbaseData, _, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(blockAcceptanceData.TransactionAcceptanceData[0].Transaction)
|
||||
// We pass postHF=true since it only affects the deserialization of the subsidy, which is not used in this context.
|
||||
_, coinbaseData, _, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(blockAcceptanceData.TransactionAcceptanceData[0].Transaction, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -265,7 +267,7 @@ func (c *coinbaseManager) calcMergedBlockReward(stagingArea *model.StagingArea,
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, _, subsidy, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(block.Transactions[transactionhelper.CoinbaseTransactionIndex])
|
||||
_, _, subsidy, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(block.Transactions[transactionhelper.CoinbaseTransactionIndex], block.Header.DAAScore() >= c.hfDAAScore)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -283,6 +285,7 @@ func New(
|
||||
genesisHash *externalapi.DomainHash,
|
||||
deflationaryPhaseDaaScore uint64,
|
||||
deflationaryPhaseBaseSubsidy uint64,
|
||||
hfDAAScore uint64,
|
||||
|
||||
dagTraversalManager model.DAGTraversalManager,
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
@ -301,6 +304,7 @@ func New(
|
||||
genesisHash: genesisHash,
|
||||
deflationaryPhaseDaaScore: deflationaryPhaseDaaScore,
|
||||
deflationaryPhaseBaseSubsidy: deflationaryPhaseBaseSubsidy,
|
||||
hfDAAScore: hfDAAScore,
|
||||
|
||||
dagTraversalManager: dagTraversalManager,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
package coinbasemanager_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/utils/testutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractCoinbaseDataBlueScoreAndSubsidy(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockStatus")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
scriptPublicKeyVersion uint16
|
||||
expectedScriptPublicKeyVersionBeforeHF uint16
|
||||
}{
|
||||
{
|
||||
name: "below 255",
|
||||
scriptPublicKeyVersion: 100,
|
||||
expectedScriptPublicKeyVersionBeforeHF: 100,
|
||||
},
|
||||
{
|
||||
name: "above 255",
|
||||
scriptPublicKeyVersion: 300,
|
||||
expectedScriptPublicKeyVersionBeforeHF: 44,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
coinbaseTx, _, err := tc.CoinbaseManager().ExpectedCoinbaseTransaction(model.NewStagingArea(), model.VirtualBlockHash, &externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: test.scriptPublicKeyVersion,
|
||||
},
|
||||
ExtraData: nil,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, cbData, _, err := tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cbData.ScriptPublicKey.Version != test.expectedScriptPublicKeyVersionBeforeHF {
|
||||
t.Fatalf("test %s pre HF expected %d but got %d", test.name, test.expectedScriptPublicKeyVersionBeforeHF, cbData.ScriptPublicKey.Version)
|
||||
}
|
||||
|
||||
_, cbData, _, err = tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cbData.ScriptPublicKey.Version != test.scriptPublicKeyVersion {
|
||||
t.Fatalf("test %s post HF expected %d but got %d", test.name, test.scriptPublicKeyVersion, cbData.ScriptPublicKey.Version)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
@ -21,6 +21,7 @@ func TestCalcDeflationaryPeriodBlockSubsidy(t *testing.T) {
|
||||
&externalapi.DomainHash{},
|
||||
deflationaryPhaseDaaScore,
|
||||
deflationaryPhaseBaseSubsidy,
|
||||
0,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
@ -96,6 +97,7 @@ func TestBuildSubsidyTable(t *testing.T) {
|
||||
&externalapi.DomainHash{},
|
||||
0,
|
||||
deflationaryPhaseBaseSubsidy,
|
||||
0,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
|
||||
@ -28,7 +28,7 @@ func (c *coinbaseManager) serializeCoinbasePayload(blueScore uint64,
|
||||
binary.LittleEndian.PutUint64(payload[:uint64Len], blueScore)
|
||||
binary.LittleEndian.PutUint64(payload[uint64Len:], subsidy)
|
||||
|
||||
payload[uint64Len+lengthOfSubsidy] = uint8(coinbaseData.ScriptPublicKey.Version)
|
||||
binary.LittleEndian.PutUint16(payload[uint64Len+lengthOfSubsidy:], coinbaseData.ScriptPublicKey.Version)
|
||||
payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script))
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script)
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData)
|
||||
@ -52,7 +52,7 @@ func ModifyCoinbasePayload(payload []byte, coinbaseData *externalapi.DomainCoinb
|
||||
payload = newPayload
|
||||
}
|
||||
|
||||
payload[uint64Len+lengthOfSubsidy] = uint8(coinbaseData.ScriptPublicKey.Version)
|
||||
binary.LittleEndian.PutUint16(payload[uint64Len+lengthOfSubsidy:uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey], coinbaseData.ScriptPublicKey.Version)
|
||||
payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script))
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script)
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData)
|
||||
@ -61,7 +61,7 @@ func ModifyCoinbasePayload(payload []byte, coinbaseData *externalapi.DomainCoinb
|
||||
}
|
||||
|
||||
// ExtractCoinbaseDataBlueScoreAndSubsidy deserializes the coinbase payload to its component (scriptPubKey, extra data, and subsidy).
|
||||
func (c *coinbaseManager) ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *externalapi.DomainTransaction) (
|
||||
func (c *coinbaseManager) ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *externalapi.DomainTransaction, postHF bool) (
|
||||
blueScore uint64, coinbaseData *externalapi.DomainCoinbaseData, subsidy uint64, err error) {
|
||||
|
||||
minLength := uint64Len + lengthOfSubsidy + lengthOfVersionScriptPubKey + lengthOfScriptPubKeyLength
|
||||
@ -74,6 +74,10 @@ func (c *coinbaseManager) ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *ext
|
||||
subsidy = binary.LittleEndian.Uint64(coinbaseTx.Payload[uint64Len:])
|
||||
|
||||
scriptPubKeyVersion := uint16(coinbaseTx.Payload[uint64Len+lengthOfSubsidy])
|
||||
if postHF {
|
||||
scriptPubKeyVersion = binary.LittleEndian.Uint16(coinbaseTx.Payload[uint64Len+lengthOfSubsidy : uint64Len+lengthOfSubsidy+uint16Len])
|
||||
}
|
||||
|
||||
scriptPubKeyScriptLength := coinbaseTx.Payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey]
|
||||
|
||||
if scriptPubKeyScriptLength > c.coinbasePayloadScriptPublicKeyMaxLength {
|
||||
|
||||
@ -10,6 +10,7 @@ type consensusStateManager struct {
|
||||
maxBlockParents externalapi.KType
|
||||
mergeSetSizeLimit uint64
|
||||
genesisHash *externalapi.DomainHash
|
||||
hfDAAScore uint64
|
||||
databaseContext model.DBManager
|
||||
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
@ -44,6 +45,7 @@ func New(
|
||||
maxBlockParents externalapi.KType,
|
||||
mergeSetSizeLimit uint64,
|
||||
genesisHash *externalapi.DomainHash,
|
||||
hfDAAScore uint64,
|
||||
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
@ -72,7 +74,9 @@ func New(
|
||||
maxBlockParents: maxBlockParents,
|
||||
mergeSetSizeLimit: mergeSetSizeLimit,
|
||||
genesisHash: genesisHash,
|
||||
databaseContext: databaseContext,
|
||||
hfDAAScore: hfDAAScore,
|
||||
|
||||
databaseContext: databaseContext,
|
||||
|
||||
ghostdagManager: ghostdagManager,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
|
||||
@ -39,7 +39,7 @@ func (csm *consensusStateManager) verifyUTXO(stagingArea *model.StagingArea, blo
|
||||
coinbaseTransaction := block.Transactions[0]
|
||||
log.Debugf("Validating coinbase transaction %s for block %s",
|
||||
consensushashing.TransactionID(coinbaseTransaction), blockHash)
|
||||
err = csm.validateCoinbaseTransaction(stagingArea, blockHash, coinbaseTransaction)
|
||||
err = csm.validateCoinbaseTransaction(stagingArea, blockHash, coinbaseTransaction, block.Header.DAAScore() >= csm.hfDAAScore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -153,14 +153,14 @@ func calculateAcceptedIDMerkleRoot(multiblockAcceptanceData externalapi.Acceptan
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) validateCoinbaseTransaction(stagingArea *model.StagingArea,
|
||||
blockHash *externalapi.DomainHash, coinbaseTransaction *externalapi.DomainTransaction) error {
|
||||
blockHash *externalapi.DomainHash, coinbaseTransaction *externalapi.DomainTransaction, postHF bool) error {
|
||||
|
||||
log.Tracef("validateCoinbaseTransaction start for block %s", blockHash)
|
||||
defer log.Tracef("validateCoinbaseTransaction end for block %s", blockHash)
|
||||
|
||||
log.Tracef("Extracting coinbase data for coinbase transaction %s in block %s",
|
||||
consensushashing.TransactionID(coinbaseTransaction), blockHash)
|
||||
_, coinbaseData, _, err := csm.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTransaction)
|
||||
_, coinbaseData, _, err := csm.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTransaction, postHF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -172,8 +172,8 @@ func (csm *consensusStateManager) validateCoinbaseTransaction(stagingArea *model
|
||||
return err
|
||||
}
|
||||
|
||||
coinbaseTransactionHash := consensushashing.TransactionHash(coinbaseTransaction)
|
||||
expectedCoinbaseTransactionHash := consensushashing.TransactionHash(expectedCoinbaseTransaction)
|
||||
coinbaseTransactionHash := consensushashing.TransactionHash(coinbaseTransaction, true)
|
||||
expectedCoinbaseTransactionHash := consensushashing.TransactionHash(expectedCoinbaseTransaction, true)
|
||||
log.Tracef("given coinbase hash: %s, expected coinbase hash: %s",
|
||||
coinbaseTransactionHash, expectedCoinbaseTransactionHash)
|
||||
|
||||
|
||||
@ -5,9 +5,9 @@ import (
|
||||
)
|
||||
|
||||
// PopulateMass calculates and populates the mass of the given transaction
|
||||
func (v *transactionValidator) PopulateMass(transaction *externalapi.DomainTransaction) {
|
||||
func (v *transactionValidator) PopulateMass(transaction *externalapi.DomainTransaction, daaScore uint64) {
|
||||
if transaction.Mass != 0 {
|
||||
return
|
||||
}
|
||||
transaction.Mass = v.txMassCalculator.CalculateTransactionMass(transaction)
|
||||
transaction.Mass = v.txMassCalculator.CalculateTransactionMass(transaction, daaScore >= v.hfDAAScore)
|
||||
}
|
||||
|
||||
@ -90,7 +90,12 @@ func (v *transactionValidator) ValidateTransactionInContextAndPopulateFee(stagin
|
||||
return err
|
||||
}
|
||||
|
||||
err = v.validateTransactionSigOpCounts(tx)
|
||||
daaScore, err := v.daaBlocksStore.DAAScore(v.databaseContext, stagingArea, povBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = v.validateTransactionSigOpCounts(tx, daaScore >= v.hfDAAScore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -341,7 +346,7 @@ func (v *transactionValidator) sequenceLockActive(sequenceLock *sequenceLock, bl
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *transactionValidator) validateTransactionSigOpCounts(tx *externalapi.DomainTransaction) error {
|
||||
func (v *transactionValidator) validateTransactionSigOpCounts(tx *externalapi.DomainTransaction, postHF bool) error {
|
||||
for i, input := range tx.Inputs {
|
||||
utxoEntry := input.UTXOEntry
|
||||
|
||||
@ -350,10 +355,20 @@ func (v *transactionValidator) validateTransactionSigOpCounts(tx *externalapi.Do
|
||||
sigScript := input.SignatureScript
|
||||
isP2SH := txscript.IsPayToScriptHash(utxoEntry.ScriptPublicKey())
|
||||
sigOpCount := txscript.GetPreciseSigOpCount(sigScript, utxoEntry.ScriptPublicKey(), isP2SH)
|
||||
if sigOpCount != int(input.SigOpCount) {
|
||||
return errors.Wrapf(ruleerrors.ErrWrongSigOpCount,
|
||||
"input %d specifies SigOpCount %d while actual SigOpCount is %d",
|
||||
i, input.SigOpCount, sigOpCount)
|
||||
|
||||
if postHF {
|
||||
if sigOpCount != int(input.SigOpCount) {
|
||||
return errors.Wrapf(ruleerrors.ErrWrongSigOpCount,
|
||||
"input %d specifies SigOpCount %d while actual SigOpCount is %d",
|
||||
i, input.SigOpCount, sigOpCount)
|
||||
}
|
||||
} else {
|
||||
const sigOpCountLimit = 10
|
||||
if sigOpCount > sigOpCountLimit {
|
||||
return errors.Wrapf(ruleerrors.ErrWrongSigOpCount,
|
||||
"input %d is using SigOpCount %d while the limit is %d",
|
||||
i, sigOpCount, sigOpCountLimit)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -23,7 +23,7 @@ func (v *transactionValidator) ValidateTransactionInIsolation(tx *externalapi.Do
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = v.checkCoinbaseLength(tx)
|
||||
err = v.checkCoinbaseInIsolation(tx, povDAAScore >= v.hfDAAScore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -114,7 +114,7 @@ func (v *transactionValidator) checkDuplicateTransactionInputs(tx *externalapi.D
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransaction) error {
|
||||
func (v *transactionValidator) checkCoinbaseInIsolation(tx *externalapi.DomainTransaction, postHF bool) error {
|
||||
if !transactionhelper.IsCoinBase(tx) {
|
||||
return nil
|
||||
}
|
||||
@ -127,6 +127,24 @@ func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransac
|
||||
payloadLen, v.maxCoinbasePayloadLength)
|
||||
}
|
||||
|
||||
if postHF {
|
||||
if len(tx.Inputs) != 0 {
|
||||
return errors.Wrap(ruleerrors.ErrCoinbaseWithInputs, "coinbase has inputs")
|
||||
}
|
||||
|
||||
outputsLimit := uint64(v.ghostdagK) + 2
|
||||
if uint64(len(tx.Outputs)) > outputsLimit {
|
||||
return errors.Wrapf(ruleerrors.ErrCoinbaseTooManyOutputs, "coinbase has too many outputs: got %d where the limit is %d", len(tx.Outputs), outputsLimit)
|
||||
}
|
||||
|
||||
for i, output := range tx.Outputs {
|
||||
if len(output.ScriptPublicKey.Script) > int(v.coinbasePayloadScriptPublicKeyMaxLength) {
|
||||
return errors.Wrapf(ruleerrors.ErrCoinbaseTooLongScriptPublicKey, "coinbase output %d has a too long script public key", i)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ type txSubnetworkData struct {
|
||||
func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
cfg := *consensusConfig
|
||||
cfg.HFDAAScore = 1000
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(&cfg, "TestValidateTransactionInIsolationAndPopulateMass")
|
||||
@ -69,7 +70,7 @@ func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) {
|
||||
nil,
|
||||
func(tx *externalapi.DomainTransaction) { tx.Inputs[1].PreviousOutpoint.Index = 0 },
|
||||
ruleerrors.ErrDuplicateTxInputs, 0},
|
||||
{"1 input coinbase",
|
||||
{"1 input coinbase - pre HF",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
@ -77,6 +78,14 @@ func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) {
|
||||
&txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, nil},
|
||||
nil,
|
||||
nil, 0},
|
||||
{"1 input coinbase - post HF",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
subnetworks.SubnetworkIDNative,
|
||||
&txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, nil},
|
||||
nil,
|
||||
ruleerrors.ErrCoinbaseWithInputs, cfg.HFDAAScore},
|
||||
{"no inputs coinbase",
|
||||
0,
|
||||
1,
|
||||
|
||||
@ -2,6 +2,7 @@ package transactionvalidator
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/util/txmass"
|
||||
)
|
||||
@ -11,22 +12,28 @@ const sigCacheSize = 10_000
|
||||
// transactionValidator exposes a set of validation classes, after which
|
||||
// it's possible to determine whether either a transaction is valid
|
||||
type transactionValidator struct {
|
||||
blockCoinbaseMaturity uint64
|
||||
databaseContext model.DBReader
|
||||
pastMedianTimeManager model.PastMedianTimeManager
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
daaBlocksStore model.DAABlocksStore
|
||||
enableNonNativeSubnetworks bool
|
||||
maxCoinbasePayloadLength uint64
|
||||
sigCache *txscript.SigCache
|
||||
sigCacheECDSA *txscript.SigCacheECDSA
|
||||
txMassCalculator *txmass.Calculator
|
||||
blockCoinbaseMaturity uint64
|
||||
databaseContext model.DBReader
|
||||
pastMedianTimeManager model.PastMedianTimeManager
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
daaBlocksStore model.DAABlocksStore
|
||||
enableNonNativeSubnetworks bool
|
||||
maxCoinbasePayloadLength uint64
|
||||
hfDAAScore uint64
|
||||
ghostdagK externalapi.KType
|
||||
coinbasePayloadScriptPublicKeyMaxLength uint8
|
||||
sigCache *txscript.SigCache
|
||||
sigCacheECDSA *txscript.SigCacheECDSA
|
||||
txMassCalculator *txmass.Calculator
|
||||
}
|
||||
|
||||
// New instantiates a new TransactionValidator
|
||||
func New(blockCoinbaseMaturity uint64,
|
||||
enableNonNativeSubnetworks bool,
|
||||
maxCoinbasePayloadLength uint64,
|
||||
hfDAAScore uint64,
|
||||
ghostdagK externalapi.KType,
|
||||
coinbasePayloadScriptPublicKeyMaxLength uint8,
|
||||
databaseContext model.DBReader,
|
||||
pastMedianTimeManager model.PastMedianTimeManager,
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
@ -34,15 +41,18 @@ func New(blockCoinbaseMaturity uint64,
|
||||
txMassCalculator *txmass.Calculator) model.TransactionValidator {
|
||||
|
||||
return &transactionValidator{
|
||||
blockCoinbaseMaturity: blockCoinbaseMaturity,
|
||||
enableNonNativeSubnetworks: enableNonNativeSubnetworks,
|
||||
maxCoinbasePayloadLength: maxCoinbasePayloadLength,
|
||||
databaseContext: databaseContext,
|
||||
pastMedianTimeManager: pastMedianTimeManager,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
daaBlocksStore: daaBlocksStore,
|
||||
sigCache: txscript.NewSigCache(sigCacheSize),
|
||||
sigCacheECDSA: txscript.NewSigCacheECDSA(sigCacheSize),
|
||||
txMassCalculator: txMassCalculator,
|
||||
blockCoinbaseMaturity: blockCoinbaseMaturity,
|
||||
enableNonNativeSubnetworks: enableNonNativeSubnetworks,
|
||||
maxCoinbasePayloadLength: maxCoinbasePayloadLength,
|
||||
hfDAAScore: hfDAAScore,
|
||||
ghostdagK: ghostdagK,
|
||||
coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength,
|
||||
databaseContext: databaseContext,
|
||||
pastMedianTimeManager: pastMedianTimeManager,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
daaBlocksStore: daaBlocksStore,
|
||||
sigCache: txscript.NewSigCache(sigCacheSize),
|
||||
sigCacheECDSA: txscript.NewSigCacheECDSA(sigCacheSize),
|
||||
txMassCalculator: txMassCalculator,
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,18 +109,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) {
|
||||
0),
|
||||
}
|
||||
|
||||
txInputWithBadSigOpCount := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: []byte{},
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
SigOpCount: 2,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100_000_000, // 1 KAS
|
||||
scriptPublicKey,
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
|
||||
txOutput := externalapi.DomainTransactionOutput{
|
||||
Value: 100000000, // 1 KAS
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
@ -193,13 +181,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) {
|
||||
SubnetworkID: subnetworks.SubnetworkIDRegistry,
|
||||
Gas: 0,
|
||||
LockTime: 0}
|
||||
txWithBadSigOpCount := externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInputWithBadSigOpCount},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{&txOutput},
|
||||
SubnetworkID: subnetworks.SubnetworkIDRegistry,
|
||||
Gas: 0,
|
||||
LockTime: 0}
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
@ -266,13 +247,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) {
|
||||
isValid: false,
|
||||
expectedError: ruleerrors.ErrScriptValidation,
|
||||
},
|
||||
{ // the SigOpCount in the input is wrong, and hence invalid
|
||||
name: "checkTransactionSigOpCounts",
|
||||
tx: &txWithBadSigOpCount,
|
||||
povBlockHash: povBlockHash,
|
||||
isValid: false,
|
||||
expectedError: ruleerrors.ErrWrongSigOpCount,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@ -241,6 +241,10 @@ var (
|
||||
ErrPruningProofEmpty = newRuleError("ErrPruningProofEmpty")
|
||||
ErrWrongCoinbaseSubsidy = newRuleError("ErrWrongCoinbaseSubsidy")
|
||||
ErrWrongBlockVersion = newRuleError("ErrWrongBlockVersion")
|
||||
ErrCoinbaseWithInputs = newRuleError("ErrCoinbaseWithInputs")
|
||||
ErrCoinbaseTooManyOutputs = newRuleError("ErrCoinbaseTooManyOutputs")
|
||||
ErrCoinbaseTooLongScriptPublicKey = newRuleError("ErrCoinbaseTooLongScriptPublicKey")
|
||||
ErrOverMaxBlockInputsPreHF = newRuleError("ErrOverMaxBlockInputsPreHF")
|
||||
)
|
||||
|
||||
// RuleError identifies a rule violation. It is used to indicate that
|
||||
|
||||
@ -23,11 +23,11 @@ const (
|
||||
)
|
||||
|
||||
// TransactionHash returns the transaction hash.
|
||||
func TransactionHash(tx *externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
func TransactionHash(tx *externalapi.DomainTransaction, postHF bool) *externalapi.DomainHash {
|
||||
// Encode the header and hash everything prior to the number of
|
||||
// transactions.
|
||||
writer := hashes.NewTransactionHashWriter()
|
||||
err := serializeTransaction(writer, tx, txEncodingFull)
|
||||
err := serializeTransaction(writer, tx, txEncodingFull, postHF)
|
||||
if err != nil {
|
||||
// It seems like this could only happen if the writer returned an error.
|
||||
// and this writer should never return an error (no allocations or possible failures)
|
||||
@ -52,7 +52,7 @@ func TransactionID(tx *externalapi.DomainTransaction) *externalapi.DomainTransac
|
||||
encodingFlags = txEncodingExcludeSignatureScript
|
||||
}
|
||||
writer := hashes.NewTransactionIDWriter()
|
||||
err := serializeTransaction(writer, tx, encodingFlags)
|
||||
err := serializeTransaction(writer, tx, encodingFlags, true)
|
||||
if err != nil {
|
||||
// this writer never return errors (no allocations or possible failures) so errors can only come from validity checks,
|
||||
// and we assume we never construct malformed transactions.
|
||||
@ -74,7 +74,7 @@ func TransactionIDs(txs []*externalapi.DomainTransaction) []*externalapi.DomainT
|
||||
return txIDs
|
||||
}
|
||||
|
||||
func serializeTransaction(w io.Writer, tx *externalapi.DomainTransaction, encodingFlags txEncoding) error {
|
||||
func serializeTransaction(w io.Writer, tx *externalapi.DomainTransaction, encodingFlags txEncoding, postHF bool) error {
|
||||
err := binaryserializer.PutUint16(w, tx.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -87,7 +87,7 @@ func serializeTransaction(w io.Writer, tx *externalapi.DomainTransaction, encodi
|
||||
}
|
||||
|
||||
for _, ti := range tx.Inputs {
|
||||
err = writeTransactionInput(w, ti, encodingFlags)
|
||||
err = writeTransactionInput(w, ti, encodingFlags, postHF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -131,7 +131,7 @@ func serializeTransaction(w io.Writer, tx *externalapi.DomainTransaction, encodi
|
||||
|
||||
// writeTransactionInput encodes ti to the kaspa protocol encoding for a transaction
|
||||
// input to w.
|
||||
func writeTransactionInput(w io.Writer, ti *externalapi.DomainTransactionInput, encodingFlags txEncoding) error {
|
||||
func writeTransactionInput(w io.Writer, ti *externalapi.DomainTransactionInput, encodingFlags txEncoding, postHF bool) error {
|
||||
err := writeOutpoint(w, &ti.PreviousOutpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -139,11 +139,21 @@ func writeTransactionInput(w io.Writer, ti *externalapi.DomainTransactionInput,
|
||||
|
||||
if encodingFlags&txEncodingExcludeSignatureScript != txEncodingExcludeSignatureScript {
|
||||
err = writeVarBytes(w, ti.SignatureScript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if postHF {
|
||||
_, err = w.Write([]byte{ti.SigOpCount})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = writeVarBytes(w, []byte{})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return binaryserializer.PutUint64(w, ti.Sequence)
|
||||
|
||||
@ -38,4 +38,7 @@ const (
|
||||
// UnacceptedDAAScore is used to for UTXOEntries that were created by transactions in the mempool, or otherwise
|
||||
// not-yet-accepted transactions.
|
||||
UnacceptedDAAScore = math.MaxUint64
|
||||
|
||||
// MaxBlockInputsPreHF is the maximum number of inputs a block can hold before the HF
|
||||
MaxBlockInputsPreHF = 900
|
||||
)
|
||||
|
||||
@ -37,10 +37,10 @@ func hashMerkleBranches(left, right *externalapi.DomainHash) *externalapi.Domain
|
||||
|
||||
// CalculateHashMerkleRoot calculates the merkle root of a tree consisted of the given transaction hashes.
|
||||
// See `merkleRoot` for more info.
|
||||
func CalculateHashMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
func CalculateHashMerkleRoot(transactions []*externalapi.DomainTransaction, postHF bool) *externalapi.DomainHash {
|
||||
txHashes := make([]*externalapi.DomainHash, len(transactions))
|
||||
for i, tx := range transactions {
|
||||
txHashes[i] = consensushashing.TransactionHash(tx)
|
||||
txHashes[i] = consensushashing.TransactionHash(tx, postHF)
|
||||
}
|
||||
return merkleRoot(txHashes)
|
||||
}
|
||||
|
||||
@ -188,6 +188,8 @@ type Params struct {
|
||||
MaxBlockLevel int
|
||||
|
||||
MergeDepth uint64
|
||||
|
||||
HFDAAScore uint64
|
||||
}
|
||||
|
||||
// NormalizeRPCServerAddress returns addr with the current network default
|
||||
@ -288,6 +290,8 @@ 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,
|
||||
|
||||
HFDAAScore: 27905000,
|
||||
}
|
||||
|
||||
// TestnetParams defines the network parameters for the test Kaspa network.
|
||||
@ -350,6 +354,7 @@ var TestnetParams = Params{
|
||||
|
||||
MaxBlockLevel: 250,
|
||||
MergeDepth: defaultMergeDepth,
|
||||
HFDAAScore: 14106400,
|
||||
}
|
||||
|
||||
// SimnetParams defines the network parameters for the simulation test Kaspa
|
||||
|
||||
@ -37,17 +37,19 @@ type blockTemplateBuilder struct {
|
||||
policy policy
|
||||
|
||||
coinbasePayloadScriptPublicKeyMaxLength uint8
|
||||
hfDAAScore uint64
|
||||
}
|
||||
|
||||
// New creates a new blockTemplateBuilder
|
||||
func New(consensusReference consensusreference.ConsensusReference, mempool miningmanagerapi.Mempool,
|
||||
blockMaxMass uint64, coinbasePayloadScriptPublicKeyMaxLength uint8) miningmanagerapi.BlockTemplateBuilder {
|
||||
blockMaxMass uint64, coinbasePayloadScriptPublicKeyMaxLength uint8, hfDAAScore uint64) miningmanagerapi.BlockTemplateBuilder {
|
||||
return &blockTemplateBuilder{
|
||||
consensusReference: consensusReference,
|
||||
mempool: mempool,
|
||||
policy: policy{BlockMaxMass: blockMaxMass},
|
||||
|
||||
coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength,
|
||||
hfDAAScore: hfDAAScore,
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +192,7 @@ func (btb *blockTemplateBuilder) ModifyBlockTemplate(newCoinbaseData *consensuse
|
||||
// Update the hash merkle root according to the modified transactions
|
||||
mutableHeader := blockTemplateToModify.Block.Header.ToMutable()
|
||||
// TODO: can be optimized to O(log(#transactions)) by caching the whole merkle tree in BlockTemplate and changing only the relevant path
|
||||
mutableHeader.SetHashMerkleRoot(merkle.CalculateHashMerkleRoot(blockTemplateToModify.Block.Transactions))
|
||||
mutableHeader.SetHashMerkleRoot(merkle.CalculateHashMerkleRoot(blockTemplateToModify.Block.Transactions, mutableHeader.DAAScore() >= btb.hfDAAScore))
|
||||
|
||||
newTimestamp := mstime.Now().UnixMilliseconds()
|
||||
if newTimestamp >= mutableHeader.TimeInMilliseconds() {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package blocktemplatebuilder
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
@ -74,6 +75,8 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
|
||||
usedP += candidateTx.p
|
||||
}
|
||||
|
||||
totalInputs := 0
|
||||
|
||||
selectedTxs := make([]*candidateTx, 0)
|
||||
for len(candidateTxs)-usedCount > 0 {
|
||||
// Rebalance the candidates if it's required
|
||||
@ -99,6 +102,10 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
|
||||
}
|
||||
tx := selectedTx.DomainTransaction
|
||||
|
||||
if totalInputs+len(tx.Inputs) > maxBlockInputsPreHF(btb.hfDAAScore) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Enforce maximum transaction mass per block. Also check
|
||||
// for overflow.
|
||||
if txsForBlockTemplate.totalMass+selectedTx.Mass < txsForBlockTemplate.totalMass ||
|
||||
@ -143,6 +150,7 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
|
||||
// save the masses, fees, and signature operation counts to the
|
||||
// result.
|
||||
selectedTxs = append(selectedTxs, selectedTx)
|
||||
totalInputs += len(selectedTx.Inputs)
|
||||
txsForBlockTemplate.totalMass += selectedTx.Mass
|
||||
txsForBlockTemplate.totalFees += selectedTx.Fee
|
||||
|
||||
@ -150,6 +158,10 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
|
||||
consensushashing.TransactionID(tx), selectedTx.Fee*1e6/selectedTx.Mass)
|
||||
|
||||
markCandidateTxForDeletion(selectedTx)
|
||||
|
||||
if totalInputs == maxBlockInputsPreHF(btb.hfDAAScore) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(selectedTxs, func(i, j int) bool {
|
||||
@ -163,6 +175,10 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
|
||||
return txsForBlockTemplate
|
||||
}
|
||||
|
||||
func maxBlockInputsPreHF(hfDAAScore uint64) int {
|
||||
return constants.MaxBlockInputsPreHF
|
||||
}
|
||||
|
||||
func rebalanceCandidates(oldCandidateTxs []*candidateTx, isFirstRun bool) (
|
||||
candidateTxs []*candidateTx, totalP float64) {
|
||||
|
||||
|
||||
@ -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.HFDAAScore)
|
||||
|
||||
return &miningManager{
|
||||
consensusReference: consensusReference,
|
||||
|
||||
@ -35,7 +35,7 @@ func (x *KaspadMessage_GetConnectedPeerInfoResponse) fromAppMessage(message *app
|
||||
TimeOffset: info.TimeOffset,
|
||||
UserAgent: info.UserAgent,
|
||||
AdvertisedProtocolVersion: info.AdvertisedProtocolVersion,
|
||||
TimeConnected: info.TimeOffset,
|
||||
TimeConnected: info.TimeConnected,
|
||||
IsIbdPeer: info.IsIBDPeer,
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func (c *Calculator) MassPerScriptPubKeyByte() uint64 { return c.massPerScriptPu
|
||||
func (c *Calculator) MassPerSigOp() uint64 { return c.massPerSigOp }
|
||||
|
||||
// CalculateTransactionMass calculates the mass of the given transaction
|
||||
func (c *Calculator) CalculateTransactionMass(transaction *externalapi.DomainTransaction) uint64 {
|
||||
func (c *Calculator) CalculateTransactionMass(transaction *externalapi.DomainTransaction, postHF bool) uint64 {
|
||||
if transactionhelper.IsCoinBase(transaction) {
|
||||
return 0
|
||||
}
|
||||
@ -49,11 +49,14 @@ func (c *Calculator) CalculateTransactionMass(transaction *externalapi.DomainTra
|
||||
massForScriptPubKey := totalScriptPubKeySize * c.massPerScriptPubKeyByte
|
||||
|
||||
// calculate mass for SigOps
|
||||
totalSigOpCount := uint64(0)
|
||||
for _, input := range transaction.Inputs {
|
||||
totalSigOpCount += uint64(input.SigOpCount)
|
||||
massForSigOps := uint64(0)
|
||||
if postHF {
|
||||
totalSigOpCount := uint64(0)
|
||||
for _, input := range transaction.Inputs {
|
||||
totalSigOpCount += uint64(input.SigOpCount)
|
||||
}
|
||||
massForSigOps = totalSigOpCount * c.massPerSigOp
|
||||
}
|
||||
massForSigOps := totalSigOpCount * c.massPerSigOp
|
||||
|
||||
// Sum all components of mass
|
||||
return massForSize + massForScriptPubKey + massForSigOps
|
||||
|
||||
@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs
|
||||
const (
|
||||
appMajor uint = 0
|
||||
appMinor uint = 12
|
||||
appPatch uint = 6
|
||||
appPatch uint = 7
|
||||
)
|
||||
|
||||
// appBuild is defined as a variable so it can be overridden during the build
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user