Disable relative time lock by time (#1800)

* ignore type flag

* Ignore type flag of relative time lock - interpret as DAA score

* Split verifyLockTime to functions with and without threshold.relative lockTimes dont need threshold check

* Change function name and order of the functions calls

Co-authored-by: tal <tal@daglabs.com>
This commit is contained in:
talelbaz 2021-07-18 17:52:16 +03:00 committed by GitHub
parent c731d74bc0
commit aba44e7bfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 123 additions and 240 deletions

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

@ -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,