Skip to content

Simplex QuorumCertificate and BLS aggregator #4004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions simplex/bls.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package simplex

import (
Expand All @@ -18,6 +15,13 @@ var (
errSignerNotFound = errors.New("signer not found in the membership set")
errInvalidNodeID = errors.New("unable to parse node ID")
errFailedToParseSignature = errors.New("failed to parse signature")

// QC errors
errFailedToParseQC = errors.New("failed to parse quorum certificate")
errNotEnoughSigners = errors.New("not enough signers")
errSignatureAggregation = errors.New("signature aggregation failed")
errEncodingMessageToSign = errors.New("failed to encode message to sign")
simplexLabel = []byte("simplex")
)

var _ simplex.Signer = (*BLSSigner)(nil)
Expand Down
235 changes: 235 additions & 0 deletions simplex/bls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/simplex"
)

func TestBLSVerifier(t *testing.T) {
Expand Down Expand Up @@ -86,3 +87,237 @@
})
}
}


func TestSignerNotInMemberSet(t *testing.T) {
config := newEngineConfig(t, 1)

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

too many arguments in call to newEngineConfig

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

too many arguments in call to newEngineConfig

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

too many arguments in call to newEngineConfig

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

too many arguments in call to newEngineConfig

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

too many arguments in call to newEngineConfig

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

too many arguments in call to newEngineConfig

Check failure on line 93 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values
signer, verifier := NewBLSAuth(&config.Config)

msg := "Begin at the beginning, and go on till you come to the end: then stop"

sig, err := signer.Sign([]byte(msg))
require.NoError(t, err)

notInMembershipSet := ids.GenerateTestNodeID()
err = verifier.Verify([]byte(msg), sig, notInMembershipSet[:])
require.ErrorIs(t, err, errSignerNotFound)
}

func TestSignerInvalidMessageEncoding(t *testing.T) {
config := newEngineConfig(t, 1)

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

too many arguments in call to newEngineConfig

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

too many arguments in call to newEngineConfig

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

too many arguments in call to newEngineConfig

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

too many arguments in call to newEngineConfig

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

too many arguments in call to newEngineConfig

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

too many arguments in call to newEngineConfig

Check failure on line 107 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

// sign a message with invalid encoding
dummyMsg := []byte("dummy message")
sig, err := config.SignBLS(dummyMsg)
require.NoError(t, err)

sigBytes := bls.SignatureToBytes(sig)

_, verifier := NewBLSAuth(&config.Config)
err = verifier.Verify(dummyMsg, sigBytes, config.Ctx.NodeID[:])
require.ErrorIs(t, err, errSignatureVerificationFailed)
}

// TestQCAggregateAndSign tests the aggregation of multiple signatures
// and then verifies the generated quorum certificate on that message.
func TestQCAggregateAndSign(t *testing.T) {
config := newEngineConfig(t, 2)

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

too many arguments in call to newEngineConfig

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

too many arguments in call to newEngineConfig

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

too many arguments in call to newEngineConfig

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

too many arguments in call to newEngineConfig

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

too many arguments in call to newEngineConfig

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

too many arguments in call to newEngineConfig

Check failure on line 124 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values
nodeID1 := config.Ctx.NodeID

signer, verifier := NewBLSAuth(&config.Config)
// nodes 1 and 2 will sign the same message
msg := []byte("Begin at the beginning, and go on till you come to the end: then stop")
sig, err := signer.Sign(msg)
require.NoError(t, err)
require.NoError(t, verifier.Verify(msg, sig, config.Ctx.NodeID[:]))

node2 := config.Nodes[1]
config.SignBLS = node2.sign
signer2, verifier2 := NewBLSAuth(&config.Config)
sig2, err := signer2.Sign(msg)
require.NoError(t, err)
require.NoError(t, verifier2.Verify(msg, sig2, node2.NodeID[:]))

// aggregate the signatures into a quorum certificate
signatureAggregator := SignatureAggregator(verifier)
qc, err := signatureAggregator.Aggregate(
[]simplex.Signature{
{Signer: nodeID1[:], Value: sig},
{Signer: node2.NodeID[:], Value: sig2},
},
)
require.NoError(t, err)
require.Equal(t, []simplex.NodeID{config.Ctx.NodeID[:], node2.NodeID[:]}, qc.Signers())
// verify the quorum certificate
require.NoError(t, qc.Verify(msg))

d := QCDeserializer(verifier)
// try to deserialize the quorum certificate
deserializedQC, err := d.DeserializeQuorumCertificate(qc.Bytes())
require.NoError(t, err)

require.Equal(t, qc.Signers(), deserializedQC.Signers())
require.NoError(t, deserializedQC.Verify(msg))
require.Equal(t, qc.Bytes(), deserializedQC.Bytes())
}

