Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@ btcutil/psbt/coverage.txt
*.swo
/.vim

#IDE
.idea

# Binaries produced by "make build"
/addblock
/btcctl
/btcd
/findcheckpoint
/gencerts

#Goland
.idea
.DS_Store
.aider*
10 changes: 9 additions & 1 deletion blockchain/difficulty.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,20 @@ func calcNextRequiredDifficulty(lastNode HeaderCtx, newBlockTime time.Time,
adjustedTimespan = c.MaxRetargetTimespan()
}

// Special difficulty rule for Testnet4
oldTarget := CompactToBig(lastNode.Bits())
if c.ChainParams().EnforceBIP94 {
// Here we use the first block of the difficulty period. This way
// the real difficulty is always preserved in the first block as
// it is not allowed to use the min-difficulty exception.
oldTarget = CompactToBig(firstNode.Bits())
}

// Calculate new target difficulty as:
// currentDifficulty * (adjustedTimespan / targetTimespan)
// The result uses integer division which means it will be slightly
// rounded down. Bitcoind also uses integer division to calculate this
// result.
oldTarget := CompactToBig(lastNode.Bits())
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
targetTimeSpan := int64(c.ChainParams().TargetTimespan / time.Second)
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
Expand Down
4 changes: 4 additions & 0 deletions blockchain/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ const (
// current chain tip. This is not a block validation rule, but is required
// for block proposals submitted via getblocktemplate RPC.
ErrPrevBlockNotBest

// ErrTimewarpAttack indicates a timewarp attack i.e.
// when block's timestamp is too early on diff adjustment block.
ErrTimewarpAttack
)

// Map of ErrorCode values back to their constant names for pretty printing.
Expand Down
17 changes: 16 additions & 1 deletion blockchain/thresholdstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ type thresholdConditionChecker interface {
// not the bit associated with the condition is set, but can be more
// complex as needed.
Condition(*blockNode) (bool, error)

// ForceActive returns if the deployment should be forced to transition
// to the active state. This is useful on certain testnet, where we
// we'd like for a deployment to always be active.
ForceActive(*blockNode) bool
}

// thresholdStateCache provides a type to cache the threshold states of each
Expand Down Expand Up @@ -279,7 +284,17 @@ func thresholdStateTransition(state ThresholdState, prevNode *blockNode,
// threshold states for previous windows are only calculated once.
//
// This function MUST be called with the chain state lock held (for writes).
func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdConditionChecker, cache *thresholdStateCache) (ThresholdState, error) {
func (b *BlockChain) thresholdState(prevNode *blockNode,
checker thresholdConditionChecker,
cache *thresholdStateCache) (ThresholdState, error) {

// If the deployment has a nonzero AlwaysActiveHeight and the next
// block’s height is at or above that threshold, then force the state
// to Active.
if checker.ForceActive(prevNode) {
return ThresholdActive, nil
}

// The threshold state for the window that contains the genesis block is
// defined by definition.
confirmationWindow := int32(checker.MinerConfirmationWindow())
Expand Down
4 changes: 4 additions & 0 deletions blockchain/thresholdstate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ func (c customDeploymentChecker) Condition(_ *blockNode) (bool, error) {
return c.conditionTrue, nil
}

func (c customDeploymentChecker) ForceActive(_ *blockNode) bool {
return false
}

// TestThresholdStateTransition tests that the thresholdStateTransition
// properly implements the BIP 009 state machine, along with the speedy trial
// augments.
Expand Down
55 changes: 49 additions & 6 deletions blockchain/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ const (
// coinbaseHeightAllocSize is the amount of bytes that the
// ScriptBuilder will allocate when validating the coinbase height.
coinbaseHeightAllocSize = 5

// maxTimeWarp is a maximum number of seconds that the timestamp of the first
// block of a difficulty adjustment period is allowed to
// be earlier than the last block of the previous period (BIP94).
maxTimeWarp = 600 * time.Second
)

var (
Expand Down Expand Up @@ -684,6 +689,12 @@ func compareScript(height int32, script []byte) error {
func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
flags BehaviorFlags, c ChainCtx, skipCheckpoint bool) error {

// The height of this block is one more than the referenced previous
// block.
blockHeight := prevNode.Height() + 1

params := c.ChainParams()

fastAdd := flags&BFFastAdd == BFFastAdd
if !fastAdd {
// Ensure the difficulty specified in the block header matches
Expand All @@ -710,16 +721,24 @@ func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
str = fmt.Sprintf(str, header.Timestamp, medianTime)
return ruleError(ErrTimeTooOld, str)
}
}

// The height of this block is one more than the referenced previous
// block.
blockHeight := prevNode.Height() + 1
// Testnet4 only: Check timestamp against prev for
// difficulty-adjustment blocks to prevent timewarp attacks.
if params.EnforceBIP94 {
err := assertNoTimeWarp(
blockHeight, c.BlocksPerRetarget(),
header.Timestamp,
time.Unix(prevNode.Timestamp(), 0),
)
if err != nil {
return err
}
}
}

// Reject outdated block versions once a majority of the network
// has upgraded. These were originally voted on by BIP0034,
// BIP0065, and BIP0066.
params := c.ChainParams()
if header.Version < 2 && blockHeight >= params.BIP0034Height ||
header.Version < 3 && blockHeight >= params.BIP0066Height ||
header.Version < 4 && blockHeight >= params.BIP0065Height {
Expand Down Expand Up @@ -761,6 +780,30 @@ func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
return nil
}

// assertNoTimeWarp checks the timestamp of the block against the previous
// block's timestamp for the first block of each difficulty adjustment interval
// to prevent timewarp attacks. This is defined in BIP-0094.
func assertNoTimeWarp(blockHeight, blocksPerReTarget int32, headerTimestamp,
prevBlockTimestamp time.Time) error {

// If this isn't the first block of the difficulty adjustment interval,
// then we can exit early.
if blockHeight%blocksPerReTarget != 0 {
return nil
}

// Check timestamp for the first block of each difficulty adjustment
// interval, except the genesis block.
if headerTimestamp.Before(prevBlockTimestamp.Add(-maxTimeWarp)) {
str := "block's timestamp %v is too early on diff adjustment " +
"block %v"
str = fmt.Sprintf(str, headerTimestamp, prevBlockTimestamp)
return ruleError(ErrTimewarpAttack, str)
}

return nil
}

// checkBlockContext performs several validation checks on the block which depend
// on its position within the block chain.
//
Expand Down Expand Up @@ -1230,7 +1273,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, vi
if csvState == ThresholdActive {
// If the CSV soft-fork is now active, then modify the
// scriptFlags to ensure that the CSV op code is properly
// validated during the script checks bleow.
// validated during the script checks below.
scriptFlags |= txscript.ScriptVerifyCheckSequenceVerify

// We obtain the MTP of the *previous* block in order to
Expand Down
Loading