From 00ebb9d14d7b6bcd3713e00e63b2b967bf19707c Mon Sep 17 00:00:00 2001 From: Dave Collins <davec@conformal.com> Date: Wed, 13 Jul 2016 19:36:36 -0500 Subject: [PATCH] blockchain: Associate time src with chain instance. Rather than making the caller to pass in the median time source on ProcessBlock and IsCurrent, modify the Config struct to include the median time source and associate it with the chain instance when it is created. This is being done because both the ProcessBlock and IsCurrent functions require access to the blockchain state already, it is a little bit safer to ensure the time source matches the chain instance state, it simplifies the caller logic, and it also allows its use within the logic of the blockchain package itself which will be required by upcoming rule change warning logic that is part of BIP9. --- blockchain/chain.go | 14 ++++++++++++-- blockchain/chain_test.go | 6 ++---- blockchain/common_test.go | 1 + blockchain/example_test.go | 14 ++++++-------- blockchain/process.go | 4 ++-- blockchain/reorganization_test.go | 3 +-- blockmanager.go | 8 ++++---- cmd/addblock/import.go | 6 ++---- 8 files changed, 30 insertions(+), 26 deletions(-) diff --git a/blockchain/chain.go b/blockchain/chain.go index 40cf0434..aa1f407e 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -164,6 +164,7 @@ type BlockChain struct { checkpointsByHeight map[int32]*chaincfg.Checkpoint db database.DB chainParams *chaincfg.Params + timeSource MedianTimeSource notifications NotificationCallback sigCache *txscript.SigCache indexManager IndexManager @@ -1331,7 +1332,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla // - 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() @@ -1344,7 +1345,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.timestamp.Before(minus24Hours) { return false } @@ -1397,6 +1398,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 @@ -1448,6 +1457,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 0cf9090e..02ff7230 100644 --- a/blockchain/chain_test.go +++ b/blockchain/chain_test.go @@ -48,10 +48,8 @@ func TestHaveBlock(t *testing.T) { chain.DisableCheckpoints(true) blockchain.TstSetCoinbaseMaturity(1) - timeSource := blockchain.NewMedianTime() for i := 1; i < len(blocks); i++ { - isOrphan, err := chain.ProcessBlock(blocks[i], timeSource, - blockchain.BFNone) + isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return @@ -65,7 +63,7 @@ func TestHaveBlock(t *testing.T) { // Insert an orphan block. isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), - timeSource, blockchain.BFNone) + blockchain.BFNone) if err != nil { t.Errorf("Unable to process block: %v", err) return diff --git a/blockchain/common_test.go b/blockchain/common_test.go index 9021b048..aebf8f7a 100644 --- a/blockchain/common_test.go +++ b/blockchain/common_test.go @@ -110,6 +110,7 @@ func chainSetup(dbName string) (*blockchain.BlockChain, func(), error) { chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: &chaincfg.MainNetParams, + TimeSource: blockchain.NewMedianTime(), }) if err != nil { teardown() diff --git a/blockchain/example_test.go b/blockchain/example_test.go index aba59557..ef39b72c 100644 --- a/blockchain/example_test.go +++ b/blockchain/example_test.go @@ -41,27 +41,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() - // 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 := btcutil.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 process block: %v\n", err) return diff --git a/blockchain/process.go b/blockchain/process.go index 930966f1..70180047 100644 --- a/blockchain/process.go +++ b/blockchain/process.go @@ -125,7 +125,7 @@ func (b *BlockChain) processOrphans(hash *wire.ShaHash, flags BehaviorFlags) err // when the error is nil. // // This function is safe for concurrent access. -func (b *BlockChain) ProcessBlock(block *btcutil.Block, timeSource MedianTimeSource, flags BehaviorFlags) (bool, error) { +func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { b.chainLock.Lock() defer b.chainLock.Unlock() @@ -152,7 +152,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, timeSource MedianTimeSou } // Perform preliminary sanity checks on the block and its transactions. - err = checkBlockSanity(block, b.chainParams.PowLimit, timeSource, flags) + err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags) if err != nil { return false, err } diff --git a/blockchain/reorganization_test.go b/blockchain/reorganization_test.go index 5ae4d3b2..efd6706f 100644 --- a/blockchain/reorganization_test.go +++ b/blockchain/reorganization_test.go @@ -58,10 +58,9 @@ func TestReorganization(t *testing.T) { chain.DisableCheckpoints(true) blockchain.TstSetCoinbaseMaturity(1) - timeSource := blockchain.NewMedianTime() expectedOrphans := map[int]struct{}{5: {}, 6: {}} for i := 1; i < len(blocks); i++ { - isOrphan, err := chain.ProcessBlock(blocks[i], timeSource, blockchain.BFNone) + isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return diff --git a/blockmanager.go b/blockmanager.go index 26659d01..f8918751 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -496,7 +496,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 } @@ -564,8 +564,7 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) { // Process the block to include validation, best chain selection, orphan // handling, etc. - isOrphan, err := b.chain.ProcessBlock(bmsg.block, - b.server.timeSource, behaviorFlags) + isOrphan, err := b.chain.ProcessBlock(bmsg.block, 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 @@ -1121,7 +1120,7 @@ out: case processBlockMsg: isOrphan, err := b.chain.ProcessBlock(msg.block, - b.server.timeSource, msg.flags) + msg.flags) if err != nil { msg.reply <- processBlockResponse{ isOrphan: false, @@ -1402,6 +1401,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 a6f2d86a..f6aab615 100644 --- a/cmd/addblock/import.go +++ b/cmd/addblock/import.go @@ -32,7 +32,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 @@ -129,8 +128,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 } @@ -329,6 +327,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 { @@ -343,7 +342,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(), }, nil }