-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathnotarization.go
More file actions
116 lines (96 loc) · 3.93 KB
/
notarization.go
File metadata and controls
116 lines (96 loc) · 3.93 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
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package simplex
import (
"bytes"
"errors"
"fmt"
"slices"
"github.com/ava-labs/simplex/record"
"go.uber.org/zap"
)
var (
ErrorNoVotes = errors.New("no votes to notarize")
ErrorInvalidFinalizationDigest = errors.New("finalization digests do not match")
)
func NewEmptyNotarizationRecord(emptyNotarization *EmptyNotarization) []byte {
return NewQuorumRecord(emptyNotarization.QC.Bytes(), emptyNotarization.Vote.Bytes(), record.EmptyNotarizationRecordType)
}
func EmptyNotarizationFromRecord(record []byte, qd QCDeserializer) (EmptyNotarization, error) {
qcBytes, emptyVote, err := ParseEmptyNotarizationRecord(record)
if err != nil {
return EmptyNotarization{}, err
}
qc, err := qd.DeserializeQuorumCertificate(qcBytes)
if err != nil {
return EmptyNotarization{}, err
}
return EmptyNotarization{
Vote: emptyVote,
QC: qc,
}, nil
}
// NewNotarization builds a Notarization for a block described by [blockHeader] from [votesForCurrentRound].
func NewNotarization(logger Logger, signatureAggregator SignatureAggregator, votesForCurrentRound map[string]*Vote, blockHeader BlockHeader) (Notarization, error) {
voteCount := len(votesForCurrentRound)
signatures := make([]Signature, 0, voteCount)
logger.Info("Collected Quorum of votes", zap.Uint64("round", blockHeader.Round), zap.Int("votes", voteCount))
var toBeSignedVote *ToBeSignedVote
for _, vote := range votesForCurrentRound {
if !vote.Vote.BlockHeader.Equals(&blockHeader) {
logger.Debug("Skipping vote for block header different than the proposal block header",
zap.Stringer("voteDigest", vote.Vote.BlockHeader), zap.Stringer("NodeID", vote.Signature.Signer))
continue
}
logger.Debug("Collected vote from node", zap.Stringer("NodeID", vote.Signature.Signer),
zap.String("Digest", vote.Vote.Digest.String()), zap.Uint64("round", vote.Vote.Round),
zap.Uint64("seq", vote.Vote.Seq))
signatures = append(signatures, vote.Signature)
if toBeSignedVote == nil {
toBeSignedVote = &vote.Vote
}
}
if toBeSignedVote == nil {
return Notarization{}, ErrorNoVotes
}
// sort the signatures by Signer to ensure consistent ordering
slices.SortFunc(signatures, compareSignatures)
var notarization Notarization
var err error
notarization.Vote = *toBeSignedVote
notarization.QC, err = signatureAggregator.Aggregate(signatures)
if err != nil {
return Notarization{}, fmt.Errorf("could not aggregate signatures for notarization: %w", err)
}
return notarization, nil
}
// NewFinalization builds a Finalization from [finalizeVotes].
func NewFinalization(logger Logger, signatureAggregator SignatureAggregator, finalizeVotes []*FinalizeVote) (Finalization, error) {
voteCount := len(finalizeVotes)
if voteCount == 0 {
return Finalization{}, ErrorNoVotes
}
signatures := make([]Signature, 0, voteCount)
expectedDigest := finalizeVotes[0].Finalization.Digest
for _, vote := range finalizeVotes {
if vote.Finalization.Digest != expectedDigest {
return Finalization{}, ErrorInvalidFinalizationDigest
}
logger.Debug("Collected a finalize vote from node", zap.Stringer("NodeID", vote.Signature.Signer), zap.Uint64("round", vote.Finalization.Round))
signatures = append(signatures, vote.Signature)
}
// sort the signatures, as they are not guaranteed to be in the same order
slices.SortFunc(signatures, compareSignatures)
var finalization Finalization
var err error
finalization.Finalization = finalizeVotes[0].Finalization
finalization.QC, err = signatureAggregator.Aggregate(signatures)
if err != nil {
return Finalization{}, fmt.Errorf("could not aggregate signatures for finalization: %w", err)
}
return finalization, nil
}
// compareSignatures compares two signatures by their Signer field returning -1, 0, 1 if i is less than, equal to, or greater than j.
func compareSignatures(i, j Signature) int {
return bytes.Compare(i.Signer, j.Signer)
}