Skip to content

Commit e8a91ec

Browse files
committed
rebase
1 parent 35c0161 commit e8a91ec

File tree

4 files changed

+443
-32
lines changed

4 files changed

+443
-32
lines changed

simplex/comm_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func TestCommFailsWithoutCurrentNode(t *testing.T) {
9090
config.Sender = sender
9191

9292
// set the curNode to a different nodeID than the one in the config
93-
vdrs := generateTestValidators(t, 3)
93+
vdrs := generateTestNodes(t, 3)
9494
config.Validators = newTestValidatorInfo(vdrs)
9595

9696
_, err := NewComm(config)

simplex/quorum.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package simplex
5+
6+
import (
7+
"encoding/asn1"
8+
"errors"
9+
"fmt"
10+
11+
"github.com/ava-labs/simplex"
12+
13+
"github.com/ava-labs/avalanchego/ids"
14+
"github.com/ava-labs/avalanchego/utils/crypto/bls"
15+
)
16+
17+
var (
18+
_ simplex.QuorumCertificate = (*QC)(nil)
19+
_ simplex.QCDeserializer = QCDeserializer{}
20+
_ simplex.SignatureAggregator = (*SignatureAggregator)(nil)
21+
22+
// QC errors
23+
errFailedToParseQC = errors.New("failed to parse quorum certificate")
24+
errUnexpectedSigners = errors.New("unexpected number of signers in quorum certificate")
25+
errSignatureAggregation = errors.New("signature aggregation failed")
26+
errEncodingMessageToSign = errors.New("failed to encode message to sign")
27+
)
28+
29+
// QC represents a quorum certificate in the Simplex consensus protocol.
30+
type QC struct {
31+
verifier BLSVerifier
32+
sig bls.Signature
33+
signers []simplex.NodeID
34+
}
35+
36+
// Signers returns the list of signers for the quorum certificate.
37+
func (qc *QC) Signers() []simplex.NodeID {
38+
return qc.signers
39+
}
40+
41+
// Verify checks if the quorum certificate is valid by verifying the aggregated signature against the signers' public keys.
42+
func (qc *QC) Verify(msg []byte) error {
43+
pks := make([]*bls.PublicKey, 0, len(qc.signers))
44+
if len(qc.signers) != simplex.Quorum(len(qc.verifier.nodeID2PK)) {
45+
return fmt.Errorf("%w: expected %d signers but got %d", errUnexpectedSigners, simplex.Quorum(len(qc.verifier.nodeID2PK)), len(qc.signers))
46+
}
47+
48+
// ensure all signers are in the membership set
49+
for _, signer := range qc.signers {
50+
pk, exists := qc.verifier.nodeID2PK[ids.NodeID(signer)]
51+
if !exists {
52+
return fmt.Errorf("%w: %x", errSignerNotFound, signer)
53+
}
54+
55+
pks = append(pks, pk)
56+
}
57+
58+
// aggregate the public keys
59+
aggPK, err := bls.AggregatePublicKeys(pks)
60+
if err != nil {
61+
return fmt.Errorf("%w: %w", errSignatureAggregation, err)
62+
}
63+
64+
message2Verify, err := encodeMessageToSign(msg, qc.verifier.chainID, qc.verifier.networkID)
65+
if err != nil {
66+
return fmt.Errorf("%w: %w", errEncodingMessageToSign, err)
67+
}
68+
69+
if !bls.Verify(aggPK, &qc.sig, message2Verify) {
70+
return errSignatureVerificationFailed
71+
}
72+
73+
return nil
74+
}
75+
76+
// asn1QC is the ASN.1 structure for the quorum certificate.
77+
// It contains the signers' public keys and the aggregated signature.
78+
// The signers are represented as byte slices of their IDs.
79+
type asn1QC struct {
80+
Signers [][]byte
81+
Signature []byte
82+
}
83+
84+
func (qc *QC) MarshalASN1() ([]byte, error) {
85+
sigBytes := bls.SignatureToBytes(&qc.sig)
86+
87+
signersBytes := make([][]byte, len(qc.signers))
88+
for i, signer := range qc.signers {
89+
s := signer // avoid aliasing
90+
signersBytes[i] = s[:]
91+
}
92+
asn1Data := asn1QC{
93+
Signers: signersBytes,
94+
Signature: sigBytes,
95+
}
96+
return asn1.Marshal(asn1Data)
97+
}
98+
99+
func (qc *QC) UnmarshalASN1(data []byte) error {
100+
var decoded asn1QC
101+
_, err := asn1.Unmarshal(data, &decoded)
102+
if err != nil {
103+
return err
104+
}
105+
qc.signers = make([]simplex.NodeID, len(decoded.Signers))
106+
for i, signerBytes := range decoded.Signers {
107+
if len(signerBytes) != ids.ShortIDLen { // TODO: so long as simplex is in a separate repo, we should decouple these ids as much as possible
108+
return errors.New("invalid signer length")
109+
}
110+
qc.signers[i] = simplex.NodeID(signerBytes)
111+
}
112+
sig, err := bls.SignatureFromBytes(decoded.Signature)
113+
if err != nil {
114+
return err
115+
}
116+
qc.sig = *sig
117+
118+
return nil
119+
}
120+
121+
// Bytes serializes the quorum certificate into bytes.
122+
func (qc *QC) Bytes() []byte {
123+
bytes, err := qc.MarshalASN1()
124+
if err != nil {
125+
panic(fmt.Errorf("failed to marshal QC: %w", err))
126+
}
127+
return bytes
128+
}
129+
130+
type QCDeserializer BLSVerifier
131+
132+
// DeserializeQuorumCertificate deserializes a quorum certificate from bytes.
133+
func (d QCDeserializer) DeserializeQuorumCertificate(bytes []byte) (simplex.QuorumCertificate, error) {
134+
var qc QC
135+
if err := qc.UnmarshalASN1(bytes); err != nil {
136+
return nil, fmt.Errorf("%w: %w", errFailedToParseQC, err)
137+
}
138+
qc.verifier = BLSVerifier(d)
139+
140+
return &qc, nil
141+
}
142+
143+
// SignatureAggregator aggregates signatures into a quorum certificate.
144+
type SignatureAggregator BLSVerifier
145+
146+
// Aggregate aggregates the provided signatures into a quorum certificate.
147+
// It requires at least a quorum of signatures to succeed.
148+
// If any signature is from a signer not in the membership set, it returns an error.
149+
func (a SignatureAggregator) Aggregate(signatures []simplex.Signature) (simplex.QuorumCertificate, error) {
150+
quorumSize := simplex.Quorum(len(a.nodeID2PK))
151+
if len(signatures) < quorumSize {
152+
return nil, fmt.Errorf("%w: wanted %d signatures but got %d", errUnexpectedSigners, quorumSize, len(signatures))
153+
}
154+
155+
signatures = signatures[:quorumSize]
156+
157+
signers := make([]simplex.NodeID, 0, quorumSize)
158+
sigs := make([]*bls.Signature, 0, quorumSize)
159+
for _, signature := range signatures {
160+
signer := signature.Signer
161+
_, exists := a.nodeID2PK[ids.NodeID(signer)]
162+
if !exists {
163+
return nil, fmt.Errorf("%w: %x", errSignerNotFound, signer)
164+
}
165+
signers = append(signers, signer)
166+
sig, err := bls.SignatureFromBytes(signature.Value)
167+
if err != nil {
168+
return nil, fmt.Errorf("%w: %w", errFailedToParseSignature, err)
169+
}
170+
sigs = append(sigs, sig)
171+
}
172+
173+
aggregatedSig, err := bls.AggregateSignatures(sigs)
174+
if err != nil {
175+
return nil, fmt.Errorf("%w: %w", errSignatureAggregation, err)
176+
}
177+
178+
return &QC{
179+
verifier: BLSVerifier(a),
180+
signers: signers,
181+
sig: *aggregatedSig,
182+
}, nil
183+
}

0 commit comments

Comments
 (0)