forked from btcsuite/btcd
/
deployment_time_frame.go
185 lines (155 loc) · 6.55 KB
/
deployment_time_frame.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package chaincfg
import (
"fmt"
"time"
"github.com/alejoacosta74/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. Using a time.Time
// instance where IsZero() is true, indicates that a deployment should be
// considered to always have been started.
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. Using a
// time.Time instance where IsZero() is true, indicates that a deployment
// should be considered to never end.
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)