Add blst for BLS (#6539)

* Add blst third party dep

* initial build

* add init

* blst passing tests

* add feature flag

* blst and herumi for spec tests

* maybe this works for mac

* Actually set feature flag

* Add stub for VerifyMultipleSignatures for blst

* verifyCompressed

* use correct cores sizes

* aggregate public keys

* add multi-sig verification

* encode not hash

* revert back

* go mod tidy

* update blst to latest commit

* add batch decompress

* fix

* add test

* gofmt

* update blst

* go mod tidy

* remove kubesec, fix

* mod tidy

* disable some remote cache

* disable some remote cache

* disable some remote cache

* disable some remote cache

* Switch to -D__ADX__

* update

* tidy

* fix build

* Make blst for only linux,amd64

* gofmt

* lint

* lint

* gazelle

* fix build tag

* more stub methods

* shift adx instructions to x86

* fix arm64

* Revert "fix arm64"

This reverts commit 4d34ac21b7509a1b385374e3039efecfcab614c1.

* add one more in

* Revert "Revert "fix arm64""

This reverts commit 1c8ae24ad16ff9811590f1058b9d98c90b63251a.

* try darwin now

* Revert "try darwin now"

This reverts commit 6f884714b8e14a7a803b72157672b6e942047f37.

* Add sha256

* remove TODO

* checkpoint

* finally builds

* fix up

* add tag

* try again

* explicit disabling

* remove

* select properly

* fix

* better

* make CI happy too

* Update .bazelrc

* Update .bazelrc

* fix tests

* revert back

* Update shared/bls/blst/public_key.go

Co-authored-by: Victor Farazdagi <simple.square@gmail.com>

* Update shared/bls/blst/public_key.go

Co-authored-by: Victor Farazdagi <simple.square@gmail.com>

* clean up tests

* more clean up

* clean up

* add

* Update shared/bls/blst/signature.go

* Update shared/bls/blst/signature.go

* Update .buildkite-bazelrc

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* try again

* remove go tag

* revert change

* gaz

* gazelle ignore

Co-authored-by: nisdas <nishdas93@gmail.com>
Co-authored-by: Victor Farazdagi <simple.square@gmail.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Preston Van Loon 2020-09-16 08:28:28 -05:00 committed by GitHub
parent b0917db4c7
commit 14dbc2b74d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1400 additions and 84 deletions

View File

@ -33,9 +33,16 @@ build --define kafka_enabled=false
test --define kafka_enabled=false
run --define kafka_enabled=false
# Enable blst by default, we use a config so that our fuzzer stops complaining.
build --config=blst_enabled
test --config=blst_enabled
run --config=blst_enabled
build:kafka_enabled --define kafka_enabled=true
build:kafka_enabled --define gotags=kafka_enabled
build:blst_enabled --define blst_enabled=true
# Release flags
build:release --workspace_status_command=./scripts/workspace_status.sh
build:release --stamp
@ -72,6 +79,7 @@ build:fuzz --linkopt -Wl,--no-as-needed
build:fuzz --define=gc_goopts=-d=libfuzzer,checkptr
build:fuzz --run_under=//tools:fuzz_wrapper
build:fuzz --compilation_mode=opt
build:fuzz --define=blst_enabled=false
test:fuzz --local_test_jobs="HOST_CPUS*.5"

View File

@ -37,6 +37,9 @@ build --flaky_test_attempts=5
# Enable kafka for CI tests only.
test --config=kafka_enabled
# Disable blst for CI tests.
test --define blst_enabled=true
build --bes_backend=grpcs://builds.prylabs.net:1985
build --bes_results_url=https://builds.prylabs.net/invocation/

View File

@ -43,7 +43,8 @@ jobs:
go get -v -t -d ./...
- name: Build
run: go build -v ./...
# Use blst tag to allow go and bazel builds for blst.
run: go build -v --tags=blst_enabled ./...
# Tests run via Bazel for now...
# - name: Test

View File

@ -231,7 +231,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []*ethpb.SignedBeaconBl
jCheckpoints := make([]*ethpb.Checkpoint, len(blks))
fCheckpoints := make([]*ethpb.Checkpoint, len(blks))
sigSet := &bls.SignatureSet{
Signatures: []bls.Signature{},
Signatures: [][]byte{},
PublicKeys: []bls.PublicKey{},
Messages: [][32]byte{},
}
@ -253,7 +253,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []*ethpb.SignedBeaconBl
fCheckpoints[i] = preState.FinalizedCheckpoint()
sigSet.Join(set)
}
verify, err := bls.VerifyMultipleSignatures(sigSet.Signatures, sigSet.Messages, sigSet.PublicKeys)
verify, err := sigSet.Verify()
if err != nil {
return nil, nil, err
}

View File

