github.com/btcsuite/btcd@v0.24.0/chaincfg/deployment_time_frame.go (about)

     1  package chaincfg
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/btcsuite/btcd/wire"
     8  )
     9  
    10  var (
    11  	// ErrNoBlockClock is returned when an operation fails due to lack of
    12  	// synchornization with the current up to date block clock.
    13  	ErrNoBlockClock = fmt.Errorf("no block clock synchronized")
    14  )
    15  
    16  // BlockClock is an abstraction over the past median time computation. The past
    17  // median time computation is used in several consensus checks such as CSV, and
    18  // also BIP 9 version bits. This interface allows callers to abstract away the
    19  // computation of the past median time from the perspective of a given block
    20  // header.
    21  type BlockClock interface {
    22  	// PastMedianTime returns the past median time from the PoV of the
    23  	// passed block header. The past median time is the median time of the
    24  	// 11 blocks prior to the passed block header.
    25  	PastMedianTime(*wire.BlockHeader) (time.Time, error)
    26  }
    27  
    28  // ConsensusDeploymentStarter determines if a given consensus deployment has
    29  // started. A deployment has started once according to the current "time", the
    30  // deployment is eligible for activation once a perquisite condition has
    31  // passed.
    32  type ConsensusDeploymentStarter interface {
    33  	// HasStarted returns true if the consensus deployment has started.
    34  	HasStarted(*wire.BlockHeader) (bool, error)
    35  }
    36  
    37  // ClockConsensusDeploymentStarter is a more specialized version of the
    38  // ConsensusDeploymentStarter that uses a BlockClock in order to determine if a
    39  // deployment has started or not.
    40  //
    41  // NOTE: Any calls to HasStarted will _fail_ with ErrNoBlockClock if they
    42  // happen before SynchronizeClock is executed.
    43  type ClockConsensusDeploymentStarter interface {
    44  	ConsensusDeploymentStarter
    45  
    46  	// SynchronizeClock synchronizes the target ConsensusDeploymentStarter
    47  	// with the current up-to date BlockClock.
    48  	SynchronizeClock(clock BlockClock)
    49  }
    50  
    51  // ConsensusDeploymentEnder determines if a given consensus deployment has
    52  // ended. A deployment has ended once according got eh current "time", the
    53  // deployment is no longer eligible for activation.
    54  type ConsensusDeploymentEnder interface {
    55  	// HasEnded returns true if the consensus deployment has ended.
    56  	HasEnded(*wire.BlockHeader) (bool, error)
    57  }
    58  
    59  // ClockConsensusDeploymentEnder is a more specialized version of the
    60  // ConsensusDeploymentEnder that uses a BlockClock in order to determine if a
    61  // deployment has started or not.
    62  //
    63  // NOTE: Any calls to HasEnded will _fail_ with ErrNoBlockClock if they
    64  // happen before SynchronizeClock is executed.
    65  type ClockConsensusDeploymentEnder interface {
    66  	ConsensusDeploymentEnder
    67  
    68  	// SynchronizeClock synchronizes the target ConsensusDeploymentStarter
    69  	// with the current up-to date BlockClock.
    70  	SynchronizeClock(clock BlockClock)
    71  }
    72  
    73  // MedianTimeDeploymentStarter is a ClockConsensusDeploymentStarter that uses
    74  // the median time past of a target block node to determine if a deployment has
    75  // started.
    76  type MedianTimeDeploymentStarter struct {
    77  	blockClock BlockClock
    78  
    79  	startTime time.Time
    80  }
    81  
    82  // NewMedianTimeDeploymentStarter returns a new instance of a
    83  // MedianTimeDeploymentStarter for a given start time. Using a time.Time
    84  // instance where IsZero() is true, indicates that a deployment should be
    85  // considered to always have been started.
    86  func NewMedianTimeDeploymentStarter(startTime time.Time) *MedianTimeDeploymentStarter {
    87  	return &MedianTimeDeploymentStarter{
    88  		startTime: startTime,
    89  	}
    90  }
    91  
    92  // SynchronizeClock synchronizes the target ConsensusDeploymentStarter with the
    93  // current up-to date BlockClock.
    94  func (m *MedianTimeDeploymentStarter) SynchronizeClock(clock BlockClock) {
    95  	m.blockClock = clock
    96  }
    97  
    98  // HasStarted returns true if the consensus deployment has started.
    99  func (m *MedianTimeDeploymentStarter) HasStarted(blkHeader *wire.BlockHeader) (bool, error) {
   100  	switch {
   101  	// If we haven't yet been synchronized with a block clock, then we
   102  	// can't tell the time, so we'll fail.
   103  	case m.blockClock == nil:
   104  		return false, ErrNoBlockClock
   105  
   106  	// If the time is "zero", then the deployment has always started.
   107  	case m.startTime.IsZero():
   108  		return true, nil
   109  	}
   110  
   111  	medianTime, err := m.blockClock.PastMedianTime(blkHeader)
   112  	if err != nil {
   113  		return false, err
   114  	}
   115  
   116  	// We check both after and equal here as after will fail for equivalent
   117  	// times, and we want to be inclusive.
   118  	return medianTime.After(m.startTime) || medianTime.Equal(m.startTime), nil
   119  }
   120  
   121  // StartTime returns the raw start time of the deployment.
   122  func (m *MedianTimeDeploymentStarter) StartTime() time.Time {
   123  	return m.startTime
   124  }
   125  
   126  // A compile-time assertion to ensure MedianTimeDeploymentStarter implements
   127  // the ClockConsensusDeploymentStarter interface.
   128  var _ ClockConsensusDeploymentStarter = (*MedianTimeDeploymentStarter)(nil)
   129  
   130  // MedianTimeDeploymentEnder is a ClockConsensusDeploymentEnder that uses the
   131  // median time past of a target block to determine if a deployment has ended.
   132  type MedianTimeDeploymentEnder struct {
   133  	blockClock BlockClock
   134  
   135  	endTime time.Time
   136  }
   137  
   138  // NewMedianTimeDeploymentEnder returns a new instance of the
   139  // MedianTimeDeploymentEnder anchored around the passed endTime.  Using a
   140  // time.Time instance where IsZero() is true, indicates that a deployment
   141  // should be considered to never end.
   142  func NewMedianTimeDeploymentEnder(endTime time.Time) *MedianTimeDeploymentEnder {
   143  	return &MedianTimeDeploymentEnder{
   144  		endTime: endTime,
   145  	}
   146  }
   147  
   148  // HasEnded returns true if the deployment has ended.
   149  func (m *MedianTimeDeploymentEnder) HasEnded(blkHeader *wire.BlockHeader) (bool, error) {
   150  	switch {
   151  	// If we haven't yet been synchronized with a block clock, then we can't tell
   152  	// the time, so we'll we haven't yet been synchronized with a block
   153  	// clock, then w can't tell the time, so we'll fail.
   154  	case m.blockClock == nil:
   155  		return false, ErrNoBlockClock
   156  
   157  	// If the time is "zero", then the deployment never ends.
   158  	case m.endTime.IsZero():
   159  		return false, nil
   160  	}
   161  
   162  	medianTime, err := m.blockClock.PastMedianTime(blkHeader)
   163  	if err != nil {
   164  		return false, err
   165  	}
   166  
   167  	// We check both after and equal here as after will fail for equivalent
   168  	// times, and we want to be inclusive.
   169  	return medianTime.After(m.endTime) || medianTime.Equal(m.endTime), nil
   170  }
   171  
   172  // MedianTimeDeploymentEnder returns the raw end time of the deployment.
   173  func (m *MedianTimeDeploymentEnder) EndTime() time.Time {
   174  	return m.endTime
   175  }
   176  
   177  // SynchronizeClock synchronizes the target ConsensusDeploymentEnder with the
   178  // current up-to date BlockClock.
   179  func (m *MedianTimeDeploymentEnder) SynchronizeClock(clock BlockClock) {
   180  	m.blockClock = clock
   181  }
   182  
   183  // A compile-time assertion to ensure MedianTimeDeploymentEnder implements the
   184  // ClockConsensusDeploymentStarter interface.
   185  var _ ClockConsensusDeploymentEnder = (*MedianTimeDeploymentEnder)(nil)