func TestQCSignerNotInMembershipSet(t *testing.T) {
config := newEngineConfig(t, 2)

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

too many arguments in call to newEngineConfig

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

too many arguments in call to newEngineConfig

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

too many arguments in call to newEngineConfig

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

too many arguments in call to newEngineConfig

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

too many arguments in call to newEngineConfig

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

too many arguments in call to newEngineConfig

Check failure on line 165 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values
nodeID1 := config.Ctx.NodeID

signer, verifier := NewBLSAuth(&config.Config)
// nodes 1 and 2 will sign the same message
msg := []byte("Begin at the beginning, and go on till you come to the end: then stop")
sig, err := signer.Sign(msg)
require.NoError(t, err)
require.NoError(t, verifier.Verify(msg, sig, config.Ctx.NodeID[:]))

// add a new validator, but it won't be in the membership set of the first node signer/verifier
vds := generateTestValidators(t, 1)
config.Ctx.NodeID = vds[0].NodeID
config.Validators[vds[0].NodeID] = &vds[0].GetValidatorOutput
config.SignBLS = vds[0].sign

// sign the same message with the new node
signer2, verifier2 := NewBLSAuth(&config.Config)
sig2, err := signer2.Sign(msg)
require.NoError(t, err)
require.NoError(t, verifier2.Verify(msg, sig2, vds[0].NodeID[:]))

// aggregate the signatures into a quorum certificate
signatureAggregator := SignatureAggregator(verifier)
_, err = signatureAggregator.Aggregate(
[]simplex.Signature{
{Signer: nodeID1[:], Value: sig},
{Signer: config.Ctx.NodeID[:], Value: sig2},
},
)
require.ErrorIs(t, err, errSignerNotFound)
}

func TestQCDeserializerInvalidInput(t *testing.T) {
config := newEngineConfig(t, 2)

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

too many arguments in call to newEngineConfig

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-22.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

too many arguments in call to newEngineConfig

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (macos-14)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

too many arguments in call to newEngineConfig

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Lint

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

too many arguments in call to newEngineConfig

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-noble)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

too many arguments in call to newEngineConfig

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (custom-arm64-jammy)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

too many arguments in call to newEngineConfig

Check failure on line 199 in simplex/bls_test.go

View workflow job for this annotation

GitHub Actions / Unit (ubuntu-24.04)

assignment mismatch: 1 variable but newEngineConfig returns 2 values

_, verifier := NewBLSAuth(&config.Config)
deserializer := QCDeserializer(verifier)

tests := []struct {
name string
input []byte
err error
}{
{
name: "too short input",
input: make([]byte, 10),
err: errFailedToParseQC,
},
{
name: "invalid signature bytes",
input: make([]byte, simplex.Quorum(len(verifier.nodeID2PK))*ids.NodeIDLen+bls.SignatureLen),
err: errFailedToParseQC,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := deserializer.DeserializeQuorumCertificate(tt.input)
require.ErrorIs(t, err, tt.err)
})
}
}

func TestSignatureAggregatorInsufficientSignatures(t *testing.T) {
config := newEngineConfig(t, 3)

signer, verifier := NewBLSAuth(&config.Config)
msg := []byte("test message")
sig, err := signer.Sign(msg)
require.NoError(t, err)

// try to aggregate with only 1 signature when quorum is 2
signatureAggregator := SignatureAggregator(verifier)
_, err = signatureAggregator.Aggregate(
[]simplex.Signature{
{Signer: config.Ctx.NodeID[:], Value: sig},
},
)
require.ErrorIs(t, err, errNotEnoughSigners)
}

func TestSignatureAggregatorInvalidSignatureBytes(t *testing.T) {
config := newEngineConfig(t, 2)

signer, verifier := NewBLSAuth(&config.Config)
msg := []byte("test message")
sig, err := signer.Sign(msg)
require.NoError(t, err)

signatureAggregator := SignatureAggregator(verifier)
_, err = signatureAggregator.Aggregate(
[]simplex.Signature{
{Signer: config.Ctx.NodeID[:], Value: sig},
{Signer: config.Ctx.NodeID[:], Value: []byte("invalid signature")},
},
)
require.ErrorIs(t, err, errFailedToParseSignature)
}

func TestSignatureAggregatorExcessSignatures(t *testing.T) {
config := newEngineConfig(t, 4)

_, verifier := NewBLSAuth(&config.Config)
msg := []byte("test message")

// Create signatures from all 4 nodes
signatures := make([]simplex.Signature, 4)
for i, node := range config.Nodes {
config.SignBLS = node.sign
nodeSigner, _ := NewBLSAuth(&config.Config)
sig, err := nodeSigner.Sign(msg)
require.NoError(t, err)

signatures[i] = simplex.Signature{Signer: node.NodeID[:], Value: sig}
}

// Aggregate should only use the first 3 signatures
signatureAggregator := SignatureAggregator(verifier)
qc, err := signatureAggregator.Aggregate(signatures)
require.NoError(t, err)

// Should only have 3 signers, not 4
require.Len(t, qc.Signers(), simplex.Quorum(len(config.Nodes)))
require.NoError(t, qc.Verify(msg))
}

func TestQCVerifyWithWrongMessage(t *testing.T) {
config := newEngineConfig(t, 2)

signer, verifier := NewBLSAuth(&config.Config)
originalMsg := []byte("original message")
wrongMsg := []byte("wrong message")

// Create signatures for original message
sig1, err := signer.Sign(originalMsg)
require.NoError(t, err)

config.SignBLS = config.Nodes[1].sign
signer2, _ := NewBLSAuth(&config.Config)
sig2, err := signer2.Sign(originalMsg)
require.NoError(t, err)

signatureAggregator := SignatureAggregator(verifier)
qc, err := signatureAggregator.Aggregate(
[]simplex.Signature{
{Signer: config.Nodes[0].NodeID[:], Value: sig1},
{Signer: config.Nodes[1].NodeID[:], Value: sig2},
},
)
require.NoError(t, err)

// Verify with original message should succeed
require.NoError(t, qc.Verify(originalMsg))

// Verify with wrong message should fail
err = qc.Verify(wrongMsg)
require.ErrorIs(t, err, errSignatureVerificationFailed)
}
Loading