@ -336,7 +336,7 @@ func verifyAttestationsSigWithDomain(ctx context.Context, beaconState *stateTrie
if err != nil {
return err
}
verify, err := bls.VerifyMultipleSignatures(set.Signatures, set.Messages, set.PublicKeys)
verify, err := set.Verify()
if err != nil {
return errors.Errorf("got error in multiple verification: %v", err)
}

View File

@ -232,7 +232,7 @@ func verifyDepositDataWithDomain(ctx context.Context, deps []*ethpb.Deposit, dom
return nil
}
pks := make([]bls.PublicKey, len(deps))
sigs := make([]bls.Signature, len(deps))
sigs := make([][]byte, len(deps))
msgs := make([][32]byte, len(deps))
for i, dep := range deps {
if ctx.Err() != nil {
@ -241,17 +241,12 @@ func verifyDepositDataWithDomain(ctx context.Context, deps []*ethpb.Deposit, dom
if dep == nil || dep.Data == nil {
return errors.New("nil deposit")
}
dpk, err := bls.PublicKeyFromBytes(dep.Data.PublicKey)
if err != nil {
return err
}
pks[i] = dpk
dsig, err := bls.SignatureFromBytes(dep.Data.Signature)
if err != nil {
return err
}
sigs[i] = dsig
sigs[i] = dep.Data.Signature
root, err := ssz.SigningRoot(dep.Data)
if err != nil {
return errors.Wrap(err, "could not get signing root")

View File

@ -20,10 +20,6 @@ func retrieveSignatureSet(signedData []byte, pub []byte, signature []byte, domai
if err != nil {
return nil, errors.Wrap(err, "could not convert bytes to public key")
}
sig, err := bls.SignatureFromBytes(signature)
if err != nil {
return nil, errors.Wrap(err, "could not convert bytes to signature")
}
signingData := &pb.SigningData{
ObjectRoot: signedData,
Domain: domain,
@ -33,7 +29,7 @@ func retrieveSignatureSet(signedData []byte, pub []byte, signature []byte, domai
return nil, errors.Wrap(err, "could not hash container")
}
return &bls.SignatureSet{
Signatures: []bls.Signature{sig},
Signatures: [][]byte{signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
}, nil
@ -52,7 +48,11 @@ func verifySignature(signedData []byte, pub []byte, signature []byte, domain []b
sig := set.Signatures[0]
publicKey := set.PublicKeys[0]
root := set.Messages[0]
if !sig.Verify(publicKey, root[:]) {
rSig, err := bls.SignatureFromBytes(sig)
if err != nil {
return err
}
if !rSig.Verify(publicKey, root[:]) {
return helpers.ErrSigFailedToVerify
}
return nil
@ -129,15 +129,11 @@ func createAttestationSignatureSet(ctx context.Context, beaconState *stateTrie.B
return nil, nil
}
sigs := make([]bls.Signature, len(atts))
sigs := make([][]byte, len(atts))
pks := make([]bls.PublicKey, len(atts))
msgs := make([][32]byte, len(atts))
for i, a := range atts {
sig, err := bls.SignatureFromBytes(a.Signature)
if err != nil {
return nil, err
}
sigs[i] = sig
sigs[i] = a.Signature
c, err := helpers.BeaconCommitteeFromState(beaconState, a.Data.Slot, a.Data.CommitteeIndex)
if err != nil {
return nil, err
@ -147,20 +143,16 @@ func createAttestationSignatureSet(ctx context.Context, beaconState *stateTrie.B
return nil, err
}
indices := ia.AttestingIndices
var pk bls.PublicKey
pubkeys := make([][]byte, len(indices))
for i := 0; i < len(indices); i++ {
pubkeyAtIdx := beaconState.PubkeyAtIndex(indices[i])
p, err := bls.PublicKeyFromBytes(pubkeyAtIdx[:])
if err != nil {
return nil, errors.Wrap(err, "could not deserialize validator public key")
}
if pk == nil {
pk = p
} else {
pk.Aggregate(p)
}
pubkeys[i] = pubkeyAtIdx[:]
}
pks[i] = pk
aggP, err := bls.AggregatePublicKeys(pubkeys)
if err != nil {
return nil, err
}
pks[i] = aggP
root, err := helpers.ComputeSigningRoot(ia.Data, domain)
if err != nil {

View File

@ -117,7 +117,11 @@ func VerifyBlockSigningRoot(blk *ethpb.BeaconBlock, pub []byte, signature []byte
publicKey := set.PublicKeys[0]
root := set.Messages[0]
if !sig.Verify(publicKey, root[:]) {
rSig, err := bls.SignatureFromBytes(sig)
if err != nil {
return err
}
if !rSig.Verify(publicKey, root[:]) {
return ErrSigFailedToVerify
}
return nil
@ -130,10 +134,6 @@ func RetrieveBlockSignatureSet(blk *ethpb.BeaconBlock, pub []byte, signature []b
if err != nil {
return nil, errors.Wrap(err, "could not convert bytes to public key")
}
sig, err := bls.SignatureFromBytes(signature)
if err != nil {
return nil, errors.Wrap(err, "could not convert bytes to signature")
}
root, err := signingData(func() ([32]byte, error) {
// utilize custom block hashing function
return blk.HashTreeRoot()
@ -142,7 +142,7 @@ func RetrieveBlockSignatureSet(blk *ethpb.BeaconBlock, pub []byte, signature []b
return nil, errors.Wrap(err, "could not compute signing root")
}
return &bls.SignatureSet{
Signatures: []bls.Signature{sig},
Signatures: [][]byte{signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
}, nil

View File

@ -1,4 +1,5 @@
load("@prysm//tools/go:def.bzl", "go_repository", "maybe") # gazelle:keep
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # gazelle:keep
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Prysm's third party / external dependencies.
@ -2720,8 +2721,9 @@ def prysm_deps():
go_repository(
name = "org_golang_google_grpc",
importpath = "google.golang.org/grpc",
sum = "h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=",
version = "v1.29.1",
sum = "h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=",
version = "v1.30.0",
build_file_proto_mode = "disable_global",
)
go_repository(
name = "org_golang_x_crypto",
@ -2821,12 +2823,6 @@ def prysm_deps():
sum = "h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0=",
version = "v0.18.3",
)
go_repository(
name = "com_github_shyiko_kubesec",
importpath = "github.com/shyiko/kubesec",
sum = "h1:au/8ClCUc9HTpuGePltbhd98DZcsqZEyG9DoFILieR4=",
version = "v0.0.0-20190816030733-7ce23ac7239c",
)
go_repository(
name = "in_gopkg_confluentinc_confluent_kafka_go_v1",
importpath = "gopkg.in/confluentinc/confluent-kafka-go.v1",
@ -3436,6 +3432,15 @@ def prysm_deps():
sum = "h1:90Ly+6UfUypEF6vvvW5rQIv9opIL8CbmW9FT20LDQoY=",
version = "v0.0.0-20191129215211-8e5a1ed0cff0",
)
http_archive(
name = "com_github_supranational_blst",
urls = [
"https://github.com/supranational/blst/archive/d75dab4fb87df7fc753d348b17be671c69b625f5.tar.gz",
],
strip_prefix = "blst-d75dab4fb87df7fc753d348b17be671c69b625f5",
build_file = "//third_party:blst/blst.BUILD",
sha256 = "c541403e73fb0553a55d7ebfef6db695f48f18855fff1a87c7fd08f69827da51",
)
go_repository(
name = "com_github_nbutton23_zxcvbn_go",
importpath = "github.com/nbutton23/zxcvbn-go",

1
go.mod
View File

@ -96,6 +96,7 @@ require (
github.com/schollz/progressbar/v3 v3.3.4
github.com/sirupsen/logrus v1.6.0
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect
github.com/supranational/blst v0.1.2-alpha.1.0.20200828152217-d75dab4fb87d
github.com/tyler-smith/go-bip39 v1.0.2
github.com/urfave/cli/v2 v2.2.0
github.com/wealdtech/eth2-signer-api v1.3.0

2
go.sum
View File

@ -1097,6 +1097,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/supranational/blst v0.1.2-alpha.1.0.20200828152217-d75dab4fb87d h1:+SGrEOwpPjlaMcSDLI5p/nLCiEZegXK8aKoELNtCcZE=
github.com/supranational/blst v0.1.2-alpha.1.0.20200828152217-d75dab4fb87d/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=

View File

@ -11,7 +11,9 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/shared/bls",
visibility = ["//visibility:public"],
deps = [
"//shared/bls/blst:go_default_library",
"//shared/bls/herumi:go_default_library",
"//shared/bls/iface:go_default_library",
"//shared/featureconfig:go_default_library",
],
)

View File

@ -4,41 +4,98 @@
package bls
import (
"github.com/prysmaticlabs/prysm/shared/bls/blst"
"github.com/prysmaticlabs/prysm/shared/bls/herumi"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
)
// SecretKeyFromBytes creates a BLS private key from a BigEndian byte slice.
func SecretKeyFromBytes(privKey []byte) (SecretKey, error) {
if featureconfig.Get().EnableBlst {
return blst.SecretKeyFromBytes(privKey)
}
return herumi.SecretKeyFromBytes(privKey)
}
// PublicKeyFromBytes creates a BLS public key from a BigEndian byte slice.
func PublicKeyFromBytes(pubKey []byte) (PublicKey, error) {
if featureconfig.Get().EnableBlst {
return blst.PublicKeyFromBytes(pubKey)
}
return herumi.PublicKeyFromBytes(pubKey)
}
// SignatureFromBytes creates a BLS signature from a LittleEndian byte slice.
func SignatureFromBytes(sig []byte) (Signature, error) {
if featureconfig.Get().EnableBlst {
return blst.SignatureFromBytes(sig)
}
return herumi.SignatureFromBytes(sig)
}
// AggregatePublicKeys aggregates the provided raw public keys into a single key.
func AggregatePublicKeys(pubs [][]byte) (PublicKey, error) {
if featureconfig.Get().EnableBlst {
return blst.AggregatePublicKeys(pubs)
}
return herumi.AggregatePublicKeys(pubs)
}
// AggregateSignatures converts a list of signatures into a single, aggregated sig.
func AggregateSignatures(sigs []iface.Signature) iface.Signature {
if featureconfig.Get().EnableBlst {
return blst.AggregateSignatures(sigs)
}
return herumi.AggregateSignatures(sigs)
}
// VerifyMultipleSignatures verifies multiple signatures for distinct messages securely.
func VerifyMultipleSignatures(sigs []iface.Signature, msgs [][32]byte, pubKeys []iface.PublicKey) (bool, error) {
return herumi.VerifyMultipleSignatures(sigs, msgs, pubKeys)
func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []iface.PublicKey) (bool, error) {
if featureconfig.Get().EnableBlst {
return blst.VerifyMultipleSignatures(sigs, msgs, pubKeys)
}
// Manually decompress each signature as herumi does not
// have a batch decompress method.
rawSigs := make([]Signature, len(sigs))
var err error
for i, s := range sigs {
rawSigs[i], err = herumi.SignatureFromBytes(s)
if err != nil {
return false, err
}
}
return herumi.VerifyMultipleSignatures(rawSigs, msgs, pubKeys)
}
// NewAggregateSignature creates a blank aggregate signature.
func NewAggregateSignature() iface.Signature {
if featureconfig.Get().EnableBlst {
return blst.NewAggregateSignature()
}
return herumi.NewAggregateSignature()
}
// RandKey creates a new private key using a random input.
func RandKey() iface.SecretKey {
if featureconfig.Get().EnableBlst {
return blst.RandKey()
}
return herumi.RandKey()
}
// VerifyCompressed signature.
func VerifyCompressed(signature []byte, pub []byte, msg []byte) bool {
if featureconfig.Get().EnableBlst {
return blst.VerifyCompressed(signature, pub, msg)
}
sig, err := SignatureFromBytes(signature)
if err != nil {
return false
}
pk, err := PublicKeyFromBytes(pub)
if err != nil {
return false
}
return sig.Verify(pk, msg)
}

151
shared/bls/blst/BUILD.bazel Normal file
View File

@ -0,0 +1,151 @@
load("@io_bazel_rules_go//go:def.bzl", "go_test")
load("@bazel_skylib//lib:selects.bzl", "selects")
load("@prysm//tools/go:def.bzl", "go_library")
# Build with --define=blst_enabled=false to exclude blst library.
config_setting(
name = "blst_enabled_linux_amd64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
values = {
"define": "blst_enabled=true",
},
)
config_setting(
name = "blst_enabled_linux_arm64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:aarch64",
],
values = {
"define": "blst_enabled=true",
},
)
config_setting(
name = "blst_enabled_android_amd64",
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:x86_64",
],
values = {
"define": "blst_enabled=true",
},
)
config_setting(
name = "blst_enabled_android_arm64",
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:aarch64",
],
values = {
"define": "blst_enabled=true",
},
)
# gazelle:resolve go github.com/supranational/blst/bindings/go @com_github_supranational_blst//:go_default_library
go_library(
name = "go_default_library",
srcs =
selects.with_or({
(
":blst_enabled_linux_amd64",
":blst_enabled_linux_arm64",
":blst_enabled_android_amd64",
":blst_enabled_android_arm64",
): [
"aliases.go",
"doc.go",
"init.go",
"public_key.go",
"secret_key.go",
"signature.go",
],
"//conditions:default": [
"stub.go",
],
}),
importpath = "github.com/prysmaticlabs/prysm/shared/bls/blst",
visibility = [
"//shared/bls:__pkg__",
],
deps = selects.with_or({
(
":blst_enabled_linux_amd64",
":blst_enabled_linux_arm64",
":blst_enabled_android_amd64",
":blst_enabled_android_arm64",
): [
"//shared/bls/iface:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/params:go_default_library",
"//shared/rand:go_default_library",
"@com_github_dgraph_io_ristretto//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_supranational_blst//:go_default_library",
],
"//conditions:default": ["//shared/bls/iface:go_default_library"],
}),
)
# gazelle:ignore
go_test(
name = "go_default_test",
srcs = selects.with_or({
(
":blst_enabled_linux_amd64",
":blst_enabled_linux_arm64",
":blst_enabled_android_amd64",
":blst_enabled_android_arm64",
): [
"public_key_test.go",
"secret_key_test.go",
"signature_test.go",
],
"//conditions:default": [],
}),
deps = selects.with_or({
(
":blst_enabled_linux_amd64",
":blst_enabled_linux_arm64",
":blst_enabled_android_amd64",
":blst_enabled_android_arm64",
): [
"//shared/bls/blst:go_default_library",
"//shared/bls/iface:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/testutil/assert:go_default_library",
"//shared/testutil/require:go_default_library",
],
"//conditions:default": [],
}),
)
# gazelle:exclude bls_benchmark_test.go
go_test(
name = "go_benchmark_test",
size = "small",
srcs = ["bls_benchmark_test.go"],
args = [
"-test.bench=.",
"-test.benchmem",
"-test.v",
],
local = True,
tags = [
"benchmark",
"manual",
"no-cache",
],
deps = [
":go_default_library",
"//shared/bls/iface:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
],
)

View File

@ -0,0 +1,11 @@
// +build linux,amd64 linux,arm64
package blst
import blst "github.com/supranational/blst/bindings/go"
// Internal types for blst.
type blstPublicKey = blst.P1Affine
type blstSignature = blst.P2Affine
type blstAggregateSignature = blst.P2Aggregate
type blstAggregatePublicKey = blst.P1Aggregate

View File

@ -0,0 +1,91 @@
// +build linux,amd64 linux,arm64
package blst_test
import (
"testing"
"github.com/herumi/bls-eth-go-binary/bls"
"github.com/prysmaticlabs/prysm/shared/bls/herumi"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/hashutil"
)
func BenchmarkPairing(b *testing.B) {
if err := bls.Init(bls.BLS12_381); err != nil {
b.Fatal(err)
}
if err := bls.SetETHmode(bls.EthModeDraft07); err != nil {
panic(err)
}
newGt := &bls.GT{}
newG1 := &bls.G1{}
newG2 := &bls.G2{}
newGt.SetInt64(10)
hash := hashutil.Hash([]byte{})
err := newG1.HashAndMapTo(hash[:])
if err != nil {
b.Fatal(err)
}
err = newG2.HashAndMapTo(hash[:])
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bls.Pairing(newGt, newG1, newG2)
}
}
func BenchmarkSignature_Verify(b *testing.B) {
sk := herumi.RandKey()
msg := []byte("Some msg")
sig := sk.Sign(msg)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !sig.Verify(sk.PublicKey(), msg) {
b.Fatal("could not verify sig")
}
}
}
func BenchmarkSignature_AggregateVerify(b *testing.B) {
sigN := 128 // MAX_ATTESTATIONS per block.
var pks []iface.PublicKey
var sigs []iface.Signature
var msgs [][32]byte
for i := 0; i < sigN; i++ {
msg := [32]byte{'s', 'i', 'g', 'n', 'e', 'd', byte(i)}
sk := herumi.RandKey()
sig := sk.Sign(msg[:])
pks = append(pks, sk.PublicKey())
sigs = append(sigs, sig)
msgs = append(msgs, msg)
}
aggregated := herumi.Aggregate(sigs)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if !aggregated.AggregateVerify(pks, msgs) {
b.Fatal("could not verify aggregate sig")
}
}
}
func BenchmarkSecretKey_Marshal(b *testing.B) {
key := herumi.RandKey()
d := key.Marshal()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := herumi.SecretKeyFromBytes(d)
_ = err
}
}

8
shared/bls/blst/doc.go Normal file
View File

@ -0,0 +1,8 @@
// Package blst implements a go-wrapper around a library implementing the
// the BLS12-381 curve and signature scheme. This package exposes a public API for
// verifying and aggregating BLS signatures used by Ethereum 2.0.
//
// This implementation uses the library written by Supranational, blst.
//
// Only linux_amd64 is supported at the moment.
package blst

18
shared/bls/blst/init.go Normal file
View File

@ -0,0 +1,18 @@
// +build linux,amd64 linux,arm64
package blst
import (
"runtime"
blst "github.com/supranational/blst/bindings/go"
)
func init() {
// Reserve 1 core for general application work
maxProcs := runtime.GOMAXPROCS(0) - 1
if maxProcs <= 0 {
maxProcs = 1
}
blst.SetMaxProcs(maxProcs)
}

View File

@ -0,0 +1,99 @@
// +build linux,amd64 linux,arm64
package blst
import (
"fmt"
"github.com/dgraph-io/ristretto"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
)
var maxKeys = int64(100000)
var pubkeyCache, _ = ristretto.NewCache(&ristretto.Config{
NumCounters: maxKeys,
MaxCost: 1 << 22, // ~4mb is cache max size
BufferItems: 64,
})
// PublicKey used in the BLS signature scheme.
type PublicKey struct {
p *blstPublicKey
}
// PublicKeyFromBytes creates a BLS public key from a BigEndian byte slice.
func PublicKeyFromBytes(pubKey []byte) (iface.PublicKey, error) {
if featureconfig.Get().SkipBLSVerify {
return &PublicKey{}, nil
}
if len(pubKey) != params.BeaconConfig().BLSPubkeyLength {
return nil, fmt.Errorf("public key must be %d bytes", params.BeaconConfig().BLSPubkeyLength)
}
if cv, ok := pubkeyCache.Get(string(pubKey)); ok {
return cv.(*PublicKey).Copy(), nil
}
p := new(blstPublicKey).Uncompress(pubKey)
if p == nil {
return nil, errors.New("could not unmarshal bytes into public key")
}
pubKeyObj := &PublicKey{p: p}
copiedKey := pubKeyObj.Copy()
pubkeyCache.Set(string(pubKey), copiedKey, 48)
return pubKeyObj, nil
}
// AggregatePublicKeys aggregates the provided raw public keys into a single key.
func AggregatePublicKeys(pubs [][]byte) (iface.PublicKey, error) {
if featureconfig.Get().SkipBLSVerify {
return &PublicKey{}, nil
}
agg := new(blstAggregatePublicKey)
mulP1 := make([]*blstPublicKey, 0, len(pubs))
for _, pubkey := range pubs {
if len(pubkey) != params.BeaconConfig().BLSPubkeyLength {
return nil, fmt.Errorf("public key must be %d bytes", params.BeaconConfig().BLSPubkeyLength)
}
if cv, ok := pubkeyCache.Get(string(pubkey)); ok {
mulP1 = append(mulP1, cv.(*PublicKey).Copy().(*PublicKey).p)
continue
}
p := new(blstPublicKey).Uncompress(pubkey)
if p == nil {
return nil, errors.New("could not unmarshal bytes into public key")
}
pubKeyObj := &PublicKey{p: p}
pubkeyCache.Set(string(pubkey), pubKeyObj.Copy(), 48)
mulP1 = append(mulP1, p)
}
agg.Aggregate(mulP1)
return &PublicKey{p: agg.ToAffine()}, nil
}
// Marshal a public key into a LittleEndian byte slice.
func (p *PublicKey) Marshal() []byte {
return p.p.Compress()
}
// Copy the public key to a new pointer reference.
func (p *PublicKey) Copy() iface.PublicKey {
np := *p.p
return &PublicKey{p: &np}
}
// Aggregate two public keys.
func (p *PublicKey) Aggregate(p2 iface.PublicKey) iface.PublicKey {
if featureconfig.Get().SkipBLSVerify {
return p
}
agg := new(blstAggregatePublicKey)
agg.Add(p.p)
agg.Add(p2.(*PublicKey).p)
p.p = agg.ToAffine()
return p
}

View File

@ -0,0 +1,73 @@
// +build linux,amd64 linux,arm64
package blst_test
import (
"bytes"
"errors"
"testing"
"github.com/prysmaticlabs/prysm/shared/bls/blst"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
func TestPublicKeyFromBytes(t *testing.T) {
tests := []struct {
name string
input []byte
err error
}{
{
name: "Nil",
err: errors.New("public key must be 48 bytes"),
},
{
name: "Empty",
input: []byte{},
err: errors.New("public key must be 48 bytes"),
},
{
name: "Short",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("public key must be 48 bytes"),
},
{
name: "Long",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("public key must be 48 bytes"),
},
{
name: "Bad",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("could not unmarshal bytes into public key"),
},
{
name: "Good",
input: []byte{0xa9, 0x9a, 0x76, 0xed, 0x77, 0x96, 0xf7, 0xbe, 0x22, 0xd5, 0xb7, 0xe8, 0x5d, 0xee, 0xb7, 0xc5, 0x67, 0x7e, 0x88, 0xe5, 0x11, 0xe0, 0xb3, 0x37, 0x61, 0x8f, 0x8c, 0x4e, 0xb6, 0x13, 0x49, 0xb4, 0xbf, 0x2d, 0x15, 0x3f, 0x64, 0x9f, 0x7b, 0x53, 0x35, 0x9f, 0xe8, 0xb9, 0x4a, 0x38, 0xe4, 0x4c},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
res, err := blst.PublicKeyFromBytes(test.input)
if test.err != nil {
assert.NotEqual(t, nil, err, "No error returned")
assert.ErrorContains(t, test.err.Error(), err, "Unexpected error returned")
} else {
assert.NoError(t, err)
assert.DeepEqual(t, 0, bytes.Compare(res.Marshal(), test.input))
}
})
}
}
func TestPublicKey_Copy(t *testing.T) {
pubkeyA := blst.RandKey().PublicKey()
pubkeyBytes := pubkeyA.Marshal()
pubkeyB := pubkeyA.Copy()
pubkeyB.Aggregate(blst.RandKey().PublicKey())
require.DeepEqual(t, pubkeyA.Marshal(), pubkeyBytes, "Pubkey was mutated after copy")
}

View File

@ -0,0 +1,75 @@
// +build linux,amd64 linux,arm64
package blst
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
blst "github.com/supranational/blst/bindings/go"
)
// bls12SecretKey used in the BLS signature scheme.
type bls12SecretKey struct {
p *blst.SecretKey
}
// RandKey creates a new private key using a random method provided as an io.Reader.
func RandKey() iface.SecretKey {
// Generate 32 bytes of randomness
var ikm [32]byte
_, err := rand.NewGenerator().Read(ikm[:])
if err != nil {
return nil
}
return &bls12SecretKey{blst.KeyGen(ikm[:])}
}
// SecretKeyFromBytes creates a BLS private key from a BigEndian byte slice.
func SecretKeyFromBytes(privKey []byte) (iface.SecretKey, error) {
if len(privKey) != params.BeaconConfig().BLSSecretKeyLength {
return nil, fmt.Errorf("secret key must be %d bytes", params.BeaconConfig().BLSSecretKeyLength)
}
secKey := new(blst.SecretKey).Deserialize(privKey)
if secKey == nil {
return nil, errors.New("could not unmarshal bytes into secret key")
}
return &bls12SecretKey{p: secKey}, nil
}
// PublicKey obtains the public key corresponding to the BLS secret key.
func (s *bls12SecretKey) PublicKey() iface.PublicKey {
return &PublicKey{p: new(blstPublicKey).From(s.p)}
}
// Sign a message using a secret key - in a beacon/validator client.
//
// In IETF draft BLS specification:
// Sign(SK, message) -> signature: a signing algorithm that generates
// a deterministic signature given a secret key SK and a message.
//
// In ETH2.0 specification:
// def Sign(SK: int, message: Bytes) -> BLSSignature
func (s *bls12SecretKey) Sign(msg []byte) iface.Signature {
if featureconfig.Get().SkipBLSVerify {
return &Signature{}
}
signature := new(blstSignature).Sign(s.p, msg, dst)
return &Signature{s: signature}
}
// Marshal a secret key into a LittleEndian byte slice.
func (s *bls12SecretKey) Marshal() []byte {
keyBytes := s.p.Serialize()
if len(keyBytes) < params.BeaconConfig().BLSSecretKeyLength {
emptyBytes := make([]byte, params.BeaconConfig().BLSSecretKeyLength-len(keyBytes))
keyBytes = append(emptyBytes, keyBytes...)
}
return keyBytes
}

View File

@ -0,0 +1,82 @@
// +build linux,amd64 linux,arm64
package blst_test
import (
"bytes"
"errors"
"testing"
"github.com/prysmaticlabs/prysm/shared/bls/blst"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
func TestMarshalUnmarshal(t *testing.T) {
b := blst.RandKey().Marshal()
b32 := bytesutil.ToBytes32(b)
pk, err := blst.SecretKeyFromBytes(b32[:])
require.NoError(t, err)
pk2, err := blst.SecretKeyFromBytes(b32[:])
require.NoError(t, err)
assert.DeepEqual(t, pk.Marshal(), pk2.Marshal(), "Keys not equal")
}
func TestSecretKeyFromBytes(t *testing.T) {
tests := []struct {
name string
input []byte
err error
}{
{
name: "Nil",
err: errors.New("secret key must be 32 bytes"),
},
{
name: "Empty",
input: []byte{},
err: errors.New("secret key must be 32 bytes"),
},
{
name: "Short",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("secret key must be 32 bytes"),
},
{
name: "Long",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("secret key must be 32 bytes"),
},
{
name: "Bad",
input: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
err: errors.New("could not unmarshal bytes into secret key"),
},
{
name: "Good",
input: []byte{0x25, 0x29, 0x5f, 0x0d, 0x1d, 0x59, 0x2a, 0x90, 0xb3, 0x33, 0xe2, 0x6e, 0x85, 0x14, 0x97, 0x08, 0x20, 0x8e, 0x9f, 0x8e, 0x8b, 0xc1, 0x8f, 0x6c, 0x77, 0xbd, 0x62, 0xf8, 0xad, 0x7a, 0x68, 0x66},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
res, err := blst.SecretKeyFromBytes(test.input)
if test.err != nil {
assert.NotEqual(t, nil, err, "No error returned")
assert.ErrorContains(t, test.err.Error(), err, "Unexpected error returned")
} else {
assert.NoError(t, err)
assert.DeepEqual(t, 0, bytes.Compare(res.Marshal(), test.input))
}
})
}
}
func TestSerialize(t *testing.T) {
rk := blst.RandKey()
b := rk.Marshal()
_, err := blst.SecretKeyFromBytes(b)
assert.NoError(t, err)
}

View File

@ -0,0 +1,215 @@
// +build linux,amd64 linux,arm64
package blst
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
blst "github.com/supranational/blst/bindings/go"
)
var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_")
const scalarBytes = 32
const randBitsEntropy = 64
// Signature used in the BLS signature scheme.
type Signature struct {
s *blstSignature
}
// SignatureFromBytes creates a BLS signature from a LittleEndian byte slice.
func SignatureFromBytes(sig []byte) (iface.Signature, error) {
if featureconfig.Get().SkipBLSVerify {
return &Signature{}, nil
}
if len(sig) != params.BeaconConfig().BLSSignatureLength {
return nil, fmt.Errorf("signature must be %d bytes", params.BeaconConfig().BLSSignatureLength)
}
signature := new(blstSignature).Uncompress(sig)
if signature == nil {
return nil, errors.New("could not unmarshal bytes into signature")
}
return &Signature{s: signature}, nil
}
// Verify a bls signature given a public key, a message.
//
// In IETF draft BLS specification:
// Verify(PK, message, signature) -> VALID or INVALID: a verification
// algorithm that outputs VALID if signature is a valid signature of
// message under public key PK, and INVALID otherwise.
//
// In ETH2.0 specification:
// def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool
func (s *Signature) Verify(pubKey iface.PublicKey, msg []byte) bool {
if featureconfig.Get().SkipBLSVerify {
return true
}
return s.s.Verify(pubKey.(*PublicKey).p, msg, dst)
}
// AggregateVerify verifies each public key against its respective message.
// This is vulnerable to rogue public-key attack. Each user must
// provide a proof-of-knowledge of the public key.
//
// In IETF draft BLS specification:
// AggregateVerify((PK_1, message_1), ..., (PK_n, message_n),
// signature) -> VALID or INVALID: an aggregate verification
// algorithm that outputs VALID if signature is a valid aggregated
// signature for a collection of public keys and messages, and
// outputs INVALID otherwise.
//
// In ETH2.0 specification:
// def AggregateVerify(pairs: Sequence[PK: BLSPubkey, message: Bytes], signature: BLSSignature) -> boo
func (s *Signature) AggregateVerify(pubKeys []iface.PublicKey, msgs [][32]byte) bool {
if featureconfig.Get().SkipBLSVerify {
return true
}
size := len(pubKeys)
if size == 0 {
return false
}
if size != len(msgs) {
return false
}
msgSlices := make([][]byte, len(msgs))
rawKeys := make([]*blstPublicKey, len(msgs))
for i := 0; i < size; i++ {
msgSlices[i] = msgs[i][:]
rawKeys[i] = pubKeys[i].(*PublicKey).p
}
return s.s.AggregateVerify(rawKeys, msgSlices, dst)
}
// FastAggregateVerify verifies all the provided public keys with their aggregated signature.
//
// In IETF draft BLS specification:
// FastAggregateVerify(PK_1, ..., PK_n, message, signature) -> VALID
// or INVALID: a verification algorithm for the aggregate of multiple
// signatures on the same message. This function is faster than
// AggregateVerify.
//
// In ETH2.0 specification:
// def FastAggregateVerify(PKs: Sequence[BLSPubkey], message: Bytes, signature: BLSSignature) -> bool
func (s *Signature) FastAggregateVerify(pubKeys []iface.PublicKey, msg [32]byte) bool {
if featureconfig.Get().SkipBLSVerify {
return true
}
if len(pubKeys) == 0 {
return false
}
rawKeys := make([]*blstPublicKey, len(pubKeys))
for i := 0; i < len(pubKeys); i++ {
rawKeys[i] = pubKeys[i].(*PublicKey).p
}
return s.s.FastAggregateVerify(rawKeys, msg[:], dst)
}
// NewAggregateSignature creates a blank aggregate signature.
func NewAggregateSignature() iface.Signature {
sig := blst.HashToG2([]byte{'m', 'o', 'c', 'k'}, dst).ToAffine()
return &Signature{s: sig}
}
// AggregateSignatures converts a list of signatures into a single, aggregated sig.
func AggregateSignatures(sigs []iface.Signature) iface.Signature {
if len(sigs) == 0 {
return nil
}
if featureconfig.Get().SkipBLSVerify {
return sigs[0]
}
rawSigs := make([]*blstSignature, len(sigs))
for i := 0; i < len(sigs); i++ {
rawSigs[i] = sigs[i].(*Signature).s
}
signature := new(blstAggregateSignature).Aggregate(rawSigs)
if signature == nil {
return nil
}
return &Signature{s: signature.ToAffine()}
}
// Aggregate is an alias for AggregateSignatures, defined to conform to BLS specification.
//
// In IETF draft BLS specification:
// Aggregate(signature_1, ..., signature_n) -> signature: an
// aggregation algorithm that compresses a collection of signatures
// into a single signature.
//
// In ETH2.0 specification:
// def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature
//
// Deprecated: Use AggregateSignatures.
func Aggregate(sigs []iface.Signature) iface.Signature {
return AggregateSignatures(sigs)
}
// VerifyMultipleSignatures verifies a non-singular set of signatures and its respective pubkeys and messages.
// This method provides a safe way to verify multiple signatures at once. We pick a number randomly from 1 to max
// uint64 and then multiply the signature by it. We continue doing this for all signatures and its respective pubkeys.
// S* = S_1 * r_1 + S_2 * r_2 + ... + S_n * r_n
// P'_{i,j} = P_{i,j} * r_i
// e(S*, G) = \prod_{i=1}^n \prod_{j=1}^{m_i} e(P'_{i,j}, M_{i,j})
// Using this we can verify multiple signatures safely.
func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []iface.PublicKey) (bool, error) {
if featureconfig.Get().SkipBLSVerify {
return true, nil
}
if len(sigs) == 0 || len(pubKeys) == 0 {
return false, nil
}
rawSigs := new(blstSignature).BatchUncompress(sigs)
length := len(sigs)
if length != len(pubKeys) || length != len(msgs) {
return false, errors.Errorf("provided signatures, pubkeys and messages have differing lengths. S: %d, P: %d,M %d",
length, len(pubKeys), len(msgs))
}
mulP1Aff := make([]*blstPublicKey, length)
rawMsgs := make([]blst.Message, length)
for i := 0; i < length; i++ {
mulP1Aff[i] = pubKeys[i].(*PublicKey).p
rawMsgs[i] = msgs[i][:]
}
// Secure source of RNG
randGen := rand.NewGenerator()
randFunc := func(scalar *blst.Scalar) {
var rbytes [scalarBytes]byte
randGen.Read(rbytes[:])
scalar.FromBEndian(rbytes[:])
}
dummySig := new(blstSignature)
return dummySig.MultipleAggregateVerify(rawSigs, mulP1Aff, rawMsgs, dst, randFunc, randBitsEntropy), nil
}
// Marshal a signature into a LittleEndian byte slice.
func (s *Signature) Marshal() []byte {
if featureconfig.Get().SkipBLSVerify {
return make([]byte, params.BeaconConfig().BLSSignatureLength)
}
return s.s.Compress()
}
// Copy returns a full deep copy of a signature.
func (s *Signature) Copy() iface.Signature {
return &Signature{s: &*s.s}
}
// VerifyCompressed verifies that the compressed signature and pubkey
// are valid from the message provided.
func VerifyCompressed(signature []byte, pub []byte, msg []byte) bool {
return new(blstSignature).VerifyCompressed(signature, pub, msg, dst)
}

View File

@ -0,0 +1,139 @@
// +build linux,amd64 linux,arm64
package blst_test
import (
"bytes"
"errors"
"testing"
"github.com/prysmaticlabs/prysm/shared/bls/blst"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
)
func TestSignVerify(t *testing.T) {
priv := blst.RandKey()
pub := priv.PublicKey()
msg := []byte("hello")
sig := priv.Sign(msg)
assert.Equal(t, true, sig.Verify(pub, msg), "Signature did not verify")
}
func TestAggregateVerify(t *testing.T) {
pubkeys := make([]iface.PublicKey, 0, 100)
sigs := make([]iface.Signature, 0, 100)
var msgs [][32]byte
for i := 0; i < 100; i++ {
msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
priv := blst.RandKey()
pub := priv.PublicKey()
sig := priv.Sign(msg[:])
pubkeys = append(pubkeys, pub)
sigs = append(sigs, sig)
msgs = append(msgs, msg)
}
aggSig := blst.Aggregate(sigs)
assert.Equal(t, true, aggSig.AggregateVerify(pubkeys, msgs), "Signature did not verify")
}
func TestFastAggregateVerify(t *testing.T) {
pubkeys := make([]iface.PublicKey, 0, 100)
sigs := make([]iface.Signature, 0, 100)
msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
for i := 0; i < 100; i++ {
priv := blst.RandKey()
pub := priv.PublicKey()
sig := priv.Sign(msg[:])
pubkeys = append(pubkeys, pub)
sigs = append(sigs, sig)
}
aggSig := blst.AggregateSignatures(sigs)
assert.Equal(t, true, aggSig.FastAggregateVerify(pubkeys, msg), "Signature did not verify")
}
func TestVerifyCompressed(t *testing.T) {
priv := blst.RandKey()
pub := priv.PublicKey()
msg := []byte("hello")
sig := priv.Sign(msg)
assert.Equal(t, true, sig.Verify(pub, msg), "Non compressed signature did not verify")
assert.Equal(t, true, blst.VerifyCompressed(sig.Marshal(), pub.Marshal(), msg), "Compressed signatures and pubkeys did not verify")
}
func TestMultipleSignatureVerification(t *testing.T) {
pubkeys := make([]iface.PublicKey, 0, 100)
sigs := make([][]byte, 0, 100)
var msgs [][32]byte
for i := 0; i < 100; i++ {
msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
priv := blst.RandKey()
pub := priv.PublicKey()
sig := priv.Sign(msg[:]).Marshal()
pubkeys = append(pubkeys, pub)
sigs = append(sigs, sig)
msgs = append(msgs, msg)
}
verify, err := blst.VerifyMultipleSignatures(sigs, msgs, pubkeys)
assert.NoError(t, err, "Signature did not verify")
assert.Equal(t, true, verify, "Signature did not verify")
}
func TestFastAggregateVerify_ReturnsFalseOnEmptyPubKeyList(t *testing.T) {
var pubkeys []iface.PublicKey
msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
aggSig := blst.NewAggregateSignature()
assert.Equal(t, false, aggSig.FastAggregateVerify(pubkeys, msg), "Expected FastAggregateVerify to return false with empty input ")
}
func TestSignatureFromBytes(t *testing.T) {
tests := []struct {
name string
input []byte
err error
}{
{
name: "Nil",
err: errors.New("signature must be 96 bytes"),
},
{
name: "Empty",
input: []byte{},
err: errors.New("signature must be 96 bytes"),
},
{
name: "Short",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("signature must be 96 bytes"),
},
{
name: "Long",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("signature must be 96 bytes"),
},
{
name: "Bad",
input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
err: errors.New("could not unmarshal bytes into signature"),
},
{
name: "Good",
input: []byte{0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
res, err := blst.SignatureFromBytes(test.input)
if test.err != nil {
assert.NotEqual(t, nil, err, "No error returned")
assert.ErrorContains(t, test.err.Error(), err, "Unexpected error returned")
} else {
assert.NoError(t, err)
assert.DeepEqual(t, 0, bytes.Compare(res.Marshal(), test.input))
}
})
}
}

114
shared/bls/blst/stub.go Normal file
View File

@ -0,0 +1,114 @@
// +build darwin,amd64 windows,amd64 linux,amd64,!blst_enabled linux,arm64,!blst_enabled
package blst
import "github.com/prysmaticlabs/prysm/shared/bls/iface"
// SecretKey -- stub
type SecretKey struct{}
// PublicKey -- stub
func (s SecretKey) PublicKey() iface.PublicKey {
panic("blst is only supported on linux amd64")
}
// Sign -- stub
func (s SecretKey) Sign(msg []byte) iface.Signature {
panic("blst is only supported on linux amd64")
}
// Marshal -- stub
func (s SecretKey) Marshal() []byte {
panic("blst is only supported on linux amd64")
}
// PublicKey -- stub
type PublicKey struct{}
// Marshal -- stub
func (p PublicKey) Marshal() []byte {
panic("blst is only supported on linux amd64")
}
// Copy -- stub
func (p PublicKey) Copy() iface.PublicKey {
panic("blst is only supported on linux amd64")
}
// Aggregate -- stub
func (p PublicKey) Aggregate(p2 iface.PublicKey) iface.PublicKey {
panic("blst is only supported on linux amd64")
}
// Signature -- stub
type Signature struct{}
// Verify -- stub
func (s Signature) Verify(pubKey iface.PublicKey, msg []byte) bool {
panic("blst is only supported on linux amd64")
}
// AggregateVerify -- stub
func (s Signature) AggregateVerify(pubKeys []iface.PublicKey, msgs [][32]byte) bool {
panic("blst is only supported on linux amd64")
}
// FastAggregateVerify -- stub
func (s Signature) FastAggregateVerify(pubKeys []iface.PublicKey, msg [32]byte) bool {
panic("blst is only supported on linux amd64")
}
// Marshal -- stub
func (s Signature) Marshal() []byte {
panic("blst is only supported on linux amd64")
}
// Copy -- stub
func (s Signature) Copy() iface.Signature {
panic("blst is only supported on linux amd64")
}
// SecretKeyFromBytes -- stub
func SecretKeyFromBytes(privKey []byte) (SecretKey, error) {
panic("blst is only supported on linux amd64")
}
// PublicKeyFromBytes -- stub
func PublicKeyFromBytes(pubKey []byte) (PublicKey, error) {
panic("blst is only supported on linux amd64")
}
// SignatureFromBytes -- stub
func SignatureFromBytes(sig []byte) (Signature, error) {
panic("blst is only supported on linux amd64")
}
// AggregatePublicKeys -- stub
func AggregatePublicKeys(pubs [][]byte) (PublicKey, error) {
panic("blst is only supported on linux amd64")
}
// AggregateSignatures -- stub
func AggregateSignatures(sigs []iface.Signature) iface.Signature {
panic("blst is only supported on linux amd64")
}
// VerifyMultipleSignatures -- stub
func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []iface.PublicKey) (bool, error) {
panic("blst is only supported on linux amd64")
}
// NewAggregateSignature -- stub
func NewAggregateSignature() iface.Signature {
panic("blst is only supported on linux amd64")
}
// RandKey -- stub
func RandKey() iface.SecretKey {
panic("blst is only supported on linux amd64")
}
// VerifyCompressed -- stub
func VerifyCompressed(signature []byte, pub []byte, msg []byte) bool {
panic("blst is only supported on linux amd64")
}

View File

@ -44,6 +44,25 @@ func PublicKeyFromBytes(pubKey []byte) (iface.PublicKey, error) {
return pubKeyObj, nil
}
// AggregatePublicKeys aggregates the provided raw public keys into a single key.
func AggregatePublicKeys(pubs [][]byte) (iface.PublicKey, error) {
if len(pubs) == 0 {
return &PublicKey{}, nil
}
p, err := PublicKeyFromBytes(pubs[0])
if err != nil {
return nil, err
}
for _, k := range pubs[1:] {
pubkey, err := PublicKeyFromBytes(k)
if err != nil {
return nil, err
}
p.Aggregate(pubkey)
}
return p, nil
}
// Marshal a public key into a LittleEndian byte slice.
func (p *PublicKey) Marshal() []byte {
return p.p.Serialize()

View File

@ -4,7 +4,7 @@ package bls
// signatures and its respective public keys and
// messages required to verify it.
type SignatureSet struct {
Signatures []Signature
Signatures [][]byte
PublicKeys []PublicKey
Messages [][32]byte
}
@ -12,7 +12,7 @@ type SignatureSet struct {
// NewSet constructs an empty signature set object.
func NewSet() *SignatureSet {
return &SignatureSet{
Signatures: []Signature{},
Signatures: [][]byte{},
PublicKeys: []PublicKey{},
Messages: [][32]byte{},
}

View File

@ -34,6 +34,7 @@ go_test(
"//shared/bls:go_default_library",
"//shared/bls/iface:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/testutil:go_default_library",
"//shared/testutil/require:go_default_library",
"@com_github_ghodss_yaml//:go_default_library",

View File

@ -9,11 +9,24 @@ import (
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
func TestAggregateYaml(t *testing.T) {
flags := &featureconfig.Flags{}
reset := featureconfig.InitWithReset(flags)
t.Run("herumi", testAggregateYaml)
reset()
flags.EnableBlst = true
reset = featureconfig.InitWithReset(flags)
t.Run("blst", testAggregateYaml)
reset()
}
func testAggregateYaml(t *testing.T) {
testFolders, testFolderPath := testutil.TestFolders(t, "general", "bls/aggregate/small")
for _, folder := range testFolders {

View File

@ -5,6 +5,8 @@ import (
"path"
"testing"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
@ -14,6 +16,18 @@ import (
)
func TestAggregateVerifyYaml(t *testing.T) {
flags := &featureconfig.Flags{}
reset := featureconfig.InitWithReset(flags)
t.Run("herumi", testAggregateVerifyYaml)
reset()
flags.EnableBlst = true
reset = featureconfig.InitWithReset(flags)
t.Run("blst", testAggregateVerifyYaml)
reset()
}
func testAggregateVerifyYaml(t *testing.T) {
testFolders, testFolderPath := testutil.TestFolders(t, "general", "bls/aggregate_verify/small")
for i, folder := range testFolders {

View File

@ -5,6 +5,8 @@ import (
"path"
"testing"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
@ -14,6 +16,18 @@ import (
)
func TestFastAggregateVerifyYaml(t *testing.T) {
flags := &featureconfig.Flags{}
reset := featureconfig.InitWithReset(flags)
t.Run("herumi", testFastAggregateVerifyYaml)
reset()
flags.EnableBlst = true
reset = featureconfig.InitWithReset(flags)
t.Run("blst", testFastAggregateVerifyYaml)
reset()
}
func testFastAggregateVerifyYaml(t *testing.T) {
testFolders, testFolderPath := testutil.TestFolders(t, "general", "bls/fast_aggregate_verify/small")
for i, folder := range testFolders {

View File

@ -8,6 +8,8 @@ import (
"path"
"testing"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/testutil"
@ -15,6 +17,18 @@ import (
)
func TestSignMessageYaml(t *testing.T) {
flags := &featureconfig.Flags{}
reset := featureconfig.InitWithReset(flags)
t.Run("herumi", testSignMessageYaml)
reset()
flags.EnableBlst = true
reset = featureconfig.InitWithReset(flags)
t.Run("blst", testSignMessageYaml)
reset()
}
func testSignMessageYaml(t *testing.T) {
testFolders, testFolderPath := testutil.TestFolders(t, "general", "bls/sign/small")
for i, folder := range testFolders {

View File

@ -5,6 +5,8 @@ import (
"path"
"testing"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/testutil"
@ -12,6 +14,18 @@ import (
)
func TestVerifyMessageYaml(t *testing.T) {
flags := &featureconfig.Flags{}
reset := featureconfig.InitWithReset(flags)
t.Run("herumi", testVerifyMessageYaml)
reset()
flags.EnableBlst = true
reset = featureconfig.InitWithReset(flags)
t.Run("blst", testVerifyMessageYaml)
reset()
}
func testVerifyMessageYaml(t *testing.T) {
testFolders, testFolderPath := testutil.TestFolders(t, "general", "bls/verify/small")
for i, folder := range testFolders {

View File

@ -42,6 +42,7 @@ type Flags struct {
InitSyncNoVerify bool // InitSyncNoVerify when initial syncing w/o verifying block's contents.
DisableDynamicCommitteeSubnets bool // Disables dynamic attestation committee subnets via p2p.
SkipBLSVerify bool // Skips BLS verification across the runtime.
EnableBlst bool // Enables new BLS library from supranational.
EnableBackupWebhook bool // EnableBackupWebhook to allow database backups to trigger from monitoring port /db/backup.
PruneEpochBoundaryStates bool // PruneEpochBoundaryStates prunes the epoch boundary state before last finalized check point.
EnableSnappyDBCompression bool // EnableSnappyDBCompression in the database.
@ -271,6 +272,10 @@ func ConfigureBeaconChain(ctx *cli.Context) {
log.Warn("Disabling advanced check point info cache")
cfg.UseCheckPointInfoCache = false
}
if ctx.Bool(enableBlst.Name) {
log.Warn("Enabling new BLS library blst")
cfg.EnableBlst = true
}
Init(cfg)
}

View File

@ -145,6 +145,10 @@ var (
Name: "init-sync-verbose",
Usage: "Enable logging every processed block during initial syncing.",
}
enableBlst = &cli.BoolFlag{
Name: "blst",
Usage: "Enable new BLS library, blst, from Supranational",
}
disableFinalizedDepositsCache = &cli.BoolFlag{
Name: "disable-finalized-deposits-cache",
Usage: "Disables utilization of cached finalized deposits",
@ -652,6 +656,7 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
OnyxTestnet,
spadinaTestnet,
disableAccountsV2,
enableBlst,
}...)
// SlasherFlags contains a list of all the feature flags that apply to the slasher client.
@ -694,6 +699,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{
disableBatchBlockVerify,
initSyncVerbose,
disableFinalizedDepositsCache,
enableBlst,
enableEth1DataMajorityVote,
enableAttBroadcastDiscoveryAttempts,
enablePeerScorer,

106
third_party/blst/blst.BUILD vendored Normal file
View File

@ -0,0 +1,106 @@
load("@prysm//tools/go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_test")
go_library(
name = "go_default_library",
srcs = [
"bindings/go/blst.go",
"bindings/go/server.c",
],
cgo = True,
copts = [
"-D__BLST_CGO__",
"-Ibindings",
"-Isrc",
] + select({
"@io_bazel_rules_go//go/platform:amd64": [
"-mno-avx",
"-D__ADX__",
],
"//conditions:default": [],
}),
cdeps = [":blst"],
importpath = "github.com/supranational/blst/bindings/go",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"bindings/go/blst_htoc_test.go",
"bindings/go/blst_minpk_test.go",
"bindings/go/blst_minsig_test.go",
],
embed = [":go_default_library"],
data = glob([
"bindings/go/hash_to_curve/*.json",
]),
)
cc_library(
name = "blst",
srcs = [
"bindings/blst.h",
"bindings/blst_aux.h",
],
hdrs = [
"bindings/blst.h",
"bindings/blst_aux.h",
],
deps = [
":src",
":asm",
],
strip_include_prefix = "bindings",
visibility = ["//visibility:public"],
)
cc_library(
name = "asm_hdrs",
hdrs = glob([
"build/**/*.s",
"build/**/*.S",
], exclude = ["build/assembly.s"]),
)
cc_library(
name = "asm",
srcs = [
"build/assembly.S",
],
copts = [
] + select({
"@io_bazel_rules_go//go/platform:amd64": [
"-mno-avx",
"-D__ADX__",
],
"//conditions:default": [],
}),
deps = [":asm_hdrs"],
linkstatic = True,
)
cc_library(
name = "hdrs",
hdrs = glob(
[
"src/*.c",
"src/*.h",
],
exclude = [
"src/server.c",
"src/client_*.c",
],
),
strip_include_prefix = "src",
)
cc_library(
name = "src",
srcs = [
"src/server.c",
],
deps = [
":hdrs",
],
)

View File

@ -5,12 +5,6 @@ load("@io_bazel_rules_docker//cc:image.bzl", CC_DEFAULT_BASE = "DEFAULT_BASE")
load("@io_bazel_rules_docker//go:image.bzl", GO_DEFAULT_BASE = "DEFAULT_BASE")
load("//tools:build_settings.bzl", "base_image")
alias(
name = "kubesec",
actual = "@com_github_shyiko_kubesec//:kubesec",
visibility = ["//visibility:public"],
)
sh_binary(
name = "fuzz_wrapper",
srcs = ["fuzz_wrapper.sh"],

View File

@ -1,26 +0,0 @@
"""TODO: Add doc here"""
def _k8s_encrypted_secret_impl(ctx):
ctx.actions.run_shell(
inputs = [ctx.file.template],
outputs = [ctx.outputs.out],
progress_message = "Decrypting %s" % ctx.file.template,
tools = [ctx.executable._kubesec],
command = "%s decrypt %s > %s" % (ctx.executable._kubesec.path, ctx.file.template.path, ctx.outputs.out.path),
)
k8s_encrypted_secret = rule(
implementation = _k8s_encrypted_secret_impl,
attrs = {
"_kubesec": attr.label(
executable = True,
cfg = "host",
default = "//tools:kubesec",
),
"template": attr.label(
allow_single_file = True,
mandatory = True,
),
"out": attr.output(mandatory = True),
},
)