diff --git a/mempool/mempool.go b/mempool/mempool.go index 3c64055395..1d9302e182 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -108,15 +108,17 @@ type Config struct { // the current best chain. BestHeight func() int64 + // PastMedianTime defines the function to use in order to access the + // median time calculated from the point-of-view of the current chain + // tip within the best chain. + PastMedianTime func() time.Time + // SubsidyCache defines a subsidy cache to use. SubsidyCache *blockchain.SubsidyCache // SigCache defines a signature cache to use. SigCache *txscript.SigCache - // TimeSource defines the timesource to use. - TimeSource blockchain.MedianTimeSource - // AddrIndex defines the optional address index instance to use for // indexing the unconfirmed transactions in the memory pool. // This can be nil if the address index is not enabled. @@ -830,8 +832,9 @@ func (mp *TxPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, rateLimit, allow // Don't allow non-standard transactions if the network parameters // forbid their relaying. if !mp.cfg.Policy.RelayNonStd { + medianTime := mp.cfg.PastMedianTime() err := checkTransactionStandard(tx, txType, nextBlockHeight, - mp.cfg.TimeSource, mp.cfg.Policy.MinRelayTxFee, + medianTime, mp.cfg.Policy.MinRelayTxFee, mp.cfg.Policy.MaxTxVersion) if err != nil { // Attempt to extract a reject code from the error so diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 2550205e03..f9287985cb 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -11,6 +11,7 @@ import ( "reflect" "sync" "testing" + "time" "github.com/decred/dcrd/blockchain" "github.com/decred/dcrd/chaincfg" @@ -32,6 +33,7 @@ type fakeChain struct { blocks map[chainhash.Hash]*dcrutil.Block currentHash chainhash.Hash currentHeight int64 + medianTime time.Time scriptFlags txscript.ScriptFlags } @@ -133,6 +135,23 @@ func (s *fakeChain) SetHeight(height int64) { s.Unlock() } +// PastMedianTime returns the current median time associated with the fake chain +// instance. +func (s *fakeChain) PastMedianTime() time.Time { + s.RLock() + medianTime := s.medianTime + s.RUnlock() + return medianTime +} + +// SetPastMedianTime sets the current median time associated with the fake chain +// instance. +func (s *fakeChain) SetPastMedianTime(medianTime time.Time) { + s.Lock() + s.medianTime = medianTime + s.Unlock() +} + // StandardVerifyFlags returns the standard verification script flags associated // with the fake chain instance. func (s *fakeChain) StandardVerifyFlags() (txscript.ScriptFlags, error) { @@ -369,9 +388,9 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp BlockByHash: chain.BlockByHash, BestHash: chain.BestHash, BestHeight: chain.BestHeight, + PastMedianTime: chain.PastMedianTime, SubsidyCache: subsidyCache, SigCache: nil, - TimeSource: blockchain.NewMedianTime(), AddrIndex: nil, ExistsAddrIndex: nil, }), @@ -394,6 +413,7 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp outputs = append(outputs, txOutToSpendableOut(coinbase, i)) } harness.chain.SetHeight(int64(chainParams.CoinbaseMaturity) + curHeight) + harness.chain.SetPastMedianTime(time.Now()) return &harness, outputs, nil } diff --git a/mempool/policy.go b/mempool/policy.go index 62a7f241aa..fda6b821f3 100644 --- a/mempool/policy.go +++ b/mempool/policy.go @@ -7,6 +7,7 @@ package mempool import ( "fmt" + "time" "github.com/decred/dcrd/blockchain" "github.com/decred/dcrd/blockchain/stake" @@ -363,7 +364,7 @@ func isDust(txOut *wire.TxOut, minRelayTxFee dcrutil.Amount) bool { // of recognized forms, and not containing "dust" outputs (those that are // so small it costs more to process them than they are worth). func checkTransactionStandard(tx *dcrutil.Tx, txType stake.TxType, height int64, - timeSource blockchain.MedianTimeSource, minRelayTxFee dcrutil.Amount, + medianTime time.Time, minRelayTxFee dcrutil.Amount, maxTxVersion uint16) error { // The transaction must be a currently supported version and serialize @@ -382,8 +383,7 @@ func checkTransactionStandard(tx *dcrutil.Tx, txType stake.TxType, height int64, // The transaction must be finalized to be standard and therefore // considered for inclusion in a block. - adjustedTime := timeSource.AdjustedTime() - if !blockchain.IsFinalizedTransaction(tx, height, adjustedTime) { + if !blockchain.IsFinalizedTransaction(tx, height, medianTime) { return txRuleError(wire.RejectNonstandard, "transaction is not finalized") } diff --git a/mempool/policy_test.go b/mempool/policy_test.go index da94ad0ce0..8c291692dc 100644 --- a/mempool/policy_test.go +++ b/mempool/policy_test.go @@ -9,8 +9,8 @@ import ( "bytes" "math/big" "testing" + "time" - "github.com/decred/dcrd/blockchain" "github.com/decred/dcrd/blockchain/stake" "github.com/decred/dcrd/chaincfg" "github.com/decred/dcrd/chaincfg/chainec" @@ -514,12 +514,13 @@ func TestCheckTransactionStandard(t *testing.T) { }, } - timeSource := blockchain.NewMedianTime() + medianTime := time.Now() for _, test := range tests { // Ensure standardness is as expected. tx := dcrutil.NewTx(&test.tx) err := checkTransactionStandard(tx, stake.DetermineTxType(&test.tx), - test.height, timeSource, DefaultMinRelayTxFee, maxTxVersion) + test.height, medianTime, DefaultMinRelayTxFee, + maxTxVersion) if err == nil && test.isStandard { // Test passes since function returned standard for a // transaction which is intended to be standard. diff --git a/server.go b/server.go index 384c432646..c162f4a39a 100644 --- a/server.go +++ b/server.go @@ -2446,7 +2446,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param BestHeight: func() int64 { return bm.chain.BestSnapshot().Height }, SubsidyCache: bm.chain.FetchSubsidyCache(), SigCache: s.sigCache, - TimeSource: s.timeSource, + PastMedianTime: func() time.Time { return bm.chain.BestSnapshot().MedianTime }, AddrIndex: s.addrIndex, ExistsAddrIndex: s.existsAddrIndex, }