diff --git a/mempool/mempool.go b/mempool/mempool.go index 1d9302e1..91e428ff 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -113,6 +113,11 @@ type Config struct { // tip within the best chain. PastMedianTime func() time.Time + // CalcSequenceLock defines the function to use in order to generate + // the current sequence lock for the given transaction using the passed + // utxo view. + CalcSequenceLock func(*dcrutil.Tx, *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) + // SubsidyCache defines a subsidy cache to use. SubsidyCache *blockchain.SubsidyCache @@ -831,8 +836,8 @@ func (mp *TxPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, rateLimit, allow // Don't allow non-standard transactions if the network parameters // forbid their relaying. + medianTime := mp.cfg.PastMedianTime() if !mp.cfg.Policy.RelayNonStd { - medianTime := mp.cfg.PastMedianTime() err := checkTransactionStandard(tx, txType, nextBlockHeight, medianTime, mp.cfg.Policy.MinRelayTxFee, mp.cfg.Policy.MaxTxVersion) @@ -991,6 +996,21 @@ func (mp *TxPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, rateLimit, allow return missingParents, nil } + // Don't allow the transaction into the mempool unless its sequence + // lock is active, meaning that it'll be allowed into the next block + // with respect to its defined relative lock times. + seqLock, err := mp.cfg.CalcSequenceLock(tx, utxoView) + if err != nil { + if cerr, ok := err.(blockchain.RuleError); ok { + return nil, chainRuleError(cerr) + } + return nil, err + } + if !blockchain.SequenceLockActive(seqLock, nextBlockHeight, medianTime) { + return nil, txRuleError(wire.RejectNonstandard, + "transaction sequence locks on inputs not met") + } + // Perform several checks on the transaction inputs using the invariant // rules in chain for what transactions are allowed into blocks. // Also returns the fees associated with the transaction which will be diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index f9287985..08ca17c2 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -152,6 +152,15 @@ func (s *fakeChain) SetPastMedianTime(medianTime time.Time) { s.Unlock() } +// CalcSequenceLock returns the current sequence lock for the passed transaction +// associated with the fake chain instance. +func (s *fakeChain) CalcSequenceLock(tx *dcrutil.Tx, view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) { + return &blockchain.SequenceLock{ + MinHeight: -1, + MinTime: -1, + }, nil +} + // StandardVerifyFlags returns the standard verification script flags associated // with the fake chain instance. func (s *fakeChain) StandardVerifyFlags() (txscript.ScriptFlags, error) { @@ -389,6 +398,7 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp BestHash: chain.BestHash, BestHeight: chain.BestHeight, PastMedianTime: chain.PastMedianTime, + CalcSequenceLock: chain.CalcSequenceLock, SubsidyCache: subsidyCache, SigCache: nil, AddrIndex: nil, diff --git a/server.go b/server.go index c162f4a3..2b18e2e5 100644 --- a/server.go +++ b/server.go @@ -2420,7 +2420,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param txC := mempool.Config{ Policy: mempool.Policy{ - MaxTxVersion: 1, + MaxTxVersion: 2, DisableRelayPriority: cfg.NoRelayPriority, RelayNonStd: cfg.RelayNonStd, FreeTxRelayLimit: cfg.FreeTxRelayLimit, @@ -2440,15 +2440,16 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param bm.chainState.Unlock() return sDiff, nil }, - FetchUtxoView: bm.chain.FetchUtxoView, - BlockByHash: bm.chain.BlockByHash, - BestHash: func() *chainhash.Hash { return bm.chain.BestSnapshot().Hash }, - BestHeight: func() int64 { return bm.chain.BestSnapshot().Height }, - SubsidyCache: bm.chain.FetchSubsidyCache(), - SigCache: s.sigCache, - PastMedianTime: func() time.Time { return bm.chain.BestSnapshot().MedianTime }, - AddrIndex: s.addrIndex, - ExistsAddrIndex: s.existsAddrIndex, + FetchUtxoView: bm.chain.FetchUtxoView, + BlockByHash: bm.chain.BlockByHash, + BestHash: func() *chainhash.Hash { return bm.chain.BestSnapshot().Hash }, + BestHeight: func() int64 { return bm.chain.BestSnapshot().Height }, + CalcSequenceLock: bm.chain.CalcSequenceLock, + SubsidyCache: bm.chain.FetchSubsidyCache(), + SigCache: s.sigCache, + PastMedianTime: func() time.Time { return bm.chain.BestSnapshot().MedianTime }, + AddrIndex: s.addrIndex, + ExistsAddrIndex: s.existsAddrIndex, } s.txMemPool = mempool.New(&txC)