mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
Validate locktime when admitted into mempool and when building a block. (#1794)
* Validate locktime when admitted into mempool and when build a block. Also, fix isFinalized to use DAAscore instead of blue score. * Change the function name:ValidateTransactionInContextIgnoringUTXO Co-authored-by: tal <tal@daglabs.com>
This commit is contained in:
parent
28ac77b202
commit
8022e4cbea
@ -92,6 +92,11 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, transaction, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.transactionValidator.ValidateTransactionInContextAndPopulateMassAndFee(
|
||||
stagingArea, transaction, model.VirtualBlockHash, virtualSelectedParentMedianTime)
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
// it's possible to determine whether a transaction is valid
|
||||
type TransactionValidator interface {
|
||||
ValidateTransactionInIsolation(transaction *externalapi.DomainTransaction) error
|
||||
ValidateTransactionInContextIgnoringUTXO(stagingArea *StagingArea, tx *externalapi.DomainTransaction,
|
||||
povBlockHash *externalapi.DomainHash) error
|
||||
ValidateTransactionInContextAndPopulateMassAndFee(stagingArea *StagingArea,
|
||||
tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash, selectedParentMedianTime int64) error
|
||||
}
|
||||
|
@ -145,6 +145,11 @@ func (bb *blockBuilder) validateTransaction(
|
||||
return err
|
||||
}
|
||||
|
||||
err = bb.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, transaction, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
virtualSelectedParentMedianTime, err := bb.pastMedianTimeManager.PastMedianTime(stagingArea, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1,13 +1,9 @@
|
||||
package blockvalidator
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"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/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -23,7 +19,7 @@ func (v *blockValidator) ValidateBodyInContext(stagingArea *model.StagingArea, b
|
||||
return err
|
||||
}
|
||||
|
||||
err = v.checkBlockTransactionsFinalized(stagingArea, blockHash)
|
||||
err = v.checkBlockTransactions(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -117,7 +113,7 @@ func (v *blockValidator) checkParentBlockBodiesExist(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *blockValidator) checkBlockTransactionsFinalized(
|
||||
func (v *blockValidator) checkBlockTransactions(
|
||||
stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) error {
|
||||
|
||||
block, err := v.blockStore.Block(v.databaseContext, stagingArea, blockHash)
|
||||
@ -125,57 +121,12 @@ func (v *blockValidator) checkBlockTransactionsFinalized(
|
||||
return err
|
||||
}
|
||||
|
||||
ghostdagData, err := v.ghostdagDataStore.Get(v.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockTime, err := v.pastMedianTimeManager.PastMedianTime(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure all transactions in the block are finalized.
|
||||
for _, tx := range block.Transactions {
|
||||
if !v.isFinalizedTransaction(tx, ghostdagData.BlueScore(), blockTime) {
|
||||
txID := consensushashing.TransactionID(tx)
|
||||
return errors.Wrapf(ruleerrors.ErrUnfinalizedTx, "block contains unfinalized "+
|
||||
"transaction %s", txID)
|
||||
if err = v.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, tx, blockHash); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsFinalizedTransaction determines whether or not a transaction is finalized.
|
||||
func (v *blockValidator) isFinalizedTransaction(tx *externalapi.DomainTransaction, blockBlueScore uint64, blockTime int64) bool {
|
||||
// Lock time of zero means the transaction is finalized.
|
||||
lockTime := tx.LockTime
|
||||
if lockTime == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// The lock time field of a transaction is either a block blue score at
|
||||
// which the transaction is finalized or a timestamp depending on if the
|
||||
// value is before the constants.LockTimeThreshold. When it is under the
|
||||
// threshold it is a block blue score.
|
||||
blockTimeOrBlueScore := uint64(0)
|
||||
if lockTime < constants.LockTimeThreshold {
|
||||
blockTimeOrBlueScore = blockBlueScore
|
||||
} else {
|
||||
blockTimeOrBlueScore = uint64(blockTime)
|
||||
}
|
||||
if lockTime < blockTimeOrBlueScore {
|
||||
return true
|
||||
}
|
||||
|
||||
// At this point, the transaction's lock time hasn't occurred yet, but
|
||||
// the transaction might still be finalized if the sequence number
|
||||
// for all transaction inputs is maxed out.
|
||||
for _, input := range tx.Inputs {
|
||||
if input.Sequence != math.MaxUint64 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -187,9 +187,10 @@ func TestIsFinalizedTransaction(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error Inserting block: %+v", err)
|
||||
}
|
||||
blockGhostDAG, err := tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), stagingArea, consensushashing.BlockHash(block))
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
blockDAAScore, err := tc.DAABlocksStore().DAAScore(tc.DatabaseContext(), stagingArea, blockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting GhostDAG Data: %+v", err)
|
||||
t.Fatalf("Error getting block DAA score : %+v", err)
|
||||
}
|
||||
blockParents := block.Header.ParentHashes()
|
||||
parentToSpend, err := tc.GetBlock(blockParents[0])
|
||||
@ -212,10 +213,10 @@ func TestIsFinalizedTransaction(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the same blueScore or higher fails, but lower passes.
|
||||
checkForLockTimeAndSequence(blockGhostDAG.BlueScore()+1, 0, false)
|
||||
checkForLockTimeAndSequence(blockGhostDAG.BlueScore(), 0, false)
|
||||
checkForLockTimeAndSequence(blockGhostDAG.BlueScore()-1, 0, true)
|
||||
// Check that the same DAAScore or higher fails, but lower passes.
|
||||
checkForLockTimeAndSequence(blockDAAScore+1, 0, false)
|
||||
checkForLockTimeAndSequence(blockDAAScore, 0, false)
|
||||
checkForLockTimeAndSequence(blockDAAScore-1, 0, true)
|
||||
|
||||
pastMedianTime, err := tc.PastMedianTimeManager().PastMedianTime(stagingArea, consensushashing.BlockHash(block))
|
||||
if err != nil {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package transactionvalidator
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
@ -11,6 +13,58 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// IsFinalizedTransaction determines whether or not a transaction is finalized.
|
||||
func (v *transactionValidator) IsFinalizedTransaction(tx *externalapi.DomainTransaction, blockDAAScore uint64, blockTime int64) bool {
|
||||
// Lock time of zero means the transaction is finalized.
|
||||
lockTime := tx.LockTime
|
||||
if lockTime == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// The lock time field of a transaction is either a block DAA score at
|
||||
// which the transaction is finalized or a timestamp depending on if the
|
||||
// value is before the constants.LockTimeThreshold. When it is under the
|
||||
// threshold it is a DAA score.
|
||||
blockTimeOrBlueScore := uint64(0)
|
||||
if lockTime < constants.LockTimeThreshold {
|
||||
blockTimeOrBlueScore = blockDAAScore
|
||||
} else {
|
||||
blockTimeOrBlueScore = uint64(blockTime)
|
||||
}
|
||||
if lockTime < blockTimeOrBlueScore {
|
||||
return true
|
||||
}
|
||||
|
||||
// At this point, the transaction's lock time hasn't occurred yet, but
|
||||
// the transaction might still be finalized if the sequence number
|
||||
// for all transaction inputs is maxed out.
|
||||
for _, input := range tx.Inputs {
|
||||
if input.Sequence != math.MaxUint64 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ValidateTransactionInContextIgnoringUTXO validates the transaction with consensus context but ignoring UTXO
|
||||
func (v *transactionValidator) ValidateTransactionInContextIgnoringUTXO(stagingArea *model.StagingArea, tx *externalapi.DomainTransaction,
|
||||
povBlockHash *externalapi.DomainHash) error {
|
||||
|
||||
povBlockDAAScore, err := v.daaBlocksStore.DAAScore(v.databaseContext, stagingArea, povBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
povBlockPastMedianTime, err := v.pastMedianTimeManager.PastMedianTime(stagingArea, povBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isFinalized := v.IsFinalizedTransaction(tx, povBlockDAAScore, povBlockPastMedianTime); !isFinalized {
|
||||
return errors.Wrapf(ruleerrors.ErrUnfinalizedTx, "unfinalized transaction %v", tx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateTransactionInContextAndPopulateMassAndFee validates the transaction against its referenced UTXO, and
|
||||
// populates its mass and fee fields.
|
||||
//
|
||||
|
@ -2,6 +2,8 @@ package consensus_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@ -11,7 +13,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestCheckLockTimeVerifyConditionedByDAAScore verifies that an output locked by the CLTV script is spendable only after
|
||||
|
Loading…
x
Reference in New Issue
Block a user