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.
This commit is contained in:
Dave Collins 2017-01-30 18:32:50 -06:00
parent 9f71f090e6
commit 5ffd552214
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
7 changed files with 49 additions and 51 deletions

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // 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. // Some fields from block headers to aid in best chain selection.
version int32 version int32
bits uint32 bits uint32
timestamp time.Time timestamp int64
} }
// newBlockNode returns a new block node for the given block header. It is // 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, height: height,
version: blockHeader.Version, version: blockHeader.Version,
bits: blockHeader.Bits, bits: blockHeader.Bits,
timestamp: blockHeader.Timestamp, timestamp: blockHeader.Timestamp.Unix(),
} }
return &node 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 // Create a slice of the previous few block timestamps used to calculate
// the median per the number defined by the constant medianTimeBlocks. // the median per the number defined by the constant medianTimeBlocks.
timestamps := make([]time.Time, medianTimeBlocks) timestamps := make([]int64, medianTimeBlocks)
numNodes := 0 numNodes := 0
iterNode := startNode iterNode := startNode
for i := 0; i < medianTimeBlocks && iterNode != nil; i++ { 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 // however, be aware that should the medianTimeBlocks constant ever be
// changed to an even number, this code will be wrong. // changed to an even number, this code will be wrong.
medianTimestamp := timestamps[numNodes/2] medianTimestamp := timestamps[numNodes/2]
return medianTimestamp, nil return time.Unix(medianTimestamp, 0), nil
} }
// CalcPastMedianTime calculates the median time of the previous few blocks // 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 // The chain appears to be current if none of the checks reported
// otherwise. // otherwise.
minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour) minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix()
return !b.bestNode.timestamp.Before(minus24Hours) return b.bestNode.timestamp >= minus24Hours
} }
// IsCurrent returns whether or not the chain believes it is current. Several // 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 params := config.ChainParams
targetTimespan := int64(params.TargetTimespan) targetTimespan := int64(params.TargetTimespan / time.Second)
targetTimePerBlock := int64(params.TargetTimePerBlock) targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
adjustmentFactor := params.RetargetAdjustmentFactor adjustmentFactor := params.RetargetAdjustmentFactor
b := BlockChain{ b := BlockChain{
checkpoints: config.Checkpoints, checkpoints: config.Checkpoints,

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"sort" "sort"
"time"
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
@ -1092,7 +1093,7 @@ func (b *BlockChain) createChainState() error {
numTxns := uint64(len(genesisBlock.MsgBlock().Transactions)) numTxns := uint64(len(genesisBlock.MsgBlock().Transactions))
blockSize := uint64(genesisBlock.MsgBlock().SerializeSize()) blockSize := uint64(genesisBlock.MsgBlock().SerializeSize())
b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, numTxns, 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 // Create the initial the database chain state including creating the
// necessary index buckets and inserting the genesis block. // necessary index buckets and inserting the genesis block.

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -158,14 +158,16 @@ func CalcWork(bits uint32) *big.Int {
// known good checkpoint. // known good checkpoint.
func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 { func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 {
// Convert types used in the calculations below. // Convert types used in the calculations below.
durationVal := int64(duration) durationVal := int64(duration / time.Second)
adjustmentFactor := big.NewInt(b.chainParams.RetargetAdjustmentFactor) adjustmentFactor := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
// The test network rules allow minimum difficulty blocks after more // The test network rules allow minimum difficulty blocks after more
// than twice the desired amount of time needed to generate a block has // than twice the desired amount of time needed to generate a block has
// elapsed. // elapsed.
if b.chainParams.ReduceMinDifficulty { if b.chainParams.ReduceMinDifficulty {
if durationVal > int64(b.chainParams.MinDiffReductionTime) { reductionTime := int64(b.chainParams.MinDiffReductionTime /
time.Second)
if durationVal > reductionTime {
return b.chainParams.PowLimitBits return b.chainParams.PowLimitBits
} }
} }
@ -243,9 +245,10 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
if b.chainParams.ReduceMinDifficulty { if b.chainParams.ReduceMinDifficulty {
// Return minimum difficulty when more than the desired // Return minimum difficulty when more than the desired
// amount of time has elapsed without mining a block. // amount of time has elapsed without mining a block.
reductionTime := b.chainParams.MinDiffReductionTime reductionTime := int64(b.chainParams.MinDiffReductionTime /
allowMinTime := lastNode.timestamp.Add(reductionTime) time.Second)
if newBlockTime.After(allowMinTime) { allowMinTime := lastNode.timestamp + reductionTime
if newBlockTime.Unix() > allowMinTime {
return b.chainParams.PowLimitBits, nil 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 // Limit the amount of adjustment that can occur to the previous
// difficulty. // difficulty.
actualTimespan := lastNode.timestamp.UnixNano() - firstNode.timestamp.UnixNano() actualTimespan := lastNode.timestamp - firstNode.timestamp
adjustedTimespan := actualTimespan adjustedTimespan := actualTimespan
if actualTimespan < b.minRetargetTimespan { if actualTimespan < b.minRetargetTimespan {
adjustedTimespan = b.minRetargetTimespan adjustedTimespan = b.minRetargetTimespan
@ -301,7 +304,8 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
// result. // result.
oldTarget := CompactToBig(lastNode.bits) oldTarget := CompactToBig(lastNode.bits)
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan)) 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. // Limit new value to the proof of work limit.
if newTarget.Cmp(b.chainParams.PowLimit) > 0 { 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("Old target %08x (%064x)", lastNode.bits, oldTarget)
log.Debugf("New target %08x (%064x)", newTargetBits, CompactToBig(newTargetBits)) log.Debugf("New target %08x (%064x)", newTargetBits, CompactToBig(newTargetBits))
log.Debugf("Actual timespan %v, adjusted timespan %v, target timespan %v", 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) b.chainParams.TargetTimespan)
return newTargetBits, nil return newTargetBits, nil

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -14,7 +14,6 @@ package blockchain
import ( import (
"sort" "sort"
"time"
) )
// TstSetCoinbaseMaturity makes the ability to set the coinbase maturity // 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 // TstTimeSorter makes the internal timeSorter type available to the test
// package. // package.
func TstTimeSorter(times []time.Time) sort.Interface { func TstTimeSorter(times []int64) sort.Interface {
return timeSorter(times) return timeSorter(times)
} }

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package blockchain package blockchain
import (
"time"
)
// timeSorter implements sort.Interface to allow a slice of timestamps to // timeSorter implements sort.Interface to allow a slice of timestamps to
// be sorted. // be sorted.
type timeSorter []time.Time type timeSorter []int64
// Len returns the number of timestamps in the slice. It is part of the // Len returns the number of timestamps in the slice. It is part of the
// sort.Interface implementation. // 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 // Less returns whether the timstamp with index i should sort before the
// timestamp with index j. It is part of the sort.Interface implementation. // timestamp with index j. It is part of the sort.Interface implementation.
func (s timeSorter) Less(i, j int) bool { func (s timeSorter) Less(i, j int) bool {
return s[i].Before(s[j]) return s[i] < s[j]
} }

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -8,7 +8,6 @@ import (
"reflect" "reflect"
"sort" "sort"
"testing" "testing"
"time"
"github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/blockchain"
) )
@ -16,31 +15,29 @@ import (
// TestTimeSorter tests the timeSorter implementation. // TestTimeSorter tests the timeSorter implementation.
func TestTimeSorter(t *testing.T) { func TestTimeSorter(t *testing.T) {
tests := []struct { tests := []struct {
in []time.Time in []int64
want []time.Time want []int64
}{ }{
{ {
in: []time.Time{ in: []int64{
time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) 1351228575, // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond) 1348310759, // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) 1305758502, // Wed May 18 22:41:42 UTC 2011 (Block #125000)
time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000) 1347777156, // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) 1349492104, // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
}, },
want: []time.Time{ want: []int64{
time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000) 1305758502, // Wed May 18 22:41:42 UTC 2011 (Block #125000)
time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) 1347777156, // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) 1348310759, // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000) 1349492104, // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) 1351228575, // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond)
}, },
}, },
} }
for i, test := range tests { for i, test := range tests {
result := make([]time.Time, len(test.in)) result := make([]int64, len(test.in))
copy(result, test.in) copy(result, test.in)
sort.Sort(blockchain.TstTimeSorter(result)) sort.Sort(blockchain.TstTimeSorter(result))
if !reflect.DeepEqual(result, test.want) { if !reflect.DeepEqual(result, test.want) {

View File

@ -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 // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // 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 // "standard" type. The rules for this BIP only apply to transactions
// after the timestamp defined by txscript.Bip16Activation. See // after the timestamp defined by txscript.Bip16Activation. See
// https://en.bitcoin.it/wiki/BIP_0016 for more details. // 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 // The number of signature operations must be less than the maximum
// allowed per block. Note that the preliminary sanity checks on a // allowed per block. Note that the preliminary sanity checks on a