mempool: Enforce relative sequence locks.

This adds enforcement of the newly introduced relative time locks via
transaction sequence numbers when considering candidate transactions for
acceptance to the mempool, relaying, and inclusion into block templates.
It also raises the maximum standard transaction version to 2
accordingly.  This is acceptable because it is a soft forking change and
the aforementioned areas only enforce policy.

The following is an overview of the changes:

- Modify mempool to consider version 2 transactions standard
- Introduce a new callback to the mempool config for obtaining the
  sequence lock for block after the current best chain tip so it can
  remain decoupled from the chain
- Update mempool to enforce relative locks on all candidate transactions
- Update the mock chain in the mempool test harness accordingly
This commit is contained in:
Dave Collins 2017-09-21 12:53:34 -05:00
parent 42175088b6
commit 636d94a3dc
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
3 changed files with 42 additions and 11 deletions

View File

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

View File

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

View File

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