-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chaincfg: create new abstract deployment starter/ender interfaces
In this commit, we create a series of new interfaces that'll allow us to abstract "when" exactly a deployment starts and ends. As is, all deployments start/end based on a unix timestamp, which is compared against the MTP of a given block to determine if a new deployment has started or ended. This works fine for BIP 9 which uses time based timeouts, but not so much for BIP 8. In order to prep a future refactor that allows our version bits implementation to support both time and block based start/end times, this new abstraction has been introduced.
- Loading branch information
Showing
2 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
package chaincfg | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/btcsuite/btcd/wire" | ||
) | ||
|
||
var ( | ||
// ErrNoBlockClock is returned when an operation fails due to lack of | ||
// synchornization with the current up to date block clock. | ||
ErrNoBlockClock = fmt.Errorf("no block clock synchronized") | ||
) | ||
|
||
// BlockClock is an abstraction over the past median time computation. The past | ||
// median time computation is used in several consensus checks such as CSV, and | ||
// also BIP 9 version bits. This interface allows callers to abstract away the | ||
// computation of the past median time from the perspective of a given block | ||
// header. | ||
type BlockClock interface { | ||
// PastMedianTime returns the past median time from the PoV of the | ||
// passed block header. The past median time is the median time of the | ||
// 11 blocks prior to the passed block header. | ||
PastMedianTime(*wire.BlockHeader) (time.Time, error) | ||
} | ||
|
||
// ConsensusDeploymentStarter determines if a given consensus deployment has | ||
// started. A deployment has started once according to the current "time", the | ||
// deployment is eligible for activation once a perquisite condition has | ||
// passed. | ||
type ConsensusDeploymentStarter interface { | ||
// HasStarted returns true if the consensus deployment has started. | ||
HasStarted(*wire.BlockHeader) (bool, error) | ||
} | ||
|
||
// ClockConsensusDeploymentStarter is a more specialized version of the | ||
// ConsensusDeploymentStarter that uses a BlockClock in order to determine if a | ||
// deployment has started or not. | ||
// | ||
// NOTE: Any calls to HasStarted will _fail_ with ErrNoBlockClock if they | ||
// happen before SynchronizeClock is executed. | ||
type ClockConsensusDeploymentStarter interface { | ||
ConsensusDeploymentStarter | ||
|
||
// SynchronizeClock synchronizes the target ConsensusDeploymentStarter | ||
// with the current up-to date BlockClock. | ||
SynchronizeClock(clock BlockClock) | ||
} | ||
|
||
// ConsensusDeploymentEnder determines if a given consensus deployment has | ||
// ended. A deployment has ended once according got eh current "time", the | ||
// deployment is no longer eligible for activation. | ||
type ConsensusDeploymentEnder interface { | ||
// HasEnded returns true if the consensus deployment has ended. | ||
HasEnded(*wire.BlockHeader) (bool, error) | ||
} | ||
|
||
// ClockConsensusDeploymentEnder is a more specialized version of the | ||
// ConsensusDeploymentEnder that uses a BlockClock in order to determine if a | ||
// deployment has started or not. | ||
// | ||
// NOTE: Any calls to HasEnded will _fail_ with ErrNoBlockClock if they | ||
// happen before SynchronizeClock is executed. | ||
type ClockConsensusDeploymentEnder interface { | ||
ConsensusDeploymentEnder | ||
|
||
// SynchronizeClock synchronizes the target ConsensusDeploymentStarter | ||
// with the current up-to date BlockClock. | ||
SynchronizeClock(clock BlockClock) | ||
} | ||
|
||
// MedianTimeDeploymentStarter is a ClockConsensusDeploymentStarter that uses | ||
// the median time past of a target block node to determine if a deployment has | ||
// started. | ||
type MedianTimeDeploymentStarter struct { | ||
blockClock BlockClock | ||
|
||
startTime time.Time | ||
} | ||
|
||
// NewMedianTimeDeploymentStarter returns a new instance of a | ||
// MedianTimeDeploymentStarter for a given start time. | ||
func NewMedianTimeDeploymentStarter(startTime time.Time) *MedianTimeDeploymentStarter { | ||
return &MedianTimeDeploymentStarter{ | ||
startTime: startTime, | ||
} | ||
} | ||
|
||
// SynchronizeClock synchronizes the target ConsensusDeploymentStarter with the | ||
// current up-to date BlockClock. | ||
func (m *MedianTimeDeploymentStarter) SynchronizeClock(clock BlockClock) { | ||
m.blockClock = clock | ||
} | ||
|
||
// HasStarted returns true if the consensus deployment has started. | ||
func (m *MedianTimeDeploymentStarter) HasStarted(blkHeader *wire.BlockHeader) (bool, error) { | ||
switch { | ||
// If we haven't yet been synchronized with a block clock, then we | ||
// can't tell the time, so we'll fail. | ||
case m.blockClock == nil: | ||
return false, ErrNoBlockClock | ||
|
||
// If the time is "zero", then the deployment has always started. | ||
case m.startTime.IsZero(): | ||
return true, nil | ||
} | ||
|
||
medianTime, err := m.blockClock.PastMedianTime(blkHeader) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
// We check both after and equal here as after will fail for equivalent | ||
// times, and we want to be inclusive. | ||
return medianTime.After(m.startTime) || medianTime.Equal(m.startTime), nil | ||
} | ||
|
||
// StartTime returns the raw start time of the deployment. | ||
func (m *MedianTimeDeploymentStarter) StartTime() time.Time { | ||
return m.startTime | ||
} | ||
|
||
// A compile-time assertion to ensure MedianTimeDeploymentStarter implements | ||
// the ClockConsensusDeploymentStarter interface. | ||
var _ ClockConsensusDeploymentStarter = (*MedianTimeDeploymentStarter)(nil) | ||
|
||
// MedianTimeDeploymentEnder is a ClockConsensusDeploymentEnder that uses the | ||
// median time past of a target block to determine if a deployment has ended. | ||
type MedianTimeDeploymentEnder struct { | ||
blockClock BlockClock | ||
|
||
endTime time.Time | ||
} | ||
|
||
// NewMedianTimeDeploymentEnder returns a new instance of the | ||
// MedianTimeDeploymentEnder anchored around the passed endTime. | ||
func NewMedianTimeDeploymentEnder(endTime time.Time) *MedianTimeDeploymentEnder { | ||
return &MedianTimeDeploymentEnder{ | ||
endTime: endTime, | ||
} | ||
} | ||
|
||
// HasEnded returns true if the deployment has ended. | ||
func (m *MedianTimeDeploymentEnder) HasEnded(blkHeader *wire.BlockHeader) (bool, error) { | ||
switch { | ||
// If we haven't yet been synchronized with a block clock, then we can't tell | ||
// the time, so we'll we haven't yet been synchronized with a block | ||
// clock, then w can't tell the time, so we'll fail. | ||
case m.blockClock == nil: | ||
return false, ErrNoBlockClock | ||
|
||
// If the time is "zero", then the deployment never ends. | ||
case m.endTime.IsZero(): | ||
return false, nil | ||
} | ||
|
||
medianTime, err := m.blockClock.PastMedianTime(blkHeader) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
// We check both after and equal here as after will fail for equivalent | ||
// times, and we want to be inclusive. | ||
return medianTime.After(m.endTime) || medianTime.Equal(m.endTime), nil | ||
} | ||
|
||
// MedianTimeDeploymentEnder returns the raw end time of the deployment. | ||
func (m *MedianTimeDeploymentEnder) EndTime() time.Time { | ||
return m.endTime | ||
} | ||
|
||
// SynchronizeClock synchronizes the target ConsensusDeploymentEnder with the | ||
// current up-to date BlockClock. | ||
func (m *MedianTimeDeploymentEnder) SynchronizeClock(clock BlockClock) { | ||
m.blockClock = clock | ||
} | ||
|
||
// A compile-time assertion to ensure MedianTimeDeploymentEnder implements the | ||
// ClockConsensusDeploymentStarter interface. | ||
var _ ClockConsensusDeploymentEnder = (*MedianTimeDeploymentEnder)(nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package chaincfg |