deployment_time_frame.go raw

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