Skip to content
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

Use a mask for BLS aggregation and improve caching #604

Merged
merged 3 commits into from
Sep 23, 2024
Merged
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
72 changes: 43 additions & 29 deletions blssig/aggregation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,55 @@ import (
"fmt"
"runtime/debug"

"github.com/filecoin-project/go-f3/gpbft"
"github.com/filecoin-project/go-f3/internal/measurements"
"go.dedis.ch/kyber/v4"
"go.opentelemetry.io/otel/metric"

"github.com/drand/kyber"
"github.com/drand/kyber/sign"
"github.com/filecoin-project/go-f3/gpbft"
"github.com/filecoin-project/go-f3/internal/bls/bdn"
"github.com/filecoin-project/go-f3/internal/measurements"
)

// Max size of the point cache.
const maxPointCacheSize = 10_000

func (v *Verifier) Aggregate(pubkeys []gpbft.PubKey, signatures [][]byte) (_agg []byte, _err error) {
type aggregation struct {
mask *bdn.Mask
scheme *bdn.Scheme
}

func (a *aggregation) Aggregate(mask []int, signatures [][]byte) (_agg []byte, _err error) {
defer func() {
status := measurements.AttrStatusSuccess
if _err != nil {
status = measurements.AttrStatusError
}

if perr := recover(); perr != nil {
_err = fmt.Errorf("panicked aggregating public keys: %v\n%s",
_err = fmt.Errorf("panicked aggregating signatures: %v\n%s",
perr, string(debug.Stack()))
log.Error(_err)
status = measurements.AttrStatusPanic
}

metrics.aggregate.Record(
context.TODO(), int64(len(pubkeys)),
context.TODO(), int64(len(mask)),
metric.WithAttributes(status),
)
}()

if len(pubkeys) != len(signatures) {
if len(mask) != len(signatures) {
return nil, fmt.Errorf("lengths of pubkeys and sigs does not match %d != %d",
len(pubkeys), len(signatures))
len(mask), len(signatures))
}

mask, err := v.pubkeysToMask(pubkeys)
if err != nil {
return nil, fmt.Errorf("converting public keys to mask: %w", err)
bdnMask := a.mask.Clone()
for _, bit := range mask {
if err := bdnMask.SetBit(bit, true); err != nil {
return nil, err
}
}

aggSigPoint, err := v.scheme.AggregateSignatures(signatures, mask)
aggSigPoint, err := a.scheme.AggregateSignatures(signatures, bdnMask)
if err != nil {
return nil, fmt.Errorf("computing aggregate signature: %w", err)
}
Expand All @@ -59,7 +66,7 @@ func (v *Verifier) Aggregate(pubkeys []gpbft.PubKey, signatures [][]byte) (_agg
return aggSig, nil
}

func (v *Verifier) VerifyAggregate(msg []byte, signature []byte, pubkeys []gpbft.PubKey) (_err error) {
func (a *aggregation) VerifyAggregate(mask []int, msg []byte, signature []byte) (_err error) {
defer func() {
status := measurements.AttrStatusSuccess
if _err != nil {
Expand All @@ -75,25 +82,35 @@ func (v *Verifier) VerifyAggregate(msg []byte, signature []byte, pubkeys []gpbft
}

metrics.verifyAggregate.Record(
context.TODO(), int64(len(pubkeys)),
context.TODO(), int64(len(mask)),
metric.WithAttributes(status),
)
}()

mask, err := v.pubkeysToMask(pubkeys)
if err != nil {
return fmt.Errorf("converting public keys to mask: %w", err)
bdnMask := a.mask.Clone()
for _, bit := range mask {
if err := bdnMask.SetBit(bit, true); err != nil {
return err
}
}

aggPubKey, err := v.scheme.AggregatePublicKeys(mask)
aggPubKey, err := a.scheme.AggregatePublicKeys(bdnMask)
if err != nil {
return fmt.Errorf("aggregating public keys: %w", err)
}

return v.scheme.Verify(aggPubKey, msg, signature)
return a.scheme.Verify(aggPubKey, msg, signature)
}

func (v *Verifier) pubkeysToMask(pubkeys []gpbft.PubKey) (*sign.Mask, error) {
func (v *Verifier) Aggregate(pubkeys []gpbft.PubKey) (_agg gpbft.Aggregate, _err error) {
defer func() {
if perr := recover(); perr != nil {
_err = fmt.Errorf("panicked aggregating public keys: %v\n%s",
perr, string(debug.Stack()))
log.Error(_err)
}
}()

kPubkeys := make([]kyber.Point, 0, len(pubkeys))
for i, p := range pubkeys {
point, err := v.pubkeyToPoint(p)
Expand All @@ -103,15 +120,12 @@ func (v *Verifier) pubkeysToMask(pubkeys []gpbft.PubKey) (*sign.Mask, error) {
kPubkeys = append(kPubkeys, point.Clone())
}

mask, err := sign.NewMask(v.suite, kPubkeys, nil)
mask, err := bdn.NewMask(v.keyGroup, kPubkeys, nil)
if err != nil {
return nil, fmt.Errorf("creating key mask: %w", err)
}
for i := range kPubkeys {
err := mask.SetBit(i, true)
if err != nil {
return nil, fmt.Errorf("setting mask bit %d: %w", i, err)
}
}
return mask, nil
return &aggregation{
mask: mask,
scheme: v.scheme,
}, nil
}
6 changes: 3 additions & 3 deletions blssig/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import (
"runtime"
"testing"

bls12381 "github.com/drand/kyber-bls12381"
"github.com/drand/kyber/sign/bdn"
"github.com/stretchr/testify/require"

"github.com/filecoin-project/go-f3/gpbft"
"github.com/filecoin-project/go-f3/internal/bls/bdn"
bls12381 "github.com/filecoin-project/go-f3/internal/bls/gnark"
)

const maxCacheMemory uint64 = 10 << 20 // 10MiB

func TestCacheMemory(t *testing.T) {
suite := bls12381.NewBLS12381Suite()
suite := bls12381.NewSuiteBLS12381()
scheme := bdn.NewSchemeOnG2(suite)

rand := suite.RandomStream()
Expand Down
8 changes: 4 additions & 4 deletions blssig/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"context"
"errors"

"github.com/drand/kyber"
bls12381 "github.com/drand/kyber-bls12381"
"github.com/drand/kyber/sign/bdn"
"github.com/filecoin-project/go-f3/gpbft"
"github.com/filecoin-project/go-f3/internal/bls/bdn"
bls12381 "github.com/filecoin-project/go-f3/internal/bls/gnark"
"go.dedis.ch/kyber/v4"
)

var _ gpbft.Signer = (*Signer)(nil)
Expand All @@ -21,7 +21,7 @@ type Signer struct {

func SignerWithKeyOnG1(pub gpbft.PubKey, privKey kyber.Scalar) *Signer {
return &Signer{
scheme: bdn.NewSchemeOnG2(bls12381.NewBLS12381Suite()),
scheme: bdn.NewSchemeOnG2(bls12381.NewSuiteBLS12381()),
pubKey: pub,
privKey: privKey,
}
Expand Down
11 changes: 4 additions & 7 deletions blssig/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@ import (
"runtime/debug"
"sync"

"github.com/drand/kyber"
bls12381 "github.com/drand/kyber-bls12381"
"github.com/drand/kyber/pairing"
"github.com/drand/kyber/sign/bdn"
"go.dedis.ch/kyber/v4"
"go.opentelemetry.io/otel/metric"

"github.com/filecoin-project/go-f3/gpbft"
"github.com/filecoin-project/go-f3/internal/bls/bdn"
bls12381 "github.com/filecoin-project/go-f3/internal/bls/gnark"
"github.com/filecoin-project/go-f3/internal/measurements"
)

type Verifier struct {
suite pairing.Suite
scheme *bdn.Scheme
keyGroup kyber.Group

Expand All @@ -27,9 +25,8 @@ type Verifier struct {
}

func VerifierWithKeyOnG1() *Verifier {
suite := bls12381.NewBLS12381Suite()
suite := bls12381.NewSuiteBLS12381()
return &Verifier{
suite: suite,
scheme: bdn.NewSchemeOnG2(suite),
keyGroup: suite.G1(),
}
Expand Down
6 changes: 3 additions & 3 deletions blssig/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import (
"context"
"testing"

bls12381 "github.com/drand/kyber-bls12381"
"github.com/drand/kyber/sign/bdn"
"github.com/filecoin-project/go-f3/internal/bls/bdn"
bls12381 "github.com/filecoin-project/go-f3/internal/bls/gnark"
"github.com/stretchr/testify/require"
)

func BenchmarkBLSSigning(b *testing.B) {
var (
blsSuit = bls12381.NewBLS12381Suite()
blsSuit = bls12381.NewSuiteBLS12381()
blsSchema = bdn.NewSchemeOnG2(blsSuit)
)
privKey, pubKey := blsSchema.NewKeyPair(blsSuit.RandomStream())
Expand Down
12 changes: 9 additions & 3 deletions certs/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ func verifyFinalityCertificateSignature(verifier gpbft.Verifier, powerTable gpbf
return fmt.Errorf("failed to scale power table: %w", err)
}

signers := make([]gpbft.PubKey, 0, len(powerTable))
keys := powerTable.PublicKeys()
mask := make([]int, 0, len(powerTable))
var signerPowers int64
if err := cert.Signers.ForEach(func(i uint64) error {
if i >= uint64(len(powerTable)) {
Expand All @@ -165,7 +166,7 @@ func verifyFinalityCertificateSignature(verifier gpbft.Verifier, powerTable gpbf
cert.GPBFTInstance, powerTable[i].ID)
}
signerPowers += power
signers = append(signers, powerTable[i].PubKey)
mask = append(mask, int(i))
return nil
}); err != nil {
return err
Expand All @@ -192,7 +193,12 @@ func verifyFinalityCertificateSignature(verifier gpbft.Verifier, powerTable gpbf
signedBytes = payload.MarshalForSigning(nn)
}

if err := verifier.VerifyAggregate(signedBytes, cert.Signature, signers); err != nil {
aggregate, err := verifier.Aggregate(keys)
if err != nil {
return err
}

if err := aggregate.VerifyAggregate(mask, signedBytes, cert.Signature); err != nil {
return fmt.Errorf("invalid signature on finality certificate for instance %d: %w", cert.GPBFTInstance, err)
}
return nil
Expand Down
5 changes: 3 additions & 2 deletions emulator/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ func (h *driverHost) GetCommittee(id uint64) (*gpbft.Committee, error) {
return nil, fmt.Errorf("instance ID %d not found", id)
}
return &gpbft.Committee{
PowerTable: instance.powerTable,
Beacon: instance.beacon,
PowerTable: instance.powerTable,
Beacon: instance.beacon,
AggregateVerifier: instance.aggregateVerifier,
}, nil
}

Expand Down
35 changes: 21 additions & 14 deletions emulator/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import (
// Instance represents a GPBFT instance capturing all the information necessary
// for GPBFT to function, along with the final decision reached if any.
type Instance struct {
t *testing.T
id uint64
supplementalData gpbft.SupplementalData
proposal gpbft.ECChain
powerTable *gpbft.PowerTable
beacon []byte
decision *gpbft.Justification
signing Signing
t *testing.T
id uint64
supplementalData gpbft.SupplementalData
proposal gpbft.ECChain
powerTable *gpbft.PowerTable
beacon []byte
decision *gpbft.Justification
signing Signing
aggregateVerifier gpbft.Aggregate
}

// NewInstance instantiates a new Instance for emulation. If absent, the
Expand Down Expand Up @@ -58,7 +59,8 @@ func NewInstance(t *testing.T, id uint64, powerEntries gpbft.PowerEntries, propo
}
proposalChain, err := gpbft.NewChain(proposal[0], proposal[1:]...)
require.NoError(t, err)
return &Instance{

i := &Instance{
t: t,
id: id,
powerTable: pt,
Expand All @@ -68,11 +70,18 @@ func NewInstance(t *testing.T, id uint64, powerEntries gpbft.PowerEntries, propo
Commitments: [32]byte{},
PowerTable: ptCid,
},
signing: AdhocSigning(),
}

i.SetSigning(AdhocSigning())
return i
}

func (i *Instance) SetSigning(signing Signing) { i.signing = signing }
func (i *Instance) SetSigning(signing Signing) {
var err error
i.signing = signing
i.aggregateVerifier, err = signing.Aggregate(i.powerTable.Entries.PublicKeys())
require.NoError(i.t, err)
}
func (i *Instance) Proposal() gpbft.ECChain { return i.proposal }
func (i *Instance) GetDecision() *gpbft.Justification { return i.decision }
func (i *Instance) ID() uint64 { return i.id }
Expand Down Expand Up @@ -140,7 +149,6 @@ func (i *Instance) NewJustificationWithPayload(payload gpbft.Payload, from ...gp
msg := i.signing.MarshalPayloadForSigning(networkName, &payload)
qr := gpbft.QuorumResult{
Signers: make([]int, len(from)),
PubKeys: make([]gpbft.PubKey, len(from)),
Signatures: make([][]byte, len(from)),
}
for j, actor := range from {
Expand All @@ -150,10 +158,9 @@ func (i *Instance) NewJustificationWithPayload(payload gpbft.Payload, from ...gp
signature, err := i.signing.Sign(context.Background(), entry.PubKey, msg)
require.NoError(i.t, err)
qr.Signatures[j] = signature
qr.PubKeys[j] = entry.PubKey
qr.Signers[j] = index
}
aggregate, err := i.signing.Aggregate(qr.PubKeys, qr.Signatures)
aggregate, err := i.aggregateVerifier.Aggregate(qr.Signers, qr.Signatures)
require.NoError(i.t, err)
return &gpbft.Justification{
Vote: payload,
Expand Down
Loading
Loading