Merge branch 'dev' into test-block-delay

This commit is contained in:
Ori Newman 2022-09-23 09:55:59 +03:00 committed by GitHub
commit 0f7d66686f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 304 additions and 112 deletions

View File

@ -159,7 +159,7 @@ func (msg *MsgTx) IsCoinBase() bool {
// TxHash generates the Hash for the transaction. // TxHash generates the Hash for the transaction.
func (msg *MsgTx) TxHash() *externalapi.DomainHash { 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. // TxID generates the Hash for the transaction without the signature script, gas and payload fields.

View File

@ -134,7 +134,7 @@ func TestTx(t *testing.T) {
// TestTxHash tests the ability to generate the hash of a transaction accurately. // TestTxHash tests the ability to generate the hash of a transaction accurately.
func TestTxHashAndID(t *testing.T) { func TestTxHashAndID(t *testing.T) {
txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc" txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7" txID1Str := "e20225c3d065ee41743607ee627db44d01ef396dc9779b05b2caf55bac50e12d"
wantTxID1, err := transactionid.FromString(txID1Str) wantTxID1, err := transactionid.FromString(txID1Str)
if err != nil { if err != nil {
t.Fatalf("NewTxIDFromStr: %v", err) t.Fatalf("NewTxIDFromStr: %v", err)

View File

@ -122,9 +122,20 @@ func (ctx *Context) PopulateTransactionWithVerboseData(
} }
ctx.Domain.Consensus().PopulateMass(domainTransaction) 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{ transaction.VerboseData = &appmessage.RPCTransactionVerboseData{
TransactionID: consensushashing.TransactionID(domainTransaction).String(), TransactionID: consensushashing.TransactionID(domainTransaction).String(),
Hash: consensushashing.TransactionHash(domainTransaction).String(), Hash: consensushashing.TransactionHash(domainTransaction, daaScore >= ctx.Config.NetParams().HFDAAScore).String(),
Mass: domainTransaction.Mass, Mass: domainTransaction.Mass,
} }
if domainBlockHeader != nil { if domainBlockHeader != nil {

View File

@ -158,7 +158,7 @@ func (s *server) splitAndInputPerSplitCounts(transaction *serialization.Partiall
// to calculate how much mass do all the inputs have // to calculate how much mass do all the inputs have
transactionWithoutInputs := transaction.Tx.Clone() transactionWithoutInputs := transaction.Tx.Clone()
transactionWithoutInputs.Inputs = []*externalapi.DomainTransactionInput{} transactionWithoutInputs.Inputs = []*externalapi.DomainTransactionInput{}
massWithoutInputs := s.txMassCalculator.CalculateTransactionMass(transactionWithoutInputs) massWithoutInputs := s.txMassCalculator.CalculateTransactionMass(transactionWithoutInputs, true)
massOfAllInputs := transactionMass - massWithoutInputs massOfAllInputs := transactionMass - massWithoutInputs
@ -177,7 +177,7 @@ func (s *server) splitAndInputPerSplitCounts(transaction *serialization.Partiall
return 0, 0, err return 0, 0, err
} }
massForEverythingExceptInputsInSplitTransaction := massForEverythingExceptInputsInSplitTransaction :=
s.txMassCalculator.CalculateTransactionMass(splitTransactionWithoutInputs.Tx) s.txMassCalculator.CalculateTransactionMass(splitTransactionWithoutInputs.Tx, true)
massForInputsInSplitTransaction := mempool.MaximumStandardTransactionMass - massForEverythingExceptInputsInSplitTransaction massForInputsInSplitTransaction := mempool.MaximumStandardTransactionMass - massForEverythingExceptInputsInSplitTransaction
inputsPerSplitCount = int(massForInputsInSplitTransaction / massPerInput) inputsPerSplitCount = int(massForInputsInSplitTransaction / massPerInput)
@ -245,7 +245,7 @@ func (s *server) estimateMassAfterSignatures(transaction *serialization.Partiall
return 0, err 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) ( func (s *server) moreUTXOsForMergeTransaction(alreadySelectedUTXOs []*libkaspawallet.UTXO, requiredAmount uint64) (

View File

@ -58,7 +58,7 @@ func TestEstimateMassAfterSignatures(t *testing.T) {
t.Fatalf("ExtractTransaction: %+v", err) t.Fatalf("ExtractTransaction: %+v", err)
} }
actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionMass(extractedSignedTx) actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionMass(extractedSignedTx, true)
if estimatedMassAfterSignatures != actualMassAfterSignatures { if estimatedMassAfterSignatures != actualMassAfterSignatures {
t.Errorf("Estimated mass after signatures: %d but actually got %d", t.Errorf("Estimated mass after signatures: %d but actually got %d",

View File

@ -184,7 +184,7 @@ func createSplitTransactionsWithSchnorrPrivteKey(
ScriptPublicKey: scriptPublicKey, 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. //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 { if len(currentTx.Inputs) == 1 {

View File

@ -21,6 +21,7 @@ type consensus struct {
genesisBlock *externalapi.DomainBlock genesisBlock *externalapi.DomainBlock
genesisHash *externalapi.DomainHash genesisHash *externalapi.DomainHash
hfDAAScore uint64
expectedDAAWindowDurationInMilliseconds int64 expectedDAAWindowDurationInMilliseconds int64
@ -917,7 +918,7 @@ func (s *consensus) EstimateNetworkHashesPerSecond(startHash *externalapi.Domain
} }
func (s *consensus) PopulateMass(transaction *externalapi.DomainTransaction) { 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 { func (s *consensus) ResolveVirtual(progressReportCallback func(uint64, uint64)) error {

View File

@ -216,6 +216,9 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
transactionValidator := transactionvalidator.New(config.BlockCoinbaseMaturity, transactionValidator := transactionvalidator.New(config.BlockCoinbaseMaturity,
config.EnableNonNativeSubnetworks, config.EnableNonNativeSubnetworks,
config.MaxCoinbasePayloadLength, config.MaxCoinbasePayloadLength,
config.HFDAAScore,
config.K,
config.CoinbasePayloadScriptPublicKeyMaxLength,
dbManager, dbManager,
pastMedianTimeManager, pastMedianTimeManager,
ghostdagDataStore, ghostdagDataStore,
@ -237,12 +240,15 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
config.GenesisBlock.Header.Bits()) config.GenesisBlock.Header.Bits())
coinbaseManager := coinbasemanager.New( coinbaseManager := coinbasemanager.New(
dbManager, dbManager,
config.SubsidyGenesisReward, config.SubsidyGenesisReward,
config.PreDeflationaryPhaseBaseSubsidy, config.PreDeflationaryPhaseBaseSubsidy,
config.CoinbasePayloadScriptPublicKeyMaxLength, config.CoinbasePayloadScriptPublicKeyMaxLength,
config.GenesisHash, config.GenesisHash,
config.DeflationaryPhaseDaaScore, config.DeflationaryPhaseDaaScore,
config.DeflationaryPhaseBaseSubsidy, config.DeflationaryPhaseBaseSubsidy,
config.HFDAAScore,
dagTraversalManager, dagTraversalManager,
ghostdagDataStore, ghostdagDataStore,
acceptanceDataStore, acceptanceDataStore,
@ -278,6 +284,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
config.MaxBlockParents, config.MaxBlockParents,
config.MergeSetSizeLimit, config.MergeSetSizeLimit,
genesisHash, genesisHash,
config.HFDAAScore,
ghostdagManager, ghostdagManager,
dagTopologyManager, dagTopologyManager,
@ -346,6 +353,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
config.TimestampDeviationTolerance, config.TimestampDeviationTolerance,
config.TargetTimePerBlock, config.TargetTimePerBlock,
config.MaxBlockLevel, config.MaxBlockLevel,
config.HFDAAScore,
dbManager, dbManager,
difficultyManager, difficultyManager,
@ -393,6 +401,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
blockBuilder := blockbuilder.New( blockBuilder := blockbuilder.New(
dbManager, dbManager,
genesisHash, genesisHash,
config.HFDAAScore,
difficultyManager, difficultyManager,
pastMedianTimeManager, pastMedianTimeManager,
@ -474,6 +483,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
genesisBlock: config.GenesisBlock, genesisBlock: config.GenesisBlock,
genesisHash: config.GenesisHash, genesisHash: config.GenesisHash,
hfDAAScore: config.HFDAAScore,
expectedDAAWindowDurationInMilliseconds: config.TargetTimePerBlock.Milliseconds() * expectedDAAWindowDurationInMilliseconds: config.TargetTimePerBlock.Milliseconds() *
int64(config.DifficultyAdjustmentWindowSize), int64(config.DifficultyAdjustmentWindowSize),

View File

@ -8,5 +8,5 @@ type CoinbaseManager interface {
ExpectedCoinbaseTransaction(stagingArea *StagingArea, blockHash *externalapi.DomainHash, ExpectedCoinbaseTransaction(stagingArea *StagingArea, blockHash *externalapi.DomainHash,
coinbaseData *externalapi.DomainCoinbaseData) (expectedTransaction *externalapi.DomainTransaction, hasRedReward bool, err error) coinbaseData *externalapi.DomainCoinbaseData) (expectedTransaction *externalapi.DomainTransaction, hasRedReward bool, err error)
CalcBlockSubsidy(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (uint64, 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)
} }

View File

@ -12,5 +12,5 @@ type TransactionValidator interface {
povBlockHash *externalapi.DomainHash, povBlockPastMedianTime int64) error povBlockHash *externalapi.DomainHash, povBlockPastMedianTime int64) error
ValidateTransactionInContextAndPopulateFee(stagingArea *StagingArea, ValidateTransactionInContextAndPopulateFee(stagingArea *StagingArea,
tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash) error tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash) error
PopulateMass(transaction *externalapi.DomainTransaction) PopulateMass(transaction *externalapi.DomainTransaction, daaScore uint64)
} }

View File

@ -20,6 +20,7 @@ import (
type blockBuilder struct { type blockBuilder struct {
databaseContext model.DBManager databaseContext model.DBManager
genesisHash *externalapi.DomainHash genesisHash *externalapi.DomainHash
hfDAAScore uint64
difficultyManager model.DifficultyManager difficultyManager model.DifficultyManager
pastMedianTimeManager model.PastMedianTimeManager pastMedianTimeManager model.PastMedianTimeManager
@ -42,6 +43,7 @@ type blockBuilder struct {
func New( func New(
databaseContext model.DBManager, databaseContext model.DBManager,
genesisHash *externalapi.DomainHash, genesisHash *externalapi.DomainHash,
hfDAAScore uint64,
difficultyManager model.DifficultyManager, difficultyManager model.DifficultyManager,
pastMedianTimeManager model.PastMedianTimeManager, pastMedianTimeManager model.PastMedianTimeManager,
@ -63,6 +65,7 @@ func New(
return &blockBuilder{ return &blockBuilder{
databaseContext: databaseContext, databaseContext: databaseContext,
genesisHash: genesisHash, genesisHash: genesisHash,
hfDAAScore: hfDAAScore,
difficultyManager: difficultyManager, difficultyManager: difficultyManager,
pastMedianTimeManager: pastMedianTimeManager, pastMedianTimeManager: pastMedianTimeManager,
@ -206,7 +209,7 @@ func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions
if err != nil { if err != nil {
return nil, err return nil, err
} }
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions) hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions, daaScore >= bb.hfDAAScore)
acceptedIDMerkleRoot, err := bb.newBlockAcceptedIDMerkleRoot(stagingArea) acceptedIDMerkleRoot, err := bb.newBlockAcceptedIDMerkleRoot(stagingArea)
if err != nil { if err != nil {
return nil, err return nil, err
@ -279,8 +282,8 @@ func (bb *blockBuilder) newBlockDifficulty(stagingArea *model.StagingArea) (uint
return bb.difficultyManager.RequiredDifficulty(stagingArea, model.VirtualBlockHash) return bb.difficultyManager.RequiredDifficulty(stagingArea, model.VirtualBlockHash)
} }
func (bb *blockBuilder) newBlockHashMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash { func (bb *blockBuilder) newBlockHashMerkleRoot(transactions []*externalapi.DomainTransaction, postHF bool) *externalapi.DomainHash {
return merkle.CalculateHashMerkleRoot(transactions) return merkle.CalculateHashMerkleRoot(transactions, postHF)
} }
func (bb *blockBuilder) newBlockAcceptedIDMerkleRoot(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) { func (bb *blockBuilder) newBlockAcceptedIDMerkleRoot(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {

View File

@ -76,7 +76,7 @@ func (bb *testBlockBuilder) buildUTXOInvalidHeader(stagingArea *model.StagingAre
return nil, err return nil, err
} }
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions) hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions, daaScore >= bb.hfDAAScore)
pruningPoint, err := bb.newBlockPruningPoint(stagingArea, tempBlockHash) pruningPoint, err := bb.newBlockPruningPoint(stagingArea, tempBlockHash)
if err != nil { if err != nil {
@ -120,7 +120,7 @@ func (bb *testBlockBuilder) buildHeaderWithParents(stagingArea *model.StagingAre
return nil, err return nil, err
} }
hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions) hashMerkleRoot := bb.newBlockHashMerkleRoot(transactions, daaScore >= bb.hfDAAScore)
acceptedIDMerkleRoot, err := bb.calculateAcceptedIDMerkleRoot(acceptanceData) acceptedIDMerkleRoot, err := bb.calculateAcceptedIDMerkleRoot(acceptanceData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -94,7 +94,7 @@ func TestBlockStatus(t *testing.T) {
invalidBlock.Header = blockheader.NewImmutableBlockHeader( invalidBlock.Header = blockheader.NewImmutableBlockHeader(
disqualifiedBlock.Header.Version(), disqualifiedBlock.Header.Version(),
disqualifiedBlock.Header.Parents(), disqualifiedBlock.Header.Parents(),
merkle.CalculateHashMerkleRoot(invalidBlock.Transactions), merkle.CalculateHashMerkleRoot(invalidBlock.Transactions, invalidBlock.Header.DAAScore() >= consensusConfig.HFDAAScore),
disqualifiedBlock.Header.AcceptedIDMerkleRoot(), disqualifiedBlock.Header.AcceptedIDMerkleRoot(),
disqualifiedBlock.Header.UTXOCommitment(), disqualifiedBlock.Header.UTXOCommitment(),
disqualifiedBlock.Header.TimeInMilliseconds(), disqualifiedBlock.Header.TimeInMilliseconds(),

View File

@ -187,7 +187,7 @@ func (v *blockValidator) checkCoinbaseSubsidy(
return err 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 { if err != nil {
return err return err
} }

View File

@ -5,6 +5,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" "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/merkle"
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks" "github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper" "github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
@ -88,11 +89,21 @@ func (v *blockValidator) ValidateBodyInIsolation(stagingArea *model.StagingArea,
return err 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 return nil
} }
func (v *blockValidator) checkCoinbaseBlueScore(block *externalapi.DomainBlock) error { 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 { if err != nil {
return err return err
} }
@ -151,7 +162,7 @@ func (v *blockValidator) checkTransactionsInIsolation(block *externalapi.DomainB
} }
func (v *blockValidator) checkBlockHashMerkleRoot(block *externalapi.DomainBlock) error { 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) { if !block.Header.HashMerkleRoot().Equal(calculatedHashMerkleRoot) {
return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block hash merkle root is invalid - block "+ return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block hash merkle root is invalid - block "+
"header indicates %s, but calculated value is %s", "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 { func (v *blockValidator) checkBlockMass(block *externalapi.DomainBlock) error {
mass := uint64(0) mass := uint64(0)
for _, transaction := range block.Transactions { for _, transaction := range block.Transactions {
v.transactionValidator.PopulateMass(transaction) v.transactionValidator.PopulateMass(transaction, block.Header.DAAScore())
massBefore := mass massBefore := mass
mass += transaction.Mass mass += transaction.Mass

View File

@ -34,6 +34,7 @@ func TestBlockValidator_ValidateBodyInIsolation(t *testing.T) {
CheckFirstBlockTransactionIsCoinbase, CheckFirstBlockTransactionIsCoinbase,
} }
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
consensusConfig.HFDAAScore = 10
tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestChainedTransactions") tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestChainedTransactions")
if err != nil { if err != nil {
t.Fatalf("Error setting up consensus: %+v", err) t.Fatalf("Error setting up consensus: %+v", err)
@ -1309,7 +1310,7 @@ func initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig *consens
Header: blockheader.NewImmutableBlockHeader( Header: blockheader.NewImmutableBlockHeader(
constants.BlockVersion, constants.BlockVersion,
[]externalapi.BlockLevelParents{[]*externalapi.DomainHash{consensusConfig.GenesisHash}}, []externalapi.BlockLevelParents{[]*externalapi.DomainHash{consensusConfig.GenesisHash}},
merkle.CalculateHashMerkleRoot([]*externalapi.DomainTransaction{tx}), merkle.CalculateHashMerkleRoot([]*externalapi.DomainTransaction{tx}, consensusConfig.HFDAAScore == 0),
&externalapi.DomainHash{}, &externalapi.DomainHash{},
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95, 0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95,

View File

@ -25,6 +25,7 @@ type blockValidator struct {
timestampDeviationTolerance int timestampDeviationTolerance int
targetTimePerBlock time.Duration targetTimePerBlock time.Duration
maxBlockLevel int maxBlockLevel int
hfDAAScore uint64
databaseContext model.DBReader databaseContext model.DBReader
difficultyManager model.DifficultyManager difficultyManager model.DifficultyManager
@ -64,6 +65,7 @@ func New(powMax *big.Int,
timestampDeviationTolerance int, timestampDeviationTolerance int,
targetTimePerBlock time.Duration, targetTimePerBlock time.Duration,
maxBlockLevel int, maxBlockLevel int,
hfDAAScore uint64,
databaseContext model.DBReader, databaseContext model.DBReader,
@ -103,6 +105,7 @@ func New(powMax *big.Int,
mergeSetSizeLimit: mergeSetSizeLimit, mergeSetSizeLimit: mergeSetSizeLimit,
maxBlockParents: maxBlockParents, maxBlockParents: maxBlockParents,
maxBlockLevel: maxBlockLevel, maxBlockLevel: maxBlockLevel,
hfDAAScore: hfDAAScore,
timestampDeviationTolerance: timestampDeviationTolerance, timestampDeviationTolerance: timestampDeviationTolerance,
targetTimePerBlock: targetTimePerBlock, targetTimePerBlock: targetTimePerBlock,

View File

@ -181,7 +181,7 @@ func TestCheckParentHeadersExist(t *testing.T) {
invalidBlock.Header = blockheader.NewImmutableBlockHeader( invalidBlock.Header = blockheader.NewImmutableBlockHeader(
invalidBlock.Header.Version(), invalidBlock.Header.Version(),
invalidBlock.Header.Parents(), invalidBlock.Header.Parents(),
merkle.CalculateHashMerkleRoot(invalidBlock.Transactions), merkle.CalculateHashMerkleRoot(invalidBlock.Transactions, orphanBlock.Header.DAAScore() >= consensusConfig.HFDAAScore),
orphanBlock.Header.AcceptedIDMerkleRoot(), orphanBlock.Header.AcceptedIDMerkleRoot(),
orphanBlock.Header.UTXOCommitment(), orphanBlock.Header.UTXOCommitment(),
orphanBlock.Header.TimeInMilliseconds(), orphanBlock.Header.TimeInMilliseconds(),

View File

@ -19,6 +19,7 @@ type coinbaseManager struct {
genesisHash *externalapi.DomainHash genesisHash *externalapi.DomainHash
deflationaryPhaseDaaScore uint64 deflationaryPhaseDaaScore uint64
deflationaryPhaseBaseSubsidy uint64 deflationaryPhaseBaseSubsidy uint64
hfDAAScore uint64
databaseContext model.DBReader databaseContext model.DBReader
dagTraversalManager model.DAGTraversalManager 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 // 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 { if err != nil {
return nil, false, err return nil, false, err
} }
@ -265,7 +267,7 @@ func (c *coinbaseManager) calcMergedBlockReward(stagingArea *model.StagingArea,
return 0, err 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 { if err != nil {
return 0, err return 0, err
} }
@ -283,6 +285,7 @@ func New(
genesisHash *externalapi.DomainHash, genesisHash *externalapi.DomainHash,
deflationaryPhaseDaaScore uint64, deflationaryPhaseDaaScore uint64,
deflationaryPhaseBaseSubsidy uint64, deflationaryPhaseBaseSubsidy uint64,
hfDAAScore uint64,
dagTraversalManager model.DAGTraversalManager, dagTraversalManager model.DAGTraversalManager,
ghostdagDataStore model.GHOSTDAGDataStore, ghostdagDataStore model.GHOSTDAGDataStore,
@ -301,6 +304,7 @@ func New(
genesisHash: genesisHash, genesisHash: genesisHash,
deflationaryPhaseDaaScore: deflationaryPhaseDaaScore, deflationaryPhaseDaaScore: deflationaryPhaseDaaScore,
deflationaryPhaseBaseSubsidy: deflationaryPhaseBaseSubsidy, deflationaryPhaseBaseSubsidy: deflationaryPhaseBaseSubsidy,
hfDAAScore: hfDAAScore,
dagTraversalManager: dagTraversalManager, dagTraversalManager: dagTraversalManager,
ghostdagDataStore: ghostdagDataStore, ghostdagDataStore: ghostdagDataStore,

View File

@ -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)
}
}
})
}

View File

@ -21,6 +21,7 @@ func TestCalcDeflationaryPeriodBlockSubsidy(t *testing.T) {
&externalapi.DomainHash{}, &externalapi.DomainHash{},
deflationaryPhaseDaaScore, deflationaryPhaseDaaScore,
deflationaryPhaseBaseSubsidy, deflationaryPhaseBaseSubsidy,
0,
nil, nil,
nil, nil,
nil, nil,
@ -96,6 +97,7 @@ func TestBuildSubsidyTable(t *testing.T) {
&externalapi.DomainHash{}, &externalapi.DomainHash{},
0, 0,
deflationaryPhaseBaseSubsidy, deflationaryPhaseBaseSubsidy,
0,
nil, nil,
nil, nil,
nil, nil,

View File

@ -28,7 +28,7 @@ func (c *coinbaseManager) serializeCoinbasePayload(blueScore uint64,
binary.LittleEndian.PutUint64(payload[:uint64Len], blueScore) binary.LittleEndian.PutUint64(payload[:uint64Len], blueScore)
binary.LittleEndian.PutUint64(payload[uint64Len:], subsidy) 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)) payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script))
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script) copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script)
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData) copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData)
@ -52,7 +52,7 @@ func ModifyCoinbasePayload(payload []byte, coinbaseData *externalapi.DomainCoinb
payload = newPayload 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)) payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script))
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script) copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script)
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData) 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). // 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) { blueScore uint64, coinbaseData *externalapi.DomainCoinbaseData, subsidy uint64, err error) {
minLength := uint64Len + lengthOfSubsidy + lengthOfVersionScriptPubKey + lengthOfScriptPubKeyLength minLength := uint64Len + lengthOfSubsidy + lengthOfVersionScriptPubKey + lengthOfScriptPubKeyLength
@ -74,6 +74,10 @@ func (c *coinbaseManager) ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *ext
subsidy = binary.LittleEndian.Uint64(coinbaseTx.Payload[uint64Len:]) subsidy = binary.LittleEndian.Uint64(coinbaseTx.Payload[uint64Len:])
scriptPubKeyVersion := uint16(coinbaseTx.Payload[uint64Len+lengthOfSubsidy]) 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] scriptPubKeyScriptLength := coinbaseTx.Payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey]
if scriptPubKeyScriptLength > c.coinbasePayloadScriptPublicKeyMaxLength { if scriptPubKeyScriptLength > c.coinbasePayloadScriptPublicKeyMaxLength {

View File

@ -10,6 +10,7 @@ type consensusStateManager struct {
maxBlockParents externalapi.KType maxBlockParents externalapi.KType
mergeSetSizeLimit uint64 mergeSetSizeLimit uint64
genesisHash *externalapi.DomainHash genesisHash *externalapi.DomainHash
hfDAAScore uint64
databaseContext model.DBManager databaseContext model.DBManager
ghostdagManager model.GHOSTDAGManager ghostdagManager model.GHOSTDAGManager
@ -44,6 +45,7 @@ func New(
maxBlockParents externalapi.KType, maxBlockParents externalapi.KType,
mergeSetSizeLimit uint64, mergeSetSizeLimit uint64,
genesisHash *externalapi.DomainHash, genesisHash *externalapi.DomainHash,
hfDAAScore uint64,
ghostdagManager model.GHOSTDAGManager, ghostdagManager model.GHOSTDAGManager,
dagTopologyManager model.DAGTopologyManager, dagTopologyManager model.DAGTopologyManager,
@ -72,7 +74,9 @@ func New(
maxBlockParents: maxBlockParents, maxBlockParents: maxBlockParents,
mergeSetSizeLimit: mergeSetSizeLimit, mergeSetSizeLimit: mergeSetSizeLimit,
genesisHash: genesisHash, genesisHash: genesisHash,
databaseContext: databaseContext, hfDAAScore: hfDAAScore,
databaseContext: databaseContext,
ghostdagManager: ghostdagManager, ghostdagManager: ghostdagManager,
dagTopologyManager: dagTopologyManager, dagTopologyManager: dagTopologyManager,

View File

@ -39,7 +39,7 @@ func (csm *consensusStateManager) verifyUTXO(stagingArea *model.StagingArea, blo
coinbaseTransaction := block.Transactions[0] coinbaseTransaction := block.Transactions[0]
log.Debugf("Validating coinbase transaction %s for block %s", log.Debugf("Validating coinbase transaction %s for block %s",
consensushashing.TransactionID(coinbaseTransaction), blockHash) consensushashing.TransactionID(coinbaseTransaction), blockHash)
err = csm.validateCoinbaseTransaction(stagingArea, blockHash, coinbaseTransaction) err = csm.validateCoinbaseTransaction(stagingArea, blockHash, coinbaseTransaction, block.Header.DAAScore() >= csm.hfDAAScore)
if err != nil { if err != nil {
return err return err
} }
@ -153,14 +153,14 @@ func calculateAcceptedIDMerkleRoot(multiblockAcceptanceData externalapi.Acceptan
} }
func (csm *consensusStateManager) validateCoinbaseTransaction(stagingArea *model.StagingArea, 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) log.Tracef("validateCoinbaseTransaction start for block %s", blockHash)
defer log.Tracef("validateCoinbaseTransaction end 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", log.Tracef("Extracting coinbase data for coinbase transaction %s in block %s",
consensushashing.TransactionID(coinbaseTransaction), blockHash) consensushashing.TransactionID(coinbaseTransaction), blockHash)
_, coinbaseData, _, err := csm.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTransaction) _, coinbaseData, _, err := csm.coinbaseManager.ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTransaction, postHF)
if err != nil { if err != nil {
return err return err
} }
@ -172,8 +172,8 @@ func (csm *consensusStateManager) validateCoinbaseTransaction(stagingArea *model
return err return err
} }
coinbaseTransactionHash := consensushashing.TransactionHash(coinbaseTransaction) coinbaseTransactionHash := consensushashing.TransactionHash(coinbaseTransaction, true)
expectedCoinbaseTransactionHash := consensushashing.TransactionHash(expectedCoinbaseTransaction) expectedCoinbaseTransactionHash := consensushashing.TransactionHash(expectedCoinbaseTransaction, true)
log.Tracef("given coinbase hash: %s, expected coinbase hash: %s", log.Tracef("given coinbase hash: %s, expected coinbase hash: %s",
coinbaseTransactionHash, expectedCoinbaseTransactionHash) coinbaseTransactionHash, expectedCoinbaseTransactionHash)

View File

@ -5,9 +5,9 @@ import (
) )
// PopulateMass calculates and populates the mass of the given transaction // 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 { if transaction.Mass != 0 {
return return
} }
transaction.Mass = v.txMassCalculator.CalculateTransactionMass(transaction) transaction.Mass = v.txMassCalculator.CalculateTransactionMass(transaction, daaScore >= v.hfDAAScore)
} }

View File

@ -90,7 +90,12 @@ func (v *transactionValidator) ValidateTransactionInContextAndPopulateFee(stagin
return err 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 { if err != nil {
return err return err
} }
@ -341,7 +346,7 @@ func (v *transactionValidator) sequenceLockActive(sequenceLock *sequenceLock, bl
return true 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 { for i, input := range tx.Inputs {
utxoEntry := input.UTXOEntry utxoEntry := input.UTXOEntry
@ -350,10 +355,20 @@ func (v *transactionValidator) validateTransactionSigOpCounts(tx *externalapi.Do
sigScript := input.SignatureScript sigScript := input.SignatureScript
isP2SH := txscript.IsPayToScriptHash(utxoEntry.ScriptPublicKey()) isP2SH := txscript.IsPayToScriptHash(utxoEntry.ScriptPublicKey())
sigOpCount := txscript.GetPreciseSigOpCount(sigScript, utxoEntry.ScriptPublicKey(), isP2SH) sigOpCount := txscript.GetPreciseSigOpCount(sigScript, utxoEntry.ScriptPublicKey(), isP2SH)
if sigOpCount != int(input.SigOpCount) {
return errors.Wrapf(ruleerrors.ErrWrongSigOpCount, if postHF {
"input %d specifies SigOpCount %d while actual SigOpCount is %d", if sigOpCount != int(input.SigOpCount) {
i, input.SigOpCount, 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 return nil

View File

@ -23,7 +23,7 @@ func (v *transactionValidator) ValidateTransactionInIsolation(tx *externalapi.Do
if err != nil { if err != nil {
return err return err
} }
err = v.checkCoinbaseLength(tx) err = v.checkCoinbaseInIsolation(tx, povDAAScore >= v.hfDAAScore)
if err != nil { if err != nil {
return err return err
} }
@ -114,7 +114,7 @@ func (v *transactionValidator) checkDuplicateTransactionInputs(tx *externalapi.D
return nil return nil
} }
func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransaction) error { func (v *transactionValidator) checkCoinbaseInIsolation(tx *externalapi.DomainTransaction, postHF bool) error {
if !transactionhelper.IsCoinBase(tx) { if !transactionhelper.IsCoinBase(tx) {
return nil return nil
} }
@ -127,6 +127,24 @@ func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransac
payloadLen, v.maxCoinbasePayloadLength) 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 return nil
} }

View File

@ -22,6 +22,7 @@ type txSubnetworkData struct {
func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) { func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
cfg := *consensusConfig cfg := *consensusConfig
cfg.HFDAAScore = 1000
factory := consensus.NewFactory() factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(&cfg, "TestValidateTransactionInIsolationAndPopulateMass") tc, teardown, err := factory.NewTestConsensus(&cfg, "TestValidateTransactionInIsolationAndPopulateMass")
@ -69,7 +70,7 @@ func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) {
nil, nil,
func(tx *externalapi.DomainTransaction) { tx.Inputs[1].PreviousOutpoint.Index = 0 }, func(tx *externalapi.DomainTransaction) { tx.Inputs[1].PreviousOutpoint.Index = 0 },
ruleerrors.ErrDuplicateTxInputs, 0}, ruleerrors.ErrDuplicateTxInputs, 0},
{"1 input coinbase", {"1 input coinbase - pre HF",
1, 1,
1, 1,
1, 1,
@ -77,6 +78,14 @@ func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) {
&txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, nil}, &txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, nil},
nil, nil,
nil, 0}, 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", {"no inputs coinbase",
0, 0,
1, 1,

View File

@ -2,6 +2,7 @@ package transactionvalidator
import ( import (
"github.com/kaspanet/kaspad/domain/consensus/model" "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/domain/consensus/utils/txscript"
"github.com/kaspanet/kaspad/util/txmass" "github.com/kaspanet/kaspad/util/txmass"
) )
@ -11,22 +12,28 @@ const sigCacheSize = 10_000
// transactionValidator exposes a set of validation classes, after which // transactionValidator exposes a set of validation classes, after which
// it's possible to determine whether either a transaction is valid // it's possible to determine whether either a transaction is valid
type transactionValidator struct { type transactionValidator struct {
blockCoinbaseMaturity uint64 blockCoinbaseMaturity uint64
databaseContext model.DBReader databaseContext model.DBReader
pastMedianTimeManager model.PastMedianTimeManager pastMedianTimeManager model.PastMedianTimeManager
ghostdagDataStore model.GHOSTDAGDataStore ghostdagDataStore model.GHOSTDAGDataStore
daaBlocksStore model.DAABlocksStore daaBlocksStore model.DAABlocksStore
enableNonNativeSubnetworks bool enableNonNativeSubnetworks bool
maxCoinbasePayloadLength uint64 maxCoinbasePayloadLength uint64
sigCache *txscript.SigCache hfDAAScore uint64
sigCacheECDSA *txscript.SigCacheECDSA ghostdagK externalapi.KType
txMassCalculator *txmass.Calculator coinbasePayloadScriptPublicKeyMaxLength uint8
sigCache *txscript.SigCache
sigCacheECDSA *txscript.SigCacheECDSA
txMassCalculator *txmass.Calculator
} }
// New instantiates a new TransactionValidator // New instantiates a new TransactionValidator
func New(blockCoinbaseMaturity uint64, func New(blockCoinbaseMaturity uint64,
enableNonNativeSubnetworks bool, enableNonNativeSubnetworks bool,
maxCoinbasePayloadLength uint64, maxCoinbasePayloadLength uint64,
hfDAAScore uint64,
ghostdagK externalapi.KType,
coinbasePayloadScriptPublicKeyMaxLength uint8,
databaseContext model.DBReader, databaseContext model.DBReader,
pastMedianTimeManager model.PastMedianTimeManager, pastMedianTimeManager model.PastMedianTimeManager,
ghostdagDataStore model.GHOSTDAGDataStore, ghostdagDataStore model.GHOSTDAGDataStore,
@ -34,15 +41,18 @@ func New(blockCoinbaseMaturity uint64,
txMassCalculator *txmass.Calculator) model.TransactionValidator { txMassCalculator *txmass.Calculator) model.TransactionValidator {
return &transactionValidator{ return &transactionValidator{
blockCoinbaseMaturity: blockCoinbaseMaturity, blockCoinbaseMaturity: blockCoinbaseMaturity,
enableNonNativeSubnetworks: enableNonNativeSubnetworks, enableNonNativeSubnetworks: enableNonNativeSubnetworks,
maxCoinbasePayloadLength: maxCoinbasePayloadLength, maxCoinbasePayloadLength: maxCoinbasePayloadLength,
databaseContext: databaseContext, hfDAAScore: hfDAAScore,
pastMedianTimeManager: pastMedianTimeManager, ghostdagK: ghostdagK,
ghostdagDataStore: ghostdagDataStore, coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength,
daaBlocksStore: daaBlocksStore, databaseContext: databaseContext,
sigCache: txscript.NewSigCache(sigCacheSize), pastMedianTimeManager: pastMedianTimeManager,
sigCacheECDSA: txscript.NewSigCacheECDSA(sigCacheSize), ghostdagDataStore: ghostdagDataStore,
txMassCalculator: txMassCalculator, daaBlocksStore: daaBlocksStore,
sigCache: txscript.NewSigCache(sigCacheSize),
sigCacheECDSA: txscript.NewSigCacheECDSA(sigCacheSize),
txMassCalculator: txMassCalculator,
} }
} }

View File

@ -109,18 +109,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) {
0), 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{ txOutput := externalapi.DomainTransactionOutput{
Value: 100000000, // 1 KAS Value: 100000000, // 1 KAS
ScriptPublicKey: scriptPublicKey, ScriptPublicKey: scriptPublicKey,
@ -193,13 +181,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) {
SubnetworkID: subnetworks.SubnetworkIDRegistry, SubnetworkID: subnetworks.SubnetworkIDRegistry,
Gas: 0, Gas: 0,
LockTime: 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() stagingArea := model.NewStagingArea()
@ -266,13 +247,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) {
isValid: false, isValid: false,
expectedError: ruleerrors.ErrScriptValidation, 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 { for _, test := range tests {

View File

@ -241,6 +241,10 @@ var (
ErrPruningProofEmpty = newRuleError("ErrPruningProofEmpty") ErrPruningProofEmpty = newRuleError("ErrPruningProofEmpty")
ErrWrongCoinbaseSubsidy = newRuleError("ErrWrongCoinbaseSubsidy") ErrWrongCoinbaseSubsidy = newRuleError("ErrWrongCoinbaseSubsidy")
ErrWrongBlockVersion = newRuleError("ErrWrongBlockVersion") 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 // RuleError identifies a rule violation. It is used to indicate that

View File

@ -23,11 +23,11 @@ const (
) )
// TransactionHash returns the transaction hash. // 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 // Encode the header and hash everything prior to the number of
// transactions. // transactions.
writer := hashes.NewTransactionHashWriter() writer := hashes.NewTransactionHashWriter()
err := serializeTransaction(writer, tx, txEncodingFull) err := serializeTransaction(writer, tx, txEncodingFull, postHF)
if err != nil { if err != nil {
// It seems like this could only happen if the writer returned an error. // 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) // 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 encodingFlags = txEncodingExcludeSignatureScript
} }
writer := hashes.NewTransactionIDWriter() writer := hashes.NewTransactionIDWriter()
err := serializeTransaction(writer, tx, encodingFlags) err := serializeTransaction(writer, tx, encodingFlags, true)
if err != nil { if err != nil {
// this writer never return errors (no allocations or possible failures) so errors can only come from validity checks, // 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. // and we assume we never construct malformed transactions.
@ -74,7 +74,7 @@ func TransactionIDs(txs []*externalapi.DomainTransaction) []*externalapi.DomainT
return txIDs 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) err := binaryserializer.PutUint16(w, tx.Version)
if err != nil { if err != nil {
return err return err
@ -87,7 +87,7 @@ func serializeTransaction(w io.Writer, tx *externalapi.DomainTransaction, encodi
} }
for _, ti := range tx.Inputs { for _, ti := range tx.Inputs {
err = writeTransactionInput(w, ti, encodingFlags) err = writeTransactionInput(w, ti, encodingFlags, postHF)
if err != nil { if err != nil {
return err 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 // writeTransactionInput encodes ti to the kaspa protocol encoding for a transaction
// input to w. // 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) err := writeOutpoint(w, &ti.PreviousOutpoint)
if err != nil { if err != nil {
return err return err
@ -139,11 +139,21 @@ func writeTransactionInput(w io.Writer, ti *externalapi.DomainTransactionInput,
if encodingFlags&txEncodingExcludeSignatureScript != txEncodingExcludeSignatureScript { if encodingFlags&txEncodingExcludeSignatureScript != txEncodingExcludeSignatureScript {
err = writeVarBytes(w, ti.SignatureScript) err = writeVarBytes(w, ti.SignatureScript)
if err != nil {
return err
}
if postHF {
_, err = w.Write([]byte{ti.SigOpCount})
if err != nil {
return err
}
}
} else { } else {
err = writeVarBytes(w, []byte{}) err = writeVarBytes(w, []byte{})
} if err != nil {
if err != nil { return err
return err }
} }
return binaryserializer.PutUint64(w, ti.Sequence) return binaryserializer.PutUint64(w, ti.Sequence)

View File

@ -38,4 +38,7 @@ const (
// UnacceptedDAAScore is used to for UTXOEntries that were created by transactions in the mempool, or otherwise // UnacceptedDAAScore is used to for UTXOEntries that were created by transactions in the mempool, or otherwise
// not-yet-accepted transactions. // not-yet-accepted transactions.
UnacceptedDAAScore = math.MaxUint64 UnacceptedDAAScore = math.MaxUint64
// MaxBlockInputsPreHF is the maximum number of inputs a block can hold before the HF
MaxBlockInputsPreHF = 900
) )

View File

@ -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. // CalculateHashMerkleRoot calculates the merkle root of a tree consisted of the given transaction hashes.
// See `merkleRoot` for more info. // 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)) txHashes := make([]*externalapi.DomainHash, len(transactions))
for i, tx := range transactions { for i, tx := range transactions {
txHashes[i] = consensushashing.TransactionHash(tx) txHashes[i] = consensushashing.TransactionHash(tx, postHF)
} }
return merkleRoot(txHashes) return merkleRoot(txHashes)
} }

View File

@ -188,6 +188,8 @@ type Params struct {
MaxBlockLevel int MaxBlockLevel int
MergeDepth uint64 MergeDepth uint64
HFDAAScore uint64
} }
// NormalizeRPCServerAddress returns addr with the current network default // 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. // This means that any block that has a level lower or equal to genesis will be level 0.
MaxBlockLevel: 225, MaxBlockLevel: 225,
MergeDepth: defaultMergeDepth, MergeDepth: defaultMergeDepth,
HFDAAScore: 27905000,
} }
// TestnetParams defines the network parameters for the test Kaspa network. // TestnetParams defines the network parameters for the test Kaspa network.
@ -350,6 +354,7 @@ var TestnetParams = Params{
MaxBlockLevel: 250, MaxBlockLevel: 250,
MergeDepth: defaultMergeDepth, MergeDepth: defaultMergeDepth,
HFDAAScore: 14106400,
} }
// SimnetParams defines the network parameters for the simulation test Kaspa // SimnetParams defines the network parameters for the simulation test Kaspa

View File

@ -37,17 +37,19 @@ type blockTemplateBuilder struct {
policy policy policy policy
coinbasePayloadScriptPublicKeyMaxLength uint8 coinbasePayloadScriptPublicKeyMaxLength uint8
hfDAAScore uint64
} }
// New creates a new blockTemplateBuilder // New creates a new blockTemplateBuilder
func New(consensusReference consensusreference.ConsensusReference, mempool miningmanagerapi.Mempool, func New(consensusReference consensusreference.ConsensusReference, mempool miningmanagerapi.Mempool,
blockMaxMass uint64, coinbasePayloadScriptPublicKeyMaxLength uint8) miningmanagerapi.BlockTemplateBuilder { blockMaxMass uint64, coinbasePayloadScriptPublicKeyMaxLength uint8, hfDAAScore uint64) miningmanagerapi.BlockTemplateBuilder {
return &blockTemplateBuilder{ return &blockTemplateBuilder{
consensusReference: consensusReference, consensusReference: consensusReference,
mempool: mempool, mempool: mempool,
policy: policy{BlockMaxMass: blockMaxMass}, policy: policy{BlockMaxMass: blockMaxMass},
coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength, coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength,
hfDAAScore: hfDAAScore,
} }
} }
@ -190,7 +192,7 @@ func (btb *blockTemplateBuilder) ModifyBlockTemplate(newCoinbaseData *consensuse
// Update the hash merkle root according to the modified transactions // Update the hash merkle root according to the modified transactions
mutableHeader := blockTemplateToModify.Block.Header.ToMutable() 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 // 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() newTimestamp := mstime.Now().UnixMilliseconds()
if newTimestamp >= mutableHeader.TimeInMilliseconds() { if newTimestamp >= mutableHeader.TimeInMilliseconds() {

View File

@ -1,6 +1,7 @@
package blocktemplatebuilder package blocktemplatebuilder
import ( import (
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"math" "math"
"math/rand" "math/rand"
"sort" "sort"
@ -74,6 +75,8 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
usedP += candidateTx.p usedP += candidateTx.p
} }
totalInputs := 0
selectedTxs := make([]*candidateTx, 0) selectedTxs := make([]*candidateTx, 0)
for len(candidateTxs)-usedCount > 0 { for len(candidateTxs)-usedCount > 0 {
// Rebalance the candidates if it's required // Rebalance the candidates if it's required
@ -99,6 +102,10 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
} }
tx := selectedTx.DomainTransaction tx := selectedTx.DomainTransaction
if totalInputs+len(tx.Inputs) > maxBlockInputsPreHF(btb.hfDAAScore) {
continue
}
// Enforce maximum transaction mass per block. Also check // Enforce maximum transaction mass per block. Also check
// for overflow. // for overflow.
if txsForBlockTemplate.totalMass+selectedTx.Mass < txsForBlockTemplate.totalMass || 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 // save the masses, fees, and signature operation counts to the
// result. // result.
selectedTxs = append(selectedTxs, selectedTx) selectedTxs = append(selectedTxs, selectedTx)
totalInputs += len(selectedTx.Inputs)
txsForBlockTemplate.totalMass += selectedTx.Mass txsForBlockTemplate.totalMass += selectedTx.Mass
txsForBlockTemplate.totalFees += selectedTx.Fee txsForBlockTemplate.totalFees += selectedTx.Fee
@ -150,6 +158,10 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
consensushashing.TransactionID(tx), selectedTx.Fee*1e6/selectedTx.Mass) consensushashing.TransactionID(tx), selectedTx.Fee*1e6/selectedTx.Mass)
markCandidateTxForDeletion(selectedTx) markCandidateTxForDeletion(selectedTx)
if totalInputs == maxBlockInputsPreHF(btb.hfDAAScore) {
break
}
} }
sort.Slice(selectedTxs, func(i, j int) bool { sort.Slice(selectedTxs, func(i, j int) bool {
@ -163,6 +175,10 @@ func (btb *blockTemplateBuilder) selectTransactions(candidateTxs []*candidateTx)
return txsForBlockTemplate return txsForBlockTemplate
} }
func maxBlockInputsPreHF(hfDAAScore uint64) int {
return constants.MaxBlockInputsPreHF
}
func rebalanceCandidates(oldCandidateTxs []*candidateTx, isFirstRun bool) ( func rebalanceCandidates(oldCandidateTxs []*candidateTx, isFirstRun bool) (
candidateTxs []*candidateTx, totalP float64) { candidateTxs []*candidateTx, totalP float64) {

View File

@ -21,7 +21,7 @@ func (f *factory) NewMiningManager(consensusReference consensusreference.Consens
mempoolConfig *mempoolpkg.Config) MiningManager { mempoolConfig *mempoolpkg.Config) MiningManager {
mempool := mempoolpkg.New(mempoolConfig, consensusReference) 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{ return &miningManager{
consensusReference: consensusReference, consensusReference: consensusReference,

View File

@ -35,7 +35,7 @@ func (x *KaspadMessage_GetConnectedPeerInfoResponse) fromAppMessage(message *app
TimeOffset: info.TimeOffset, TimeOffset: info.TimeOffset,
UserAgent: info.UserAgent, UserAgent: info.UserAgent,
AdvertisedProtocolVersion: info.AdvertisedProtocolVersion, AdvertisedProtocolVersion: info.AdvertisedProtocolVersion,
TimeConnected: info.TimeOffset, TimeConnected: info.TimeConnected,
IsIbdPeer: info.IsIBDPeer, IsIbdPeer: info.IsIBDPeer,
} }
} }

View File

@ -31,7 +31,7 @@ func (c *Calculator) MassPerScriptPubKeyByte() uint64 { return c.massPerScriptPu
func (c *Calculator) MassPerSigOp() uint64 { return c.massPerSigOp } func (c *Calculator) MassPerSigOp() uint64 { return c.massPerSigOp }
// CalculateTransactionMass calculates the mass of the given transaction // 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) { if transactionhelper.IsCoinBase(transaction) {
return 0 return 0
} }
@ -49,11 +49,14 @@ func (c *Calculator) CalculateTransactionMass(transaction *externalapi.DomainTra
massForScriptPubKey := totalScriptPubKeySize * c.massPerScriptPubKeyByte massForScriptPubKey := totalScriptPubKeySize * c.massPerScriptPubKeyByte
// calculate mass for SigOps // calculate mass for SigOps
totalSigOpCount := uint64(0) massForSigOps := uint64(0)
for _, input := range transaction.Inputs { if postHF {
totalSigOpCount += uint64(input.SigOpCount) 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 // Sum all components of mass
return massForSize + massForScriptPubKey + massForSigOps return massForSize + massForScriptPubKey + massForSigOps

View File

@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs
const ( const (
appMajor uint = 0 appMajor uint = 0
appMinor uint = 12 appMinor uint = 12
appPatch uint = 6 appPatch uint = 7
) )
// appBuild is defined as a variable so it can be overridden during the build // appBuild is defined as a variable so it can be overridden during the build