-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtypes.go
More file actions
354 lines (282 loc) · 11.5 KB
/
types.go
File metadata and controls
354 lines (282 loc) · 11.5 KB
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
// Package hotstuff2 implements the HotStuff-2 consensus algorithm.
//
// HotStuff-2 is a two-phase Byzantine Fault Tolerant (BFT) consensus protocol
// that achieves optimistic responsiveness and linear view-change complexity.
//
// This implementation follows the architecture patterns from nspcc-dev/dbft
// for compatibility with NeoGo and uses generic types for flexibility.
package hotstuff2
// Hash represents a cryptographic hash function output.
// Implementations must be comparable and suitable for use as map keys.
type Hash interface {
// Bytes returns the raw byte representation of the hash.
Bytes() []byte
// Equals returns true if this hash equals the other hash.
// Must be consistent with Bytes() comparison.
Equals(other Hash) bool
// String returns a human-readable representation (typically hex-encoded).
String() string
}
// PublicKey represents a validator's public key for signature verification.
type PublicKey interface {
// Bytes returns the raw byte representation of the public key.
Bytes() []byte
// Verify verifies a signature over the given message.
// Returns true if the signature is valid for this public key.
Verify(message []byte, signature []byte) bool
// Equals returns true if this public key equals the other.
// Accepts interface{} for flexibility with different implementations.
Equals(other interface{ Bytes() []byte }) bool
// String returns a human-readable representation of the public key.
String() string
}
// PrivateKey represents a validator's private key for signing messages.
type PrivateKey interface {
// PublicKey returns the corresponding public key.
// Returns a type that implements PublicKey interface methods.
PublicKey() interface {
Bytes() []byte
Verify(message []byte, signature []byte) bool
Equals(other interface{ Bytes() []byte }) bool
String() string
}
// Sign signs the given message and returns the signature.
// Returns an error if signing fails.
Sign(message []byte) ([]byte, error)
// Bytes returns the raw byte representation of the private key.
// WARNING: Handle with care - this exposes sensitive key material.
Bytes() []byte
}
// Block represents a proposed block in the consensus protocol.
// Blocks form a tree structure via parent links.
//
// The Block interface is intentionally minimal and payload-agnostic.
// Consensus treats block content as opaque bytes, allowing integration
// with various execution models:
// - Traditional transactions (serialize tx list into Payload)
// - DAG-based mempools like Narwhal (payload contains vertex references)
// - Rollup batches (payload contains batch commitments)
// - Any other application-specific data
type Block[H Hash] interface {
// Hash returns the unique identifier for this block.
// Also referred to as "nodeHash" in the HotStuff-2 paper.
Hash() H
// Height returns the block height (0 for genesis).
// Also referred to as block number or sequence number.
Height() uint32
// PrevHash returns the hash of the parent block.
// Must reference a valid block in the node tree.
PrevHash() H
// Payload returns the application-specific block content.
// Consensus treats this as opaque bytes - interpretation is left
// to the Executor implementation.
//
// Examples:
// - Serialized transaction list
// - DAG vertex references (Narwhal-style)
// - Rollup batch commitment
// - Empty (for empty blocks)
Payload() []byte
// ProposerIndex returns the validator index of the block proposer.
ProposerIndex() uint16
// Timestamp returns the block timestamp (milliseconds since epoch).
Timestamp() uint64
// Bytes returns the serialized form of the block.
Bytes() []byte
}
// QuorumCertificate represents an aggregated certificate from 2f+1 validators.
//
// CRITICAL SAFETY RULES:
// QC validation is the most critical part of consensus safety. A forged QC can
// cause replicas to commit conflicting blocks, violating Byzantine fault tolerance.
//
// Implementations MUST:
// - Validate signer set has exactly 2f+1 distinct validators (not more, not less)
// - Deduplicate signers before accepting QC (prevents forged QCs via duplicate votes)
// - Verify aggregate signature matches node digest + view number
// - NEVER accept QC without full validation - even from trusted sources
//
// Attack scenario: If duplicate signers are allowed, a Byzantine node could create
// a valid-looking QC with only f+1 real votes by duplicating each signature.
type QuorumCertificate[H Hash] interface {
// Node returns the hash of the block this QC certifies.
Node() H
// View returns the view number in which this QC was formed.
View() uint32
// Signers returns the list of validator indices who signed.
// MUST be deduplicated and sorted.
Signers() []uint16
// AggregateSignature returns the aggregated signature.
AggregateSignature() []byte
// Bytes returns the serialized form of the QC.
Bytes() []byte
// Validate verifies the QC is well-formed and signatures are valid.
// Returns error if:
// - Signer count < 2f+1
// - Duplicate signers found
// - Invalid signer indices
// - Aggregate signature verification fails
Validate(validators ValidatorSet) error
}
// ValidatorSet represents the current set of consensus validators.
type ValidatorSet interface {
// Count returns the total number of validators.
Count() int
// GetByIndex returns the public key for a validator by index.
// Returns error if index is out of bounds.
GetByIndex(index uint16) (PublicKey, error)
// Contains returns true if the validator index is valid.
Contains(index uint16) bool
// GetPublicKeys returns public keys for the given validator indices.
// Used for aggregate signature verification.
GetPublicKeys(indices []uint16) ([]PublicKey, error)
// GetLeader returns the validator index of the leader for a given view.
// Typically implements round-robin: view % count.
GetLeader(view uint32) uint16
// F returns the maximum number of Byzantine validators tolerated.
// Must return (n-1)/3 where n is the total validator count.
F() int
}
// Storage provides persistent storage for consensus state.
//
// CRITICAL SAFETY: All Put operations must be durable before returning.
// The locked QC and view number must be persisted atomically to prevent
// safety violations after crash recovery.
type Storage[H Hash] interface {
// GetBlock retrieves a block by its hash.
GetBlock(hash H) (Block[H], error)
// PutBlock persists a block.
// Must complete durably before returning.
PutBlock(block Block[H]) error
// GetLastBlock returns the most recently committed block.
GetLastBlock() (Block[H], error)
// GetQC retrieves the QC that certifies the given block.
GetQC(nodeHash H) (QuorumCertificate[H], error)
// PutQC persists a QC.
// Must complete durably before returning.
PutQC(qc QuorumCertificate[H]) error
// GetHighestLockedQC returns the highest locked QC.
// This is safety-critical for crash recovery.
GetHighestLockedQC() (QuorumCertificate[H], error)
// PutHighestLockedQC persists the highest locked QC.
// Must complete durably before returning.
PutHighestLockedQC(qc QuorumCertificate[H]) error
// GetView returns the current view number.
GetView() (uint32, error)
// PutView persists the current view number.
// Must complete durably before returning.
PutView(view uint32) error
// Close releases any resources held by the storage.
Close() error
}
// Network provides message broadcasting and delivery.
type Network[H Hash] interface {
// Broadcast sends a message to all validators.
Broadcast(payload ConsensusPayload[H])
// SendTo sends a message to a specific validator.
SendTo(validatorIndex uint16, payload ConsensusPayload[H])
// Receive returns a channel for receiving consensus messages.
// The channel should be buffered to prevent message loss.
Receive() <-chan ConsensusPayload[H]
// Close releases any resources held by the network.
Close() error
}
// Executor handles block execution and validation.
type Executor[H Hash] interface {
// Execute applies a block's payload and returns the resulting state hash.
// Must be deterministic - same input always produces same output.
Execute(block Block[H]) (stateHash H, err error)
// Verify checks if a block is valid before voting.
// Should validate:
// - Parent block exists
// - Payload is well-formed (application-specific)
// - Block height is correct
// - Any application-specific rules
Verify(block Block[H]) error
// GetStateHash returns the current state hash after all executed blocks.
GetStateHash() H
// CreateBlock creates a new block proposal.
// Called when this validator is the leader.
// The implementation decides what payload to include (e.g., transactions
// from mempool, DAG references, rollup batch, etc.).
CreateBlock(height uint32, prevHash H, proposerIndex uint16) (Block[H], error)
}
// Timer provides timeout management for the pacemaker.
type Timer interface {
// Start starts the timer with the given duration in milliseconds.
Start(duration uint64)
// Stop stops the timer.
Stop()
// Reset resets the timer with a new duration.
Reset(duration uint64)
// C returns a channel that receives when the timer expires.
C() <-chan struct{}
}
// ConsensusPayload represents a message in the consensus protocol.
type ConsensusPayload[H Hash] interface {
// Type returns the message type (PROPOSE, VOTE, NEWVIEW).
Type() MessageType
// View returns the view number for this message.
View() uint32
// ValidatorIndex returns the index of the validator who created this message.
ValidatorIndex() uint16
// Bytes returns the serialized form of the payload.
Bytes() []byte
// Hash returns the hash of the payload for signing.
Hash() H
}
// MessageType represents the type of consensus message.
type MessageType uint8
const (
// MessageProposal represents a block proposal from the leader.
MessageProposal MessageType = iota
// MessageVote represents a vote for a proposal.
MessageVote
// MessageNewView represents a new view message during view change.
MessageNewView
)
// String returns the string representation of the message type.
func (mt MessageType) String() string {
switch mt {
case MessageProposal:
return "PROPOSAL"
case MessageVote:
return "VOTE"
case MessageNewView:
return "NEWVIEW"
default:
return "UNKNOWN"
}
}
// ConsensusState provides read-only access to consensus state.
// Use HotStuff2.State() to obtain an instance.
type ConsensusState interface {
// View returns the current view number.
View() uint32
// Height returns the height of the last committed block.
Height() uint32
// LockedQCView returns the view of the locked QC, or 0 if none.
LockedQCView() uint32
// HighQCView returns the view of the highest QC seen, or 0 if none.
HighQCView() uint32
// CommittedCount returns the number of committed blocks.
CommittedCount() int
}
// Hooks provides callbacks for consensus events.
// All callbacks are optional - nil callbacks are safely ignored.
// Callbacks are invoked synchronously, so implementations should be fast
// or dispatch to a goroutine to avoid blocking consensus.
type Hooks[H Hash] struct {
// OnPropose is called when this node proposes a block (leader only).
OnPropose func(view uint32, block Block[H])
// OnVote is called when this node votes for a proposal.
OnVote func(view uint32, blockHash H)
// OnQCFormed is called when a quorum certificate is formed.
OnQCFormed func(view uint32, qc QuorumCertificate[H])
// OnCommit is called when a block is committed (finalized).
OnCommit func(block Block[H])
// OnViewChange is called when the view changes.
OnViewChange func(oldView, newView uint32)
// OnTimeout is called when a view times out.
OnTimeout func(view uint32)
}