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)