diff --git a/blockchain/chain.go b/blockchain/chain.go index 75b87c68..2bd1d1eb 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -193,6 +193,7 @@ type BlockChain struct { db database.DB dbInfo *databaseInfo chainParams *chaincfg.Params + timeSource MedianTimeSource notifications NotificationCallback sigCache *txscript.SigCache indexManager IndexManager @@ -1703,8 +1704,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, // forceReorganizationToBlock forces a reorganization of the block chain to the // block hash requested, so long as it matches up with the current organization // of the best chain. -func (b *BlockChain) forceHeadReorganization(formerBest chainhash.Hash, - newBest chainhash.Hash, timeSource MedianTimeSource) error { +func (b *BlockChain) forceHeadReorganization(formerBest chainhash.Hash, newBest chainhash.Hash) error { if formerBest.IsEqual(&newBest) { return fmt.Errorf("can't reorganize to the same block") } @@ -1773,10 +1773,10 @@ func (b *BlockChain) forceHeadReorganization(formerBest chainhash.Hash, return err } - err = checkBlockSanity(newBestBlock, - timeSource, - BFNone, - b.chainParams) + err = checkBlockSanity(newBestBlock, b.timeSource, BFNone, b.chainParams) + if err != nil { + return err + } err = b.checkConnectBlock(newBestNode, newBestBlock, view, nil) if err != nil { @@ -1792,11 +1792,10 @@ func (b *BlockChain) forceHeadReorganization(formerBest chainhash.Hash, } // ForceHeadReorganization is the exported version of forceHeadReorganization. -func (b *BlockChain) ForceHeadReorganization(formerBest chainhash.Hash, - newBest chainhash.Hash, timeSource MedianTimeSource) error { +func (b *BlockChain) ForceHeadReorganization(formerBest chainhash.Hash, newBest chainhash.Hash) error { b.chainLock.Lock() defer b.chainLock.Unlock() - return b.forceHeadReorganization(formerBest, newBest, timeSource) + return b.forceHeadReorganization(formerBest, newBest) } // connectBestChain handles connecting the passed block to the chain while @@ -2001,7 +2000,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *dcrutil.Block, // - Latest block has a timestamp newer than 24 hours ago // // This function is safe for concurrent access. -func (b *BlockChain) IsCurrent(timeSource MedianTimeSource) bool { +func (b *BlockChain) IsCurrent() bool { b.chainLock.RLock() defer b.chainLock.RUnlock() @@ -2014,7 +2013,7 @@ func (b *BlockChain) IsCurrent(timeSource MedianTimeSource) bool { // Not current if the latest best block has a timestamp before 24 hours // ago. - minus24Hours := timeSource.AdjustedTime().Add(-24 * time.Hour) + minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour) if b.bestNode.header.Timestamp.Before(minus24Hours) { return false } @@ -2067,6 +2066,14 @@ type Config struct { // This field is required. ChainParams *chaincfg.Params + // TimeSource defines the median time source to use for things such as + // block processing and determining whether or not the chain is current. + // + // The caller is expected to keep a reference to the time source as well + // and add time samples from other peers on the network so the local + // time is adjusted to be in agreement with other peers. + TimeSource MedianTimeSource + // Notifications defines a callback to which notifications will be sent // when various events take place. See the documentation for // Notification and NotificationType for details on the types and @@ -2118,6 +2125,7 @@ func New(config *Config) (*BlockChain, error) { checkpointsByHeight: checkpointsByHeight, db: config.DB, chainParams: params, + timeSource: config.TimeSource, notifications: config.Notifications, sigCache: config.SigCache, indexManager: config.IndexManager, diff --git a/blockchain/chain_test.go b/blockchain/chain_test.go index 80e174de..1fe419f8 100644 --- a/blockchain/chain_test.go +++ b/blockchain/chain_test.go @@ -56,7 +56,6 @@ func TestBlockchainFunctions(t *testing.T) { } // Insert blocks 1 to 168 and perform various tests. - timeSource := blockchain.NewMedianTime() for i := 1; i <= 168; i++ { bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)]) if err != nil { @@ -64,7 +63,7 @@ func TestBlockchainFunctions(t *testing.T) { } bl.SetHeight(int64(i)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) } diff --git a/blockchain/common_test.go b/blockchain/common_test.go index d2f024e3..444d4ae1 100644 --- a/blockchain/common_test.go +++ b/blockchain/common_test.go @@ -112,6 +112,7 @@ func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: params, + TimeSource: blockchain.NewMedianTime(), }) if err != nil { diff --git a/blockchain/example_test.go b/blockchain/example_test.go index 5638a3d5..b857dad4 100644 --- a/blockchain/example_test.go +++ b/blockchain/example_test.go @@ -42,28 +42,25 @@ func ExampleBlockChain_ProcessBlock() { // Create a new BlockChain instance using the underlying database for // the main bitcoin network. This example does not demonstrate some // of the other available configuration options such as specifying a - // notification callback and signature cache. + // notification callback and signature cache. Also, the caller would + // ordinarily keep a reference to the median time source and add time + // values obtained from other peers on the network so the local time is + // adjusted to be in agreement with other peers. chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: &chaincfg.MainNetParams, + TimeSource: blockchain.NewMedianTime(), }) if err != nil { fmt.Printf("Failed to create chain instance: %v\n", err) return } - // Create a new median time source that is required by the upcoming - // call to ProcessBlock. Ordinarily this would also add time values - // obtained from other peers on the network so the local time is - // adjusted to be in agreement with other peers. - timeSource := blockchain.NewMedianTime() - - // Create a new BlockChain instance using the underlying database for - // the main bitcoin network. This example does not demonstrate some - // of the other available configuration options such as specifying a - // notification callback and signature cache. + // Process a block. For this example, we are going to intentionally + // cause an error by trying to process the genesis block which already + // exists. genesisBlock := dcrutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) - _, isOrphan, err := chain.ProcessBlock(genesisBlock, timeSource, blockchain.BFNone) + _, isOrphan, err := chain.ProcessBlock(genesisBlock, blockchain.BFNone) if err != nil { fmt.Printf("Failed to create chain instance: %v\n", err) return diff --git a/blockchain/fullblocks_test.go b/blockchain/fullblocks_test.go index c9847fb4..2174af48 100644 --- a/blockchain/fullblocks_test.go +++ b/blockchain/fullblocks_test.go @@ -32,7 +32,6 @@ func TestFullBlocks(t *testing.T) { // testAcceptedBlock attempts to process the block in the provided test // instance and ensures that it was accepted according to the flags // specified in the test. - timeSource := blockchain.NewMedianTime() testAcceptedBlock := func(item fullblocktests.AcceptedBlock) { blockHeight := item.Block.Header.Height block := dcrutil.NewBlock(item.Block) @@ -40,7 +39,7 @@ func TestFullBlocks(t *testing.T) { item.Name, block.Sha(), blockHeight) isMainChain, isOrphan, err := chain.ProcessBlock(block, - timeSource, blockchain.BFNone) + blockchain.BFNone) if err != nil { t.Fatalf("block %q (hash %s, height %d) should "+ "have been accepted: %v", item.Name, @@ -72,8 +71,7 @@ func TestFullBlocks(t *testing.T) { t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Sha(), blockHeight) - _, _, err := chain.ProcessBlock(block, timeSource, - blockchain.BFNone) + _, _, err := chain.ProcessBlock(block, blockchain.BFNone) if err == nil { t.Fatalf("block %q (hash %s, height %d) should not "+ "have been accepted", item.Name, block.Sha(), @@ -106,8 +104,7 @@ func TestFullBlocks(t *testing.T) { t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Sha(), blockHeight) - _, isOrphan, err := chain.ProcessBlock(block, timeSource, - blockchain.BFNone) + _, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone) if err != nil { // Ensure the error code is of the expected type. if _, ok := err.(blockchain.RuleError); !ok { diff --git a/blockchain/process.go b/blockchain/process.go index f5428ce8..f153ec69 100644 --- a/blockchain/process.go +++ b/blockchain/process.go @@ -126,8 +126,7 @@ func (b *BlockChain) processOrphans(hash *chainhash.Hash, flags BehaviorFlags) e // or on a side chain. True means it's on the main chain. // // This function is safe for concurrent access. -func (b *BlockChain) ProcessBlock(block *dcrutil.Block, - timeSource MedianTimeSource, flags BehaviorFlags) (bool, bool, error) { +func (b *BlockChain) ProcessBlock(block *dcrutil.Block, flags BehaviorFlags) (bool, bool, error) { b.chainLock.Lock() defer b.chainLock.Unlock() @@ -160,7 +159,7 @@ func (b *BlockChain) ProcessBlock(block *dcrutil.Block, } // Perform preliminary sanity checks on the block and its transactions. - err = checkBlockSanity(block, timeSource, flags, b.chainParams) + err = checkBlockSanity(block, b.timeSource, flags, b.chainParams) if err != nil { return false, false, err } diff --git a/blockchain/reorganization_test.go b/blockchain/reorganization_test.go index ddfef95b..602da8f2 100644 --- a/blockchain/reorganization_test.go +++ b/blockchain/reorganization_test.go @@ -57,7 +57,6 @@ func reorgTestLong(t *testing.T) { } // Load up the short chain - timeSource := blockchain.NewMedianTime() finalIdx1 := 179 for i := 1; i < finalIdx1+1; i++ { bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)]) @@ -66,7 +65,7 @@ func reorgTestLong(t *testing.T) { } bl.SetHeight(int64(i)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) } @@ -102,7 +101,7 @@ func reorgTestLong(t *testing.T) { } bl.SetHeight(int64(i)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error: %v", err.Error()) } @@ -168,8 +167,6 @@ func reorgTestShort(t *testing.T) { t.Errorf("error decoding test blockchain: %v", err.Error()) } - timeSource := blockchain.NewMedianTime() - // Load the long chain and begin loading blocks from that too, // forcing a reorganization // Load up the rest of the blocks up to HEAD. @@ -201,7 +198,7 @@ func reorgTestShort(t *testing.T) { } bl.SetHeight(int64(i)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) } @@ -218,7 +215,7 @@ func reorgTestShort(t *testing.T) { } bl.SetHeight(int64(i + j)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error: %v", err.Error()) } @@ -287,7 +284,6 @@ func reorgTestForced(t *testing.T) { } // Load up the short chain - timeSource := blockchain.NewMedianTime() finalIdx1 := 131 var oldBestHash *chainhash.Hash for i := 1; i < finalIdx1+1; i++ { @@ -300,7 +296,7 @@ func reorgTestForced(t *testing.T) { oldBestHash = bl.Sha() } - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) } @@ -334,13 +330,13 @@ func reorgTestForced(t *testing.T) { } forkBl.SetHeight(forkPoint) - _, _, err = chain.ProcessBlock(forkBl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(forkBl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error: %v", err.Error()) } newBestHash := forkBl.Sha() - err = chain.ForceHeadReorganization(*oldBestHash, *newBestHash, timeSource) + err = chain.ForceHeadReorganization(*oldBestHash, *newBestHash) if err != nil { t.Fatalf("failed forced reorganization: %v", err.Error()) } diff --git a/blockchain/validate_test.go b/blockchain/validate_test.go index 983d028a..3ed79b13 100644 --- a/blockchain/validate_test.go +++ b/blockchain/validate_test.go @@ -180,7 +180,7 @@ func TestBlockValidationRules(t *testing.T) { } bl.SetHeight(int64(i)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) } @@ -257,7 +257,7 @@ func TestBlockValidationRules(t *testing.T) { } bl.SetHeight(int64(i)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock error at height %v: %v", i, err.Error()) @@ -330,7 +330,7 @@ func TestBlockValidationRules(t *testing.T) { b153test = dcrutil.NewBlock(badDifficulty153) b153test.SetHeight(int64(testsIdx1)) - _, _, err = chain.ProcessBlock(b153test, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(b153test, blockchain.BFNone) if err == nil || err.(blockchain.RuleError).ErrorCode != blockchain.ErrUnexpectedDifficulty { t.Errorf("Failed to get error or correct error for "+ @@ -345,7 +345,7 @@ func TestBlockValidationRules(t *testing.T) { b153test = dcrutil.NewBlock(badBlockSize153) b153test.SetHeight(int64(testsIdx1)) - _, _, err = chain.ProcessBlock(b153test, timeSource, blockchain.BFNoPoWCheck) + _, _, err = chain.ProcessBlock(b153test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != blockchain.ErrWrongBlockSize { t.Errorf("Failed to get error or correct error for "+ @@ -360,7 +360,7 @@ func TestBlockValidationRules(t *testing.T) { b153test = dcrutil.NewBlock(badHash153) b153test.SetHeight(int64(testsIdx1)) - _, _, err = chain.ProcessBlock(b153test, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(b153test, blockchain.BFNone) if err == nil || err.(blockchain.RuleError).ErrorCode != blockchain.ErrHighHash { t.Errorf("Failed to get error or correct error for "+ @@ -741,7 +741,7 @@ func TestBlockValidationRules(t *testing.T) { block153MsgBlock.FromBytes(block153Bytes) b153test = dcrutil.NewBlock(block153MsgBlock) b153test.SetHeight(int64(testsIdx1)) - _, _, err = chain.ProcessBlock(b153test, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(b153test, blockchain.BFNone) if err != nil { t.Errorf("Got unexpected error processing block 153 %v", err) } @@ -1420,7 +1420,7 @@ func TestBlockValidationRules(t *testing.T) { b154test.SetHeight(int64(testsIdx2)) // Throws ProcessBlock error through checkBlockContext. - _, _, err = chain.ProcessBlock(b154test, timeSource, blockchain.BFNoPoWCheck) + _, _, err = chain.ProcessBlock(b154test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != blockchain.ErrBadBlockHeight { t.Errorf("ProcessBlock ErrBadBlockHeight test no or unexpected "+ @@ -1780,7 +1780,7 @@ func TestBlockValidationRules(t *testing.T) { } } - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock error: %v", err.Error()) } @@ -2008,7 +2008,6 @@ func TestBlockchainSpendJournal(t *testing.T) { } // Load up the short chain - timeSource := blockchain.NewMedianTime() finalIdx1 := 179 for i := 1; i < finalIdx1+1; i++ { bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)]) @@ -2017,7 +2016,7 @@ func TestBlockchainSpendJournal(t *testing.T) { } bl.SetHeight(int64(i)) - _, _, err = chain.ProcessBlock(bl, timeSource, blockchain.BFNone) + _, _, err = chain.ProcessBlock(bl, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error()) } diff --git a/blockmanager.go b/blockmanager.go index c89d0c55..5f5734d4 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -865,7 +865,7 @@ func (b *blockManager) handleTxMsg(tmsg *txMsg) { // current returns true if we believe we are synced with our peers, false if we // still have blocks to check func (b *blockManager) current() bool { - if !b.chain.IsCurrent(b.server.timeSource) { + if !b.chain.IsCurrent() { return false } @@ -1124,7 +1124,7 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) { // Process the block to include validation, best chain selection, orphan // handling, etc. onMainChain, isOrphan, err := b.chain.ProcessBlock(bmsg.block, - b.server.timeSource, behaviorFlags) + behaviorFlags) if err != nil { // When the error is a rule error, it means the block was simply // rejected as opposed to something actually going wrong, so log @@ -1821,9 +1821,7 @@ out: case forceReorganizationMsg: err := b.chain.ForceHeadReorganization( - msg.formerBest, - msg.newBest, - b.server.timeSource) + msg.formerBest, msg.newBest) // Reorganizing has succeeded, so we need to // update the chain state. @@ -1906,7 +1904,7 @@ out: case processBlockMsg: onMainChain, isOrphan, err := b.chain.ProcessBlock( - msg.block, b.server.timeSource, msg.flags) + msg.block, msg.flags) if err != nil { msg.reply <- processBlockResponse{ onMainChain: onMainChain, @@ -2741,6 +2739,7 @@ func newBlockManager(s *server, indexManager blockchain.IndexManager) (*blockMan bm.chain, err = blockchain.New(&blockchain.Config{ DB: s.db, ChainParams: s.chainParams, + TimeSource: s.timeSource, Notifications: bm.handleNotifyMsg, SigCache: s.sigCache, IndexManager: indexManager, diff --git a/cmd/addblock/import.go b/cmd/addblock/import.go index 4a7fdbe3..b7fed26d 100644 --- a/cmd/addblock/import.go +++ b/cmd/addblock/import.go @@ -35,7 +35,6 @@ type importResults struct { type blockImporter struct { db database.DB chain *blockchain.BlockChain - medianTime blockchain.MedianTimeSource r io.ReadSeeker processQueue chan []byte doneChan chan bool @@ -133,8 +132,7 @@ func (bi *blockImporter) processBlock(serializedBlock []byte) (bool, error) { // Ensure the blocks follows all of the chain rules and match up to the // known checkpoints. - _, isOrphan, err := bi.chain.ProcessBlock(block, bi.medianTime, - blockchain.BFFastAdd) + _, isOrphan, err := bi.chain.ProcessBlock(block, blockchain.BFFastAdd) if err != nil { return false, err } @@ -340,6 +338,7 @@ func newBlockImporter(db database.DB, r io.ReadSeeker) (*blockImporter, error) { chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: activeNetParams, + TimeSource: blockchain.NewMedianTime(), IndexManager: indexManager, }) if err != nil { @@ -354,7 +353,6 @@ func newBlockImporter(db database.DB, r io.ReadSeeker) (*blockImporter, error) { errChan: make(chan error), quit: make(chan struct{}), chain: chain, - medianTime: blockchain.NewMedianTime(), lastLogTime: time.Now(), startTime: time.Now(), }, nil