From 5ffd552214c052b14e65796bdfe0bc6c383e0c7e Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 30 Jan 2017 18:32:50 -0600 Subject: [PATCH] blockchain: Use int64 timestamps in block nodes. This modifies the block nodes used in the blockchain package for keeping track of the block index to use int64 for the timestamps instead of time.Time. This is being done because a time.Time takes 24 bytes while an int64 only takes 8 and the plan is to eventually move the entire block index into memory instead of the current dynamically-loaded version, so cutting the number of bytes used for the timestamp by a third is highly desirable. Also, the consensus code requires working with unix-style timestamps anyways, so switching over to them in the block node does not seem unreasonable. Finally, this does not go so far as to change all of the time.Time references, particularly those that are in the public API, so it is purely an internal change. --- blockchain/chain.go | 18 +++++++++--------- blockchain/chainio.go | 5 +++-- blockchain/difficulty.go | 23 ++++++++++++++--------- blockchain/internal_test.go | 5 ++--- blockchain/timesorter.go | 10 +++------- blockchain/timesorter_test.go | 35 ++++++++++++++++------------------- blockchain/validate.go | 4 ++-- 7 files changed, 49 insertions(+), 51 deletions(-) diff --git a/blockchain/chain.go b/blockchain/chain.go index 2b286f7a..70f1b7ea 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -62,7 +62,7 @@ type blockNode struct { // Some fields from block headers to aid in best chain selection. version int32 bits uint32 - timestamp time.Time + timestamp int64 } // newBlockNode returns a new block node for the given block header. It is @@ -81,7 +81,7 @@ func newBlockNode(blockHeader *wire.BlockHeader, blockHash *chainhash.Hash, heig height: height, version: blockHeader.Version, bits: blockHeader.Bits, - timestamp: blockHeader.Timestamp, + timestamp: blockHeader.Timestamp.Unix(), } return &node } @@ -666,7 +666,7 @@ func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error) // Create a slice of the previous few block timestamps used to calculate // the median per the number defined by the constant medianTimeBlocks. - timestamps := make([]time.Time, medianTimeBlocks) + timestamps := make([]int64, medianTimeBlocks) numNodes := 0 iterNode := startNode for i := 0; i < medianTimeBlocks && iterNode != nil; i++ { @@ -705,7 +705,7 @@ func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error) // however, be aware that should the medianTimeBlocks constant ever be // changed to an even number, this code will be wrong. medianTimestamp := timestamps[numNodes/2] - return medianTimestamp, nil + return time.Unix(medianTimestamp, 0), nil } // CalcPastMedianTime calculates the median time of the previous few blocks @@ -1581,8 +1581,8 @@ func (b *BlockChain) isCurrent() bool { // // The chain appears to be current if none of the checks reported // otherwise. - minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour) - return !b.bestNode.timestamp.Before(minus24Hours) + minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix() + return b.bestNode.timestamp >= minus24Hours } // IsCurrent returns whether or not the chain believes it is current. Several @@ -1716,8 +1716,8 @@ func New(config *Config) (*BlockChain, error) { } params := config.ChainParams - targetTimespan := int64(params.TargetTimespan) - targetTimePerBlock := int64(params.TargetTimePerBlock) + targetTimespan := int64(params.TargetTimespan / time.Second) + targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second) adjustmentFactor := params.RetargetAdjustmentFactor b := BlockChain{ checkpoints: config.Checkpoints, diff --git a/blockchain/chainio.go b/blockchain/chainio.go index e510fd5d..cc9c0303 100644 --- a/blockchain/chainio.go +++ b/blockchain/chainio.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2015-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,6 +10,7 @@ import ( "fmt" "math/big" "sort" + "time" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -1092,7 +1093,7 @@ func (b *BlockChain) createChainState() error { numTxns := uint64(len(genesisBlock.MsgBlock().Transactions)) blockSize := uint64(genesisBlock.MsgBlock().SerializeSize()) b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, numTxns, - b.bestNode.timestamp) + time.Unix(b.bestNode.timestamp, 0)) // Create the initial the database chain state including creating the // necessary index buckets and inserting the genesis block. diff --git a/blockchain/difficulty.go b/blockchain/difficulty.go index 2b8dd5f5..f6a0689e 100644 --- a/blockchain/difficulty.go +++ b/blockchain/difficulty.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -158,14 +158,16 @@ func CalcWork(bits uint32) *big.Int { // known good checkpoint. func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 { // Convert types used in the calculations below. - durationVal := int64(duration) + durationVal := int64(duration / time.Second) adjustmentFactor := big.NewInt(b.chainParams.RetargetAdjustmentFactor) // The test network rules allow minimum difficulty blocks after more // than twice the desired amount of time needed to generate a block has // elapsed. if b.chainParams.ReduceMinDifficulty { - if durationVal > int64(b.chainParams.MinDiffReductionTime) { + reductionTime := int64(b.chainParams.MinDiffReductionTime / + time.Second) + if durationVal > reductionTime { return b.chainParams.PowLimitBits } } @@ -243,9 +245,10 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim if b.chainParams.ReduceMinDifficulty { // Return minimum difficulty when more than the desired // amount of time has elapsed without mining a block. - reductionTime := b.chainParams.MinDiffReductionTime - allowMinTime := lastNode.timestamp.Add(reductionTime) - if newBlockTime.After(allowMinTime) { + reductionTime := int64(b.chainParams.MinDiffReductionTime / + time.Second) + allowMinTime := lastNode.timestamp + reductionTime + if newBlockTime.Unix() > allowMinTime { return b.chainParams.PowLimitBits, nil } @@ -286,7 +289,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim // Limit the amount of adjustment that can occur to the previous // difficulty. - actualTimespan := lastNode.timestamp.UnixNano() - firstNode.timestamp.UnixNano() + actualTimespan := lastNode.timestamp - firstNode.timestamp adjustedTimespan := actualTimespan if actualTimespan < b.minRetargetTimespan { adjustedTimespan = b.minRetargetTimespan @@ -301,7 +304,8 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim // result. oldTarget := CompactToBig(lastNode.bits) newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan)) - newTarget.Div(newTarget, big.NewInt(int64(b.chainParams.TargetTimespan))) + targetTimeSpan := int64(b.chainParams.TargetTimespan / time.Second) + newTarget.Div(newTarget, big.NewInt(targetTimeSpan)) // Limit new value to the proof of work limit. if newTarget.Cmp(b.chainParams.PowLimit) > 0 { @@ -317,7 +321,8 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim log.Debugf("Old target %08x (%064x)", lastNode.bits, oldTarget) log.Debugf("New target %08x (%064x)", newTargetBits, CompactToBig(newTargetBits)) log.Debugf("Actual timespan %v, adjusted timespan %v, target timespan %v", - time.Duration(actualTimespan), time.Duration(adjustedTimespan), + time.Duration(actualTimespan)*time.Second, + time.Duration(adjustedTimespan)*time.Second, b.chainParams.TargetTimespan) return newTargetBits, nil diff --git a/blockchain/internal_test.go b/blockchain/internal_test.go index 1508b4dd..e7432adb 100644 --- a/blockchain/internal_test.go +++ b/blockchain/internal_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -14,7 +14,6 @@ package blockchain import ( "sort" - "time" ) // TstSetCoinbaseMaturity makes the ability to set the coinbase maturity @@ -25,7 +24,7 @@ func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) { // TstTimeSorter makes the internal timeSorter type available to the test // package. -func TstTimeSorter(times []time.Time) sort.Interface { +func TstTimeSorter(times []int64) sort.Interface { return timeSorter(times) } diff --git a/blockchain/timesorter.go b/blockchain/timesorter.go index 516fe4fd..d0288e1d 100644 --- a/blockchain/timesorter.go +++ b/blockchain/timesorter.go @@ -1,16 +1,12 @@ -// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package blockchain -import ( - "time" -) - // timeSorter implements sort.Interface to allow a slice of timestamps to // be sorted. -type timeSorter []time.Time +type timeSorter []int64 // Len returns the number of timestamps in the slice. It is part of the // sort.Interface implementation. @@ -27,5 +23,5 @@ func (s timeSorter) Swap(i, j int) { // Less returns whether the timstamp with index i should sort before the // timestamp with index j. It is part of the sort.Interface implementation. func (s timeSorter) Less(i, j int) bool { - return s[i].Before(s[j]) + return s[i] < s[j] } diff --git a/blockchain/timesorter_test.go b/blockchain/timesorter_test.go index ca1b4afb..e1516557 100644 --- a/blockchain/timesorter_test.go +++ b/blockchain/timesorter_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -8,7 +8,6 @@ import ( "reflect" "sort" "testing" - "time" "github.com/btcsuite/btcd/blockchain" ) @@ -16,31 +15,29 @@ import ( // TestTimeSorter tests the timeSorter implementation. func TestTimeSorter(t *testing.T) { tests := []struct { - in []time.Time - want []time.Time + in []int64 + want []int64 }{ { - in: []time.Time{ - time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) - time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond) - time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) - time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000) - time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) - time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000) + in: []int64{ + 1351228575, // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) + 1348310759, // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) + 1305758502, // Wed May 18 22:41:42 UTC 2011 (Block #125000) + 1347777156, // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) + 1349492104, // Sat Oct 6 02:55:04 UTC 2012 (Block #202000) }, - want: []time.Time{ - time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000) - time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) - time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) - time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000) - time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) - time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond) + want: []int64{ + 1305758502, // Wed May 18 22:41:42 UTC 2011 (Block #125000) + 1347777156, // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) + 1348310759, // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) + 1349492104, // Sat Oct 6 02:55:04 UTC 2012 (Block #202000) + 1351228575, // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) }, }, } for i, test := range tests { - result := make([]time.Time, len(test.in)) + result := make([]int64, len(test.in)) copy(result, test.in) sort.Sort(blockchain.TstTimeSorter(result)) if !reflect.DeepEqual(result, test.want) { diff --git a/blockchain/validate.go b/blockchain/validate.go index 48451787..822c1791 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -1011,7 +1011,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, vi // "standard" type. The rules for this BIP only apply to transactions // after the timestamp defined by txscript.Bip16Activation. See // https://en.bitcoin.it/wiki/BIP_0016 for more details. - enforceBIP0016 := node.timestamp.After(txscript.Bip16Activation) + enforceBIP0016 := node.timestamp >= txscript.Bip16Activation.Unix() // The number of signature operations must be less than the maximum // allowed per block. Note that the preliminary sanity checks on a