diff --git a/domain/consensus/consensus.go b/domain/consensus/consensus.go index 60d48e2f4..9dd1cd0ae 100644 --- a/domain/consensus/consensus.go +++ b/domain/consensus/consensus.go @@ -87,17 +87,12 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction return err } - virtualSelectedParentMedianTime, err := s.pastMedianTimeManager.PastMedianTime(stagingArea, model.VirtualBlockHash) - if err != nil { - return err - } - err = s.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, transaction, model.VirtualBlockHash) if err != nil { return err } return s.transactionValidator.ValidateTransactionInContextAndPopulateFee( - stagingArea, transaction, model.VirtualBlockHash, virtualSelectedParentMedianTime) + stagingArea, transaction, model.VirtualBlockHash) } func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) { diff --git a/domain/consensus/model/interface_processes_transactionvalidator.go b/domain/consensus/model/interface_processes_transactionvalidator.go index 7964945dd..679bf157e 100644 --- a/domain/consensus/model/interface_processes_transactionvalidator.go +++ b/domain/consensus/model/interface_processes_transactionvalidator.go @@ -11,6 +11,6 @@ type TransactionValidator interface { ValidateTransactionInContextIgnoringUTXO(stagingArea *StagingArea, tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash) error ValidateTransactionInContextAndPopulateFee(stagingArea *StagingArea, - tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash, selectedParentMedianTime int64) error + tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash) error PopulateMass(transaction *externalapi.DomainTransaction) } diff --git a/domain/consensus/processes/blockbuilder/block_builder.go b/domain/consensus/processes/blockbuilder/block_builder.go index 38cf02b3c..7848239d1 100644 --- a/domain/consensus/processes/blockbuilder/block_builder.go +++ b/domain/consensus/processes/blockbuilder/block_builder.go @@ -150,13 +150,7 @@ func (bb *blockBuilder) validateTransaction( return err } - virtualSelectedParentMedianTime, err := bb.pastMedianTimeManager.PastMedianTime(stagingArea, model.VirtualBlockHash) - if err != nil { - return err - } - - return bb.transactionValidator.ValidateTransactionInContextAndPopulateFee( - stagingArea, transaction, model.VirtualBlockHash, virtualSelectedParentMedianTime) + return bb.transactionValidator.ValidateTransactionInContextAndPopulateFee(stagingArea, transaction, model.VirtualBlockHash) } func (bb *blockBuilder) newBlockCoinbaseTransaction(stagingArea *model.StagingArea, diff --git a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go index d8bcb6226..e7fc4d33f 100644 --- a/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go +++ b/domain/consensus/processes/consensusstatemanager/calculate_past_utxo.go @@ -238,7 +238,7 @@ func (csm *consensusStateManager) maybeAcceptTransaction(stagingArea *model.Stag } else { log.Tracef("Validating transaction %s in block %s", transactionID, blockHash) err = csm.transactionValidator.ValidateTransactionInContextAndPopulateFee( - stagingArea, transaction, blockHash, selectedParentPastMedianTime) + stagingArea, transaction, blockHash) if err != nil { if !errors.As(err, &(ruleerrors.RuleError{})) { return false, 0, err diff --git a/domain/consensus/processes/consensusstatemanager/import_pruning_utxo_set.go b/domain/consensus/processes/consensusstatemanager/import_pruning_utxo_set.go index 0c4d18dcb..a7376cf46 100644 --- a/domain/consensus/processes/consensusstatemanager/import_pruning_utxo_set.go +++ b/domain/consensus/processes/consensusstatemanager/import_pruning_utxo_set.go @@ -119,7 +119,7 @@ func (csm *consensusStateManager) importPruningPoint( } log.Tracef("Validating transaction %s and populating it with mass and fee", transactionID) err = csm.transactionValidator.ValidateTransactionInContextAndPopulateFee( - stagingArea, transaction, newPruningPointHash, newPruningPointSelectedParentMedianTime) + stagingArea, transaction, newPruningPointHash) if err != nil { return err } diff --git a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go index b9672356f..561e1c493 100644 --- a/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go +++ b/domain/consensus/processes/consensusstatemanager/verify_and_build_utxo.go @@ -85,7 +85,7 @@ func (csm *consensusStateManager) validateBlockTransactionsAgainstPastUTXO(stagi log.Tracef("Validating transaction %s and populating it with fee", transactionID) err = csm.transactionValidator.ValidateTransactionInContextAndPopulateFee( - stagingArea, transaction, blockHash, selectedParentMedianTime) + stagingArea, transaction, blockHash) if err != nil { return err } diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_context.go b/domain/consensus/processes/transactionvalidator/transaction_in_context.go index de8bc5761..ba9ee2b6d 100644 --- a/domain/consensus/processes/transactionvalidator/transaction_in_context.go +++ b/domain/consensus/processes/transactionvalidator/transaction_in_context.go @@ -70,7 +70,7 @@ func (v *transactionValidator) ValidateTransactionInContextIgnoringUTXO(stagingA // // Note: if the function fails, there's no guarantee that the transaction fee field will remain unaffected. func (v *transactionValidator) ValidateTransactionInContextAndPopulateFee(stagingArea *model.StagingArea, - tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash, selectedParentMedianTime int64) error { + tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash) error { err := v.checkTransactionCoinbaseMaturity(stagingArea, povBlockHash, tx) if err != nil { @@ -89,7 +89,7 @@ func (v *transactionValidator) ValidateTransactionInContextAndPopulateFee(stagin tx.Fee = totalSompiIn - totalSompiOut - err = v.checkTransactionSequenceLock(stagingArea, povBlockHash, tx, selectedParentMedianTime) + err = v.checkTransactionSequenceLock(stagingArea, povBlockHash, tx) if err != nil { return err } @@ -204,7 +204,7 @@ func (v *transactionValidator) checkTransactionOutputAmounts(tx *externalapi.Dom } func (v *transactionValidator) checkTransactionSequenceLock(stagingArea *model.StagingArea, - povBlockHash *externalapi.DomainHash, tx *externalapi.DomainTransaction, medianTime int64) error { + povBlockHash *externalapi.DomainHash, tx *externalapi.DomainTransaction) error { // A transaction can only be included within a block // once the sequence locks of *all* its inputs are @@ -219,7 +219,7 @@ func (v *transactionValidator) checkTransactionSequenceLock(stagingArea *model.S return err } - if !v.sequenceLockActive(sequenceLock, daaScore, medianTime) { + if !v.sequenceLockActive(sequenceLock, daaScore) { return errors.Wrapf(ruleerrors.ErrUnfinalizedTx, "block contains "+ "transaction whose input sequence "+ "locks are not met") @@ -271,14 +271,13 @@ func (v *transactionValidator) validateTransactionScripts(tx *externalapi.Domain func (v *transactionValidator) calcTxSequenceLockFromReferencedUTXOEntries(stagingArea *model.StagingArea, povBlockHash *externalapi.DomainHash, tx *externalapi.DomainTransaction) (*sequenceLock, error) { - // A value of -1 for each relative lock type represents a relative time - // lock value that will allow a transaction to be included in a block - // at any given height or time. - sequenceLock := &sequenceLock{Milliseconds: -1, BlockDAAScore: -1} + // A value of -1 represents a relative timelock value that will allow a transaction to be + //included in a block at any given DAA score. + sequenceLock := &sequenceLock{BlockDAAScore: -1} // Sequence locks don't apply to coinbase transactions Therefore, we // return sequence lock values of -1 indicating that this transaction - // can be included within a block at any given height or time. + // can be included within a block at any given DAA score. if transactionhelper.IsCoinBase(tx) { return sequenceLock, nil } @@ -299,70 +298,19 @@ func (v *transactionValidator) calcTxSequenceLockFromReferencedUTXOEntries(stagi sequenceNum := input.Sequence relativeLock := int64(sequenceNum & constants.SequenceLockTimeMask) - switch { // Relative time locks are disabled for this input, so we can // skip any further calculation. - case sequenceNum&constants.SequenceLockTimeDisabled == constants.SequenceLockTimeDisabled: + if sequenceNum&constants.SequenceLockTimeDisabled == constants.SequenceLockTimeDisabled { continue - case sequenceNum&constants.SequenceLockTimeIsSeconds == constants.SequenceLockTimeIsSeconds: - // This input requires a relative time lock expressed - // in seconds before it can be spent. Therefore, we - // need to query for the block prior to the one in - // which this input was accepted within so we can - // compute the past median time for the block prior to - // the one which accepted this referenced output. - baseGHOSTDAGData, err := v.ghostdagDataStore.Get(v.databaseContext, stagingArea, povBlockHash) - if err != nil { - return nil, err - } - - baseHash := povBlockHash - - for { - selectedParentDAAScore, err := v.daaBlocksStore.DAAScore(v.databaseContext, stagingArea, baseHash) - if err != nil { - return nil, err - } - - if selectedParentDAAScore <= inputDAAScore { - break - } - - selectedParentGHOSTDAGData, err := v.ghostdagDataStore.Get( - v.databaseContext, stagingArea, baseGHOSTDAGData.SelectedParent()) - if err != nil { - return nil, err - } - - baseHash = baseGHOSTDAGData.SelectedParent() - baseGHOSTDAGData = selectedParentGHOSTDAGData - } - - medianTime, err := v.pastMedianTimeManager.PastMedianTime(stagingArea, baseHash) - if err != nil { - return nil, err - } - - // Time based relative time-locks have a time granularity of - // constants.SequenceLockTimeGranularity, so we shift left by this - // amount to convert to the proper relative time-lock. We also - // subtract one from the relative lock to maintain the original - // lockTime semantics. - timeLockMilliseconds := (relativeLock * constants.SequenceLockTimeGranularity) - 1 - timeLock := medianTime + timeLockMilliseconds - if timeLock > sequenceLock.Milliseconds { - sequenceLock.Milliseconds = timeLock - } - default: - // The relative lock-time for this input is expressed - // in blocks so we calculate the relative offset from - // the input's DAA score as its converted absolute - // lock-time. We subtract one from the relative lock in - // order to maintain the original lockTime semantics. - blockDAAScore := int64(inputDAAScore) + relativeLock - 1 - if blockDAAScore > sequenceLock.BlockDAAScore { - sequenceLock.BlockDAAScore = blockDAAScore - } + } + // The relative lock-time for this input is expressed + // in blocks so we calculate the relative offset from + // the input's DAA score as its converted absolute + // lock-time. We subtract one from the relative lock in + // order to maintain the original lockTime semantics. + blockDAAScore := int64(inputDAAScore) + relativeLock - 1 + if blockDAAScore > sequenceLock.BlockDAAScore { + sequenceLock.BlockDAAScore = blockDAAScore } } if len(missingOutpoints) > 0 { @@ -372,28 +320,24 @@ func (v *transactionValidator) calcTxSequenceLockFromReferencedUTXOEntries(stagi return sequenceLock, nil } -// sequenceLock represents the converted relative lock-time in seconds, and +// sequenceLock represents the converted relative lock-time in // absolute block-daa-score for a transaction input's relative lock-times. // According to sequenceLock, after the referenced input has been confirmed // within a block, a transaction spending that input can be included into a -// block either after 'seconds' (according to past median time), or once the -// 'BlockDAAScore' has been reached. +// block either after the 'BlockDAAScore' has been reached. type sequenceLock struct { - Milliseconds int64 BlockDAAScore int64 } // sequenceLockActive determines if a transaction's sequence locks have been // met, meaning that all the inputs of a given transaction have reached a -// DAA score or time sufficient for their relative lock-time maturity. -func (v *transactionValidator) sequenceLockActive(sequenceLock *sequenceLock, blockDAAScore uint64, - medianTimePast int64) bool { +// DAA score sufficient for their relative lock-time maturity. +func (v *transactionValidator) sequenceLockActive(sequenceLock *sequenceLock, blockDAAScore uint64) bool { - // If either the milliseconds, or DAA score relative-lock time has not yet + // If (DAA score) relative-lock time has not yet // reached, then the transaction is not yet mature according to its // sequence locks. - if sequenceLock.Milliseconds >= medianTimePast || - sequenceLock.BlockDAAScore >= int64(blockDAAScore) { + if sequenceLock.BlockDAAScore >= int64(blockDAAScore) { return false } diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_context_test.go b/domain/consensus/processes/transactionvalidator/transaction_in_context_test.go index 55177efb3..d22e9663a 100644 --- a/domain/consensus/processes/transactionvalidator/transaction_in_context_test.go +++ b/domain/consensus/processes/transactionvalidator/transaction_in_context_test.go @@ -1,7 +1,6 @@ package transactionvalidator import ( - "github.com/kaspanet/kaspad/util/mstime" "testing" ) @@ -11,32 +10,22 @@ func TestSequenceLocksActive(t *testing.T) { tests := []struct { seqLock sequenceLock blockDAAScore uint64 - mtp mstime.Time want bool }{ // Block based sequence lock with equal block DAA score. - {seqLock: sequenceLock{-1, 1000}, blockDAAScore: 1001, mtp: mstime.UnixMilliseconds(9), want: true}, - - // Time based sequence lock with mtp past the absolute time. - {seqLock: sequenceLock{30, -1}, blockDAAScore: 2, mtp: mstime.UnixMilliseconds(31), want: true}, + {seqLock: sequenceLock{1000}, blockDAAScore: 1001, want: true}, // Block based sequence lock with current DAA score below seq lock block DAA score. - {seqLock: sequenceLock{-1, 1000}, blockDAAScore: 90, mtp: mstime.UnixMilliseconds(9), want: false}, - - // Time based sequence lock with current time before lock time. - {seqLock: sequenceLock{30, -1}, blockDAAScore: 2, mtp: mstime.UnixMilliseconds(29), want: false}, + {seqLock: sequenceLock{1000}, blockDAAScore: 90, want: false}, // Block based sequence lock at the same DAA score, so shouldn't yet be active. - {seqLock: sequenceLock{-1, 1000}, blockDAAScore: 1000, mtp: mstime.UnixMilliseconds(9), want: false}, - - // Time based sequence lock with current time equal to lock time, so shouldn't yet be active. - {seqLock: sequenceLock{30, -1}, blockDAAScore: 2, mtp: mstime.UnixMilliseconds(30), want: false}, + {seqLock: sequenceLock{1000}, blockDAAScore: 1000, want: false}, } validator := transactionValidator{} for i, test := range tests { - got := validator.sequenceLockActive(&test.seqLock, test.blockDAAScore, test.mtp.UnixMilliseconds()) + got := validator.sequenceLockActive(&test.seqLock, test.blockDAAScore) if got != test.want { t.Fatalf("SequenceLockActive #%d got %v want %v", i, got, test.want) } diff --git a/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go b/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go index cf28af586..5fa7d02eb 100644 --- a/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go +++ b/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go @@ -19,28 +19,10 @@ import ( "github.com/pkg/errors" ) -type mocPastMedianTimeManager struct { - pastMedianTimeForTest int64 -} - -func (mdf *mocPastMedianTimeManager) InvalidateVirtualPastMedianTimeCache() { - // do nothing -} - -// PastMedianTime returns the past median time for the test. -func (mdf *mocPastMedianTimeManager) PastMedianTime(*model.StagingArea, *externalapi.DomainHash) (int64, error) { - return mdf.pastMedianTimeForTest, nil -} - func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { factory := consensus.NewFactory() - pastMedianManager := &mocPastMedianTimeManager{} - factory.SetTestPastMedianTimeManager(func(int, model.DBReader, model.DAGTraversalManager, model.BlockHeaderStore, - model.GHOSTDAGDataStore, *externalapi.DomainHash) model.PastMedianTimeManager { - return pastMedianManager - }) tc, tearDown, err := factory.NewTestConsensus(consensusConfig, "TestValidateTransactionInContextAndPopulateFee") if err != nil { @@ -48,7 +30,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { } defer tearDown(false) - pastMedianManager.pastMedianTimeForTest = 1 privateKey, err := secp256k1.GenerateSchnorrKeyPair() if err != nil { t.Fatalf("Failed to generate a private key: %v", err) @@ -83,7 +64,17 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { true, uint64(5)), } - immatureInput := externalapi.DomainTransactionInput{ + txInputWrongSignature := externalapi.DomainTransactionInput{ + PreviousOutpoint: prevOutPoint, + SignatureScript: []byte{}, + SigOpCount: 1, + UTXOEntry: utxo.NewUTXOEntry( + 100_000_000, // 1 KAS + scriptPublicKey, + true, + uint64(5)), + } + immatureCoinbaseInput := externalapi.DomainTransactionInput{ PreviousOutpoint: prevOutPoint, SignatureScript: []byte{}, Sequence: constants.MaxTxInSequenceNum, @@ -94,17 +85,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { true, uint64(6)), } - txInputWithSequenceLockTimeIsSeconds := externalapi.DomainTransactionInput{ - PreviousOutpoint: prevOutPoint, - SignatureScript: []byte{}, - Sequence: constants.SequenceLockTimeIsSeconds, - SigOpCount: 1, - UTXOEntry: utxo.NewUTXOEntry( - 100000000, // 1 KAS - scriptPublicKey, - true, - uint64(5)), - } txInputWithLargeAmount := externalapi.DomainTransactionInput{ PreviousOutpoint: prevOutPoint, SignatureScript: []byte{}, @@ -128,19 +108,19 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { uint64(5)), } - txOut := externalapi.DomainTransactionOutput{ + txOutput := externalapi.DomainTransactionOutput{ Value: 100000000, // 1 KAS ScriptPublicKey: scriptPublicKey, } - txOutBigValue := externalapi.DomainTransactionOutput{ + txOutputBigValue := externalapi.DomainTransactionOutput{ Value: 200_000_000, // 2 KAS ScriptPublicKey: scriptPublicKey, } validTx := externalapi.DomainTransaction{ Version: constants.MaxTransactionVersion, - Inputs: []*externalapi.DomainTransactionInput{&txInputWithSequenceLockTimeIsSeconds}, - Outputs: []*externalapi.DomainTransactionOutput{&txOut}, + Inputs: []*externalapi.DomainTransactionInput{&txInput}, + Outputs: []*externalapi.DomainTransactionOutput{&txOutput}, SubnetworkID: subnetworks.SubnetworkIDRegistry, Gas: 0, LockTime: 0} @@ -156,36 +136,36 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { txWithImmatureCoinbase := externalapi.DomainTransaction{ Version: constants.MaxTransactionVersion, - Inputs: []*externalapi.DomainTransactionInput{&immatureInput}, - Outputs: []*externalapi.DomainTransactionOutput{&txOut}, + Inputs: []*externalapi.DomainTransactionInput{&immatureCoinbaseInput}, + Outputs: []*externalapi.DomainTransactionOutput{&txOutput}, SubnetworkID: subnetworks.SubnetworkIDRegistry, Gas: 0, LockTime: 0} txWithLargeAmount := externalapi.DomainTransaction{ Version: constants.MaxTransactionVersion, Inputs: []*externalapi.DomainTransactionInput{&txInput, &txInputWithLargeAmount}, - Outputs: []*externalapi.DomainTransactionOutput{&txOut}, + Outputs: []*externalapi.DomainTransactionOutput{&txOutput}, SubnetworkID: subnetworks.SubnetworkIDRegistry, Gas: 0, LockTime: 0} txWithBigValue := externalapi.DomainTransaction{ Version: constants.MaxTransactionVersion, Inputs: []*externalapi.DomainTransactionInput{&txInput}, - Outputs: []*externalapi.DomainTransactionOutput{&txOutBigValue}, + Outputs: []*externalapi.DomainTransactionOutput{&txOutputBigValue}, SubnetworkID: subnetworks.SubnetworkIDRegistry, Gas: 0, LockTime: 0} txWithInvalidSignature := externalapi.DomainTransaction{ Version: constants.MaxTransactionVersion, - Inputs: []*externalapi.DomainTransactionInput{&txInput}, - Outputs: []*externalapi.DomainTransactionOutput{&txOut}, + Inputs: []*externalapi.DomainTransactionInput{&txInputWrongSignature}, + Outputs: []*externalapi.DomainTransactionOutput{&txOutput}, SubnetworkID: subnetworks.SubnetworkIDRegistry, Gas: 0, LockTime: 0} txWithBadSigOpCount := externalapi.DomainTransaction{ Version: constants.MaxTransactionVersion, Inputs: []*externalapi.DomainTransactionInput{&txInputWithBadSigOpCount}, - Outputs: []*externalapi.DomainTransactionOutput{&txOut}, + Outputs: []*externalapi.DomainTransactionOutput{&txOutput}, SubnetworkID: subnetworks.SubnetworkIDRegistry, Gas: 0, LockTime: 0} @@ -205,75 +185,60 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { nil)) tests := []struct { - name string - tx *externalapi.DomainTransaction - povBlockHash *externalapi.DomainHash - selectedParentMedianTime int64 - isValid bool - expectedError error + name string + tx *externalapi.DomainTransaction + povBlockHash *externalapi.DomainHash + isValid bool + expectedError error }{ { - name: "Valid transaction", - tx: &validTx, - povBlockHash: povBlockHash, - selectedParentMedianTime: 1, - isValid: true, - expectedError: nil, + name: "Valid transaction", + tx: &validTx, + povBlockHash: povBlockHash, + isValid: true, + expectedError: nil, }, { // The calculated block coinbase maturity is smaller than the minimum expected blockCoinbaseMaturity. // The povBlockHash DAA score is 10 and the UTXO DAA score is 5, hence the The subtraction between // them will yield a smaller result than the required CoinbaseMaturity (currently set to 100). - name: "checkTransactionCoinbaseMaturity", - tx: &txWithImmatureCoinbase, - povBlockHash: povBlockHash, - selectedParentMedianTime: 1, - isValid: false, - expectedError: ruleerrors.ErrImmatureSpend, + name: "checkTransactionCoinbaseMaturity", + tx: &txWithImmatureCoinbase, + povBlockHash: povBlockHash, + isValid: false, + expectedError: ruleerrors.ErrImmatureSpend, }, { // The total inputs amount is bigger than the allowed maximum (constants.MaxSompi) - name: "checkTransactionInputAmounts", - tx: &txWithLargeAmount, - povBlockHash: povBlockHash, - selectedParentMedianTime: 1, - isValid: false, - expectedError: ruleerrors.ErrBadTxOutValue, + name: "checkTransactionInputAmounts", + tx: &txWithLargeAmount, + povBlockHash: povBlockHash, + isValid: false, + expectedError: ruleerrors.ErrBadTxOutValue, }, { // The total SompiIn (sum of inputs amount) is smaller than the total SompiOut (sum of outputs value) and hence invalid. - name: "checkTransactionOutputAmounts", - tx: &txWithBigValue, - povBlockHash: povBlockHash, - selectedParentMedianTime: 1, - isValid: false, - expectedError: ruleerrors.ErrSpendTooHigh, + name: "checkTransactionOutputAmounts", + tx: &txWithBigValue, + povBlockHash: povBlockHash, + isValid: false, + expectedError: ruleerrors.ErrSpendTooHigh, }, - { // the selectedParentMedianTime is negative and hence invalid. - name: "checkTransactionSequenceLock", - tx: &validTx, - povBlockHash: povBlockHash, - selectedParentMedianTime: -1, - isValid: false, - expectedError: ruleerrors.ErrUnfinalizedTx, - }, - { // The SignatureScript (in the immatureInput) is empty and hence invalid. - name: "checkTransactionScripts", - tx: &txWithInvalidSignature, - povBlockHash: povBlockHash, - selectedParentMedianTime: 1, - isValid: false, - expectedError: ruleerrors.ErrScriptValidation, + { // The SignatureScript is wrong + name: "checkTransactionScripts", + tx: &txWithInvalidSignature, + povBlockHash: povBlockHash, + isValid: false, + expectedError: ruleerrors.ErrScriptValidation, }, { // the SigOpCount in the input is wrong, and hence invalid - name: "checkTransactionSigOpCounts", - tx: &txWithBadSigOpCount, - povBlockHash: povBlockHash, - selectedParentMedianTime: 1, - isValid: false, - expectedError: ruleerrors.ErrWrongSigOpCount, + name: "checkTransactionSigOpCounts", + tx: &txWithBadSigOpCount, + povBlockHash: povBlockHash, + isValid: false, + expectedError: ruleerrors.ErrWrongSigOpCount, }, } for _, test := range tests { - err := tc.TransactionValidator().ValidateTransactionInContextAndPopulateFee(stagingArea, test.tx, test.povBlockHash, test.selectedParentMedianTime) + err := tc.TransactionValidator().ValidateTransactionInContextAndPopulateFee(stagingArea, test.tx, test.povBlockHash) if test.isValid { if err != nil { diff --git a/domain/consensus/utils/constants/constants.go b/domain/consensus/utils/constants/constants.go index 9ba84857c..aa1edf119 100644 --- a/domain/consensus/utils/constants/constants.go +++ b/domain/consensus/utils/constants/constants.go @@ -28,22 +28,11 @@ const ( // as a relative locktime. SequenceLockTimeDisabled uint64 = 1 << 63 - // SequenceLockTimeIsSeconds is a flag that if set on a transaction - // input's sequence number, the relative locktime has units of 1 second. - // If the flag is not set, the relative lockatime is according to DAA score. - SequenceLockTimeIsSeconds uint64 = 1 << 62 - // SequenceLockTimeMask is a mask that extracts the relative locktime // when masked against the transaction input sequence number. SequenceLockTimeMask uint64 = 0x00000000ffffffff - // SequenceLockTimeGranularity is the defined time based granularity - // for milliseconds-based relative time locks. When converting from milliseconds - // to a sequence number, the value is multiplied by this amount, - // therefore the granularity of relative time locks is 1000 milliseconds or 1 second. - SequenceLockTimeGranularity = 1000 - // LockTimeThreshold is the number below which a lock time is - // interpreted to be a block number. + // interpreted to be a DAA score. LockTimeThreshold = 5e11 // Tue Nov 5 00:53:20 1985 UTC ) diff --git a/domain/consensus/utils/txscript/opcode.go b/domain/consensus/utils/txscript/opcode.go index 7d3b75595..3543d1be2 100644 --- a/domain/consensus/utils/txscript/opcode.go +++ b/domain/consensus/utils/txscript/opcode.go @@ -1068,17 +1068,16 @@ func opcodeReturn(op *parsedOpcode, vm *Engine) error { return scriptError(ErrEarlyReturn, "script returned early") } -// verifyLockTime is a helper function used to validate locktimes. -func verifyLockTime(txLockTime, threshold, lockTime uint64) error { - // The lockTimes in both the script and transaction must be of the same - // type. - if !((txLockTime < threshold && lockTime < threshold) || - (txLockTime >= threshold && lockTime >= threshold)) { - str := fmt.Sprintf("mismatched locktime types -- tx locktime "+ - "%d, stack locktime %d", txLockTime, lockTime) - return scriptError(ErrUnsatisfiedLockTime, str) +func verifyLockTimeWithThreshold(txLockTime, threshold, lockTime uint64) error { + err := verifyLockTimeThreshold(txLockTime, threshold, lockTime) + if err != nil { + return err } + return verifyLockTime(txLockTime, lockTime) +} +// checkLockTimeRequirement is a helper function used to validate locktimes. +func verifyLockTime(txLockTime, lockTime uint64) error { if lockTime > txLockTime { str := fmt.Sprintf("locktime requirement not satisfied -- "+ "locktime is greater than the transaction locktime: "+ @@ -1089,6 +1088,18 @@ func verifyLockTime(txLockTime, threshold, lockTime uint64) error { return nil } +// verifyLockTimeThreshold is a helper function used to verify the lockTimes in both the script and transaction have the same type. +func verifyLockTimeThreshold(txLockTime, threshold, lockTime uint64) error { + if !((txLockTime < threshold && lockTime < threshold) || + (txLockTime >= threshold && lockTime >= threshold)) { + str := fmt.Sprintf("mismatched locktime types -- tx locktime "+ + "%d, stack locktime %d", txLockTime, lockTime) + return scriptError(ErrUnsatisfiedLockTime, str) + } + + return nil +} + // opcodeCheckLockTimeVerify compares the top item on the data stack to the // LockTime field of the transaction containing the script signature // validating if the transaction outputs are spendable yet. @@ -1111,12 +1122,11 @@ func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { lockTimeBytes = paddedLockTimeBytes } stackLockTime := binary.LittleEndian.Uint64(lockTimeBytes) - // The lock time field of a transaction is either a block height at + // The lock time field of a transaction is either a 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 block height. - err = verifyLockTime(vm.tx.LockTime, constants.LockTimeThreshold, - stackLockTime) + // threshold it is a DAA score. + err = verifyLockTimeWithThreshold(vm.tx.LockTime, constants.LockTimeThreshold, stackLockTime) if err != nil { return err } @@ -1188,10 +1198,9 @@ func opcodeCheckSequenceVerify(op *parsedOpcode, vm *Engine) error { } // Mask off non-consensus bits before doing comparisons. - lockTimeMask := constants.SequenceLockTimeIsSeconds | constants.SequenceLockTimeMask - maskedTxSequence := txSequence & lockTimeMask - maskedStackSequence := stackSequence & lockTimeMask - return verifyLockTime(maskedTxSequence, constants.SequenceLockTimeIsSeconds, maskedStackSequence) + maskedTxSequence := txSequence & constants.SequenceLockTimeMask + maskedStackSequence := stackSequence & constants.SequenceLockTimeMask + return verifyLockTime(maskedTxSequence, maskedStackSequence) } // opcodeToAltStack removes the top item from the main data stack and pushes it diff --git a/domain/miningmanager/miningmanager_test.go b/domain/miningmanager/miningmanager_test.go index cf9ead85c..b6c382988 100644 --- a/domain/miningmanager/miningmanager_test.go +++ b/domain/miningmanager/miningmanager_test.go @@ -21,8 +21,6 @@ import ( "github.com/pkg/errors" ) -const blockMaxMass uint64 = 10000000 - // TestValidateAndInsertTransaction verifies that valid transactions were successfully inserted into the mempool. func TestValidateAndInsertTransaction(t *testing.T) { testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { @@ -553,10 +551,10 @@ func createTransactionWithUTXOEntry(t *testing.T, i int) *externalapi.DomainTran if err != nil { t.Fatalf("PayToScriptHashSignatureScript: %v", err) } - txInputWithMaxSequence := externalapi.DomainTransactionInput{ + txInput := externalapi.DomainTransactionInput{ PreviousOutpoint: prevOutPoint, SignatureScript: signatureScript, - Sequence: constants.SequenceLockTimeIsSeconds, + Sequence: constants.MaxTxInSequenceNum, UTXOEntry: utxo.NewUTXOEntry( 100000000, // 1 KAS scriptPublicKey, @@ -569,7 +567,7 @@ func createTransactionWithUTXOEntry(t *testing.T, i int) *externalapi.DomainTran } tx := externalapi.DomainTransaction{ Version: constants.MaxTransactionVersion, - Inputs: []*externalapi.DomainTransactionInput{&txInputWithMaxSequence}, + Inputs: []*externalapi.DomainTransactionInput{&txInput}, Outputs: []*externalapi.DomainTransactionOutput{&txOut}, SubnetworkID: subnetworks.SubnetworkIDNative, Gas: 0,