mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 04:54:05 -05:00
Refactor BLS (#6395)
* refactor BLS to expose an interface rather than a single implementation * Split up tests, gofmt, goimports, docs * godoc for signature.Copy() * more godocs * revert gomod / deps changes * rm newline * revert proto changes * rename bls12 to herumi for clarity
This commit is contained in:
@@ -758,7 +758,7 @@ func VerifyIndexedAttestation(ctx context.Context, beaconState *stateTrie.Beacon
|
||||
return err
|
||||
}
|
||||
indices := indexedAtt.AttestingIndices
|
||||
pubkeys := []*bls.PublicKey{}
|
||||
pubkeys := []bls.PublicKey{}
|
||||
if len(indices) > 0 {
|
||||
for i := 0; i < len(indices); i++ {
|
||||
pubkeyAtIdx := beaconState.PubkeyAtIndex(indices[i])
|
||||
@@ -847,8 +847,8 @@ func verifyAttestationsWithDomain(ctx context.Context, beaconState *stateTrie.Be
|
||||
return nil
|
||||
}
|
||||
|
||||
sigs := make([]*bls.Signature, len(atts))
|
||||
pks := make([]*bls.PublicKey, len(atts))
|
||||
sigs := make([]bls.Signature, 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)
|
||||
@@ -862,7 +862,7 @@ func verifyAttestationsWithDomain(ctx context.Context, beaconState *stateTrie.Be
|
||||
}
|
||||
ia := attestationutil.ConvertToIndexed(ctx, a, c)
|
||||
indices := ia.AttestingIndices
|
||||
var pk *bls.PublicKey
|
||||
var pk bls.PublicKey
|
||||
for i := 0; i < len(indices); i++ {
|
||||
pubkeyAtIdx := beaconState.PubkeyAtIndex(indices[i])
|
||||
p, err := bls.PublicKeyFromBytes(pubkeyAtIdx[:])
|
||||
|
||||
@@ -836,7 +836,7 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
}
|
||||
sig0 := privKeys[0].Sign(signingRoot[:])
|
||||
sig1 := privKeys[1].Sign(signingRoot[:])
|
||||
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
aggregateSig := bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att1.Signature = aggregateSig.Marshal()[:]
|
||||
|
||||
att2 := ðpb.IndexedAttestation{
|
||||
@@ -852,7 +852,7 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
}
|
||||
sig0 = privKeys[0].Sign(signingRoot[:])
|
||||
sig1 = privKeys[1].Sign(signingRoot[:])
|
||||
aggregateSig = bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
aggregateSig = bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att2.Signature = aggregateSig.Marshal()[:]
|
||||
|
||||
slashings := []*ethpb.AttesterSlashing{
|
||||
@@ -1148,7 +1148,7 @@ func TestProcessAttestations_OK(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
@@ -1212,7 +1212,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices1))
|
||||
sigs := make([]bls.Signature, len(attestingIndices1))
|
||||
for i, indice := range attestingIndices1 {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
@@ -1240,7 +1240,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs = make([]*bls.Signature, len(attestingIndices2))
|
||||
sigs = make([]bls.Signature, len(attestingIndices2))
|
||||
for i, indice := range attestingIndices2 {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
@@ -1294,7 +1294,7 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices1))
|
||||
sigs := make([]bls.Signature, len(attestingIndices1))
|
||||
for i, indice := range attestingIndices1 {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
@@ -1321,7 +1321,7 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs = make([]*bls.Signature, len(attestingIndices2))
|
||||
sigs = make([]bls.Signature, len(attestingIndices2))
|
||||
for i, indice := range attestingIndices2 {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
@@ -1540,7 +1540,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var sig []*bls.Signature
|
||||
var sig []bls.Signature
|
||||
for _, idx := range tt.attestation.AttestingIndices {
|
||||
validatorSig := keys[idx].Sign(root[:])
|
||||
sig = append(sig, validatorSig)
|
||||
@@ -2107,7 +2107,7 @@ func TestVerifyAttestations_VerifiesMultipleAttestations(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var sigs []*bls.Signature
|
||||
var sigs []bls.Signature
|
||||
for i, u := range comm1 {
|
||||
att1.AggregationBits.SetBitAt(uint64(i), true)
|
||||
sigs = append(sigs, keys[u].Sign(root[:]))
|
||||
@@ -2189,7 +2189,7 @@ func TestVerifyAttestations_HandlesPlannedFork(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var sigs []*bls.Signature
|
||||
var sigs []bls.Signature
|
||||
for i, u := range comm1 {
|
||||
att1.AggregationBits.SetBitAt(uint64(i), true)
|
||||
sigs = append(sigs, keys[u].Sign(root[:]))
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestProcessAttesterSlashings_RegressionSlashableIndices(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Could not get signing root of beacon block header: %v", err)
|
||||
}
|
||||
aggSigs := []*bls.Signature{}
|
||||
aggSigs := []bls.Signature{}
|
||||
for _, index := range setA {
|
||||
sig := privKeys[index].Sign(signingRoot[:])
|
||||
aggSigs = append(aggSigs, sig)
|
||||
@@ -70,7 +70,7 @@ func TestProcessAttesterSlashings_RegressionSlashableIndices(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Could not get signing root of beacon block header: %v", err)
|
||||
}
|
||||
aggSigs = []*bls.Signature{}
|
||||
aggSigs = []bls.Signature{}
|
||||
for _, index := range setB {
|
||||
sig := privKeys[index].Sign(signingRoot[:])
|
||||
aggSigs = append(aggSigs, sig)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// domain = get_domain(state, DOMAIN_SELECTION_PROOF, compute_epoch_at_slot(slot))
|
||||
// signing_root = compute_signing_root(slot, domain)
|
||||
// return bls.Sign(privkey, signing_root)
|
||||
func SlotSignature(state *stateTrie.BeaconState, slot uint64, privKey *bls.SecretKey) (*bls.Signature, error) {
|
||||
func SlotSignature(state *stateTrie.BeaconState, slot uint64, privKey bls.SecretKey) (bls.Signature, error) {
|
||||
d, err := Domain(state.Fork(), CurrentEpoch(state), params.BeaconConfig().DomainBeaconAttester, state.GenesisValidatorRoot())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -54,8 +54,8 @@ func IsAggregator(committeeCount uint64, slotSig []byte) (bool, error) {
|
||||
// def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature:
|
||||
// signatures = [attestation.signature for attestation in attestations]
|
||||
// return bls_aggregate_signatures(signatures)
|
||||
func AggregateSignature(attestations []*ethpb.Attestation) (*bls.Signature, error) {
|
||||
sigs := make([]*bls.Signature, len(attestations))
|
||||
func AggregateSignature(attestations []*ethpb.Attestation) (bls.Signature, error) {
|
||||
sigs := make([]bls.Signature, len(attestations))
|
||||
var err error
|
||||
for i := 0; i < len(sigs); i++ {
|
||||
sigs[i], err = bls.SignatureFromBytes(attestations[i].Signature)
|
||||
|
||||
@@ -88,7 +88,7 @@ func TestAttestation_IsAggregator(t *testing.T) {
|
||||
|
||||
func TestAttestation_AggregateSignature(t *testing.T) {
|
||||
t.Run("verified", func(t *testing.T) {
|
||||
pubkeys := make([]*bls.PublicKey, 0, 100)
|
||||
pubkeys := make([]bls.PublicKey, 0, 100)
|
||||
atts := make([]*ethpb.Attestation, 0, 100)
|
||||
msg := bytesutil.ToBytes32([]byte("hello"))
|
||||
for i := 0; i < 100; i++ {
|
||||
@@ -109,7 +109,7 @@ func TestAttestation_AggregateSignature(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("not verified", func(t *testing.T) {
|
||||
pubkeys := make([]*bls.PublicKey, 0, 100)
|
||||
pubkeys := make([]bls.PublicKey, 0, 100)
|
||||
atts := make([]*ethpb.Attestation, 0, 100)
|
||||
msg := []byte("hello")
|
||||
for i := 0; i < 100; i++ {
|
||||
|
||||
@@ -451,7 +451,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
}
|
||||
sig0 := privKeys[0].Sign(hashTreeRoot[:])
|
||||
sig1 := privKeys[1].Sign(hashTreeRoot[:])
|
||||
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
aggregateSig := bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att1.Signature = aggregateSig.Marshal()[:]
|
||||
|
||||
mockRoot3 := [32]byte{'B'}
|
||||
@@ -468,7 +468,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
}
|
||||
sig0 = privKeys[0].Sign(hashTreeRoot[:])
|
||||
sig1 = privKeys[1].Sign(hashTreeRoot[:])
|
||||
aggregateSig = bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
aggregateSig = bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att2.Signature = aggregateSig.Marshal()[:]
|
||||
|
||||
attesterSlashings := []*ethpb.AttesterSlashing{
|
||||
@@ -511,7 +511,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
@@ -875,7 +875,7 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
hashTreeRoot, err := helpers.ComputeSigningRoot(att.Data, domain)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func validAttesterSlashingForValIdx(t *testing.T, beaconState *state.BeaconState, privs []*bls.SecretKey, valIdx ...uint64) *ethpb.AttesterSlashing {
|
||||
func validAttesterSlashingForValIdx(t *testing.T, beaconState *state.BeaconState, privs []bls.SecretKey, valIdx ...uint64) *ethpb.AttesterSlashing {
|
||||
slashings := []*ethpb.AttesterSlashing{}
|
||||
for _, idx := range valIdx {
|
||||
slashing, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privs[idx], idx)
|
||||
@@ -24,8 +24,8 @@ func validAttesterSlashingForValIdx(t *testing.T, beaconState *state.BeaconState
|
||||
}
|
||||
slashings = append(slashings, slashing)
|
||||
}
|
||||
allSig1 := []*bls.Signature{}
|
||||
allSig2 := []*bls.Signature{}
|
||||
allSig1 := []bls.Signature{}
|
||||
allSig2 := []bls.Signature{}
|
||||
for _, slashing := range slashings {
|
||||
sig1 := slashing.Attestation_1.Signature
|
||||
sig2 := slashing.Attestation_2.Signature
|
||||
|
||||
@@ -251,7 +251,7 @@ func TestSubmitAggregateAndProof_AggregateNotOk(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateAtt(state *beaconstate.BeaconState, index uint64, privKeys []*bls.SecretKey) (*ethpb.Attestation, error) {
|
||||
func generateAtt(state *beaconstate.BeaconState, index uint64, privKeys []bls.SecretKey) (*ethpb.Attestation, error) {
|
||||
aggBits := bitfield.NewBitlist(4)
|
||||
aggBits.SetBitAt(index, true)
|
||||
aggBits.SetBitAt(index+1, true)
|
||||
@@ -273,7 +273,7 @@ func generateAtt(state *beaconstate.BeaconState, index uint64, privKeys []*bls.S
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
zeroSig := [96]byte{}
|
||||
att.Signature = zeroSig[:]
|
||||
|
||||
@@ -291,7 +291,7 @@ func generateAtt(state *beaconstate.BeaconState, index uint64, privKeys []*bls.S
|
||||
return att, nil
|
||||
}
|
||||
|
||||
func generateUnaggregatedAtt(state *beaconstate.BeaconState, index uint64, privKeys []*bls.SecretKey) (*ethpb.Attestation, error) {
|
||||
func generateUnaggregatedAtt(state *beaconstate.BeaconState, index uint64, privKeys []bls.SecretKey) (*ethpb.Attestation, error) {
|
||||
aggBits := bitfield.NewBitlist(4)
|
||||
aggBits.SetBitAt(index, true)
|
||||
att := ðpb.Attestation{
|
||||
@@ -312,7 +312,7 @@ func generateUnaggregatedAtt(state *beaconstate.BeaconState, index uint64, privK
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
zeroSig := [96]byte{}
|
||||
att.Signature = zeroSig[:]
|
||||
|
||||
|
||||
@@ -1430,7 +1430,7 @@ func TestFilterAttestation_OK(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
zeroSig := [96]byte{}
|
||||
atts[i].Signature = zeroSig[:]
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ func TestProcessPendingAtts_HasBlockSaveAggregatedAtt(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
|
||||
@@ -415,7 +415,7 @@ func TestValidateAggregateAndProofWithNewStateMgmt_CanValidate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
@@ -539,7 +539,7 @@ func TestVerifyIndexInCommittee_SeenAggregatorEpoch(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:])
|
||||
sigs[i] = sig
|
||||
|
||||
@@ -50,7 +50,7 @@ func setupValidAttesterSlashing(t *testing.T) (*ethpb.AttesterSlashing, *stateTr
|
||||
}
|
||||
sig0 := privKeys[0].Sign(hashTreeRoot[:])
|
||||
sig1 := privKeys[1].Sign(hashTreeRoot[:])
|
||||
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
aggregateSig := bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att1.Signature = aggregateSig.Marshal()[:]
|
||||
|
||||
att2 := ðpb.IndexedAttestation{
|
||||
@@ -66,7 +66,7 @@ func setupValidAttesterSlashing(t *testing.T) (*ethpb.AttesterSlashing, *stateTr
|
||||
}
|
||||
sig0 = privKeys[0].Sign(hashTreeRoot[:])
|
||||
sig1 = privKeys[1].Sign(hashTreeRoot[:])
|
||||
aggregateSig = bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
aggregateSig = bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att2.Signature = aggregateSig.Marshal()[:]
|
||||
|
||||
slashing := ðpb.AttesterSlashing{
|
||||
|
||||
@@ -78,7 +78,7 @@ func AggregatePair(a1 *ethpb.Attestation, a2 *ethpb.Attestation) (*ethpb.Attesta
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aggregatedSig := aggregateSignatures([]*bls.Signature{baseSig, newSig})
|
||||
aggregatedSig := aggregateSignatures([]bls.Signature{baseSig, newSig})
|
||||
baseAtt.Signature = aggregatedSig.Marshal()
|
||||
baseAtt.AggregationBits = newBits
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ func (al attList) aggregate(coverage bitfield.Bitlist) (*ethpb.Attestation, erro
|
||||
if len(al) < 2 {
|
||||
return nil, errors.Wrap(ErrInvalidAttestationCount, "cannot aggregate")
|
||||
}
|
||||
signs := make([]*bls.Signature, len(al))
|
||||
signs := make([]bls.Signature, len(al))
|
||||
for i := 0; i < len(al); i++ {
|
||||
sig, err := signatureFromBytes(al[i].Signature)
|
||||
if err != nil {
|
||||
|
||||
@@ -94,7 +94,7 @@ func AttestingIndices(bf bitfield.Bitfield, committee []uint64) []uint64 {
|
||||
// domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
|
||||
// signing_root = compute_signing_root(indexed_attestation.data, domain)
|
||||
// return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
|
||||
func VerifyIndexedAttestationSig(ctx context.Context, indexedAtt *ethpb.IndexedAttestation, pubKeys []*bls.PublicKey, domain []byte) error {
|
||||
func VerifyIndexedAttestationSig(ctx context.Context, indexedAtt *ethpb.IndexedAttestation, pubKeys []bls.PublicKey, domain []byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "attestationutil.VerifyIndexedAttestationSig")
|
||||
defer span.End()
|
||||
indices := indexedAtt.AttestingIndices
|
||||
|
||||
@@ -1,50 +1,16 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
|
||||
# gazelle:resolve go github.com/herumi/bls-eth-go-binary/bls @herumi_bls_eth_go_binary//:go_default_library
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["bls.go"],
|
||||
srcs = [
|
||||
"bls.go",
|
||||
"constants.go",
|
||||
"interface.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/bls",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_dgraph_io_ristretto//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@herumi_bls_eth_go_binary//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["bls_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//shared/bytesutil:go_default_library"],
|
||||
)
|
||||
|
||||
# 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 = [
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"@herumi_bls_eth_go_binary//:go_default_library",
|
||||
"//shared/bls/herumi:go_default_library",
|
||||
"//shared/bls/iface:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -4,281 +4,36 @@
|
||||
package bls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dgraph-io/ristretto"
|
||||
bls12 "github.com/herumi/bls-eth-go-binary/bls"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/herumi"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := bls12.Init(bls12.BLS12_381); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := bls12.SetETHmode(bls12.EthModeDraft07); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// DomainByteLength length of domain byte array.
|
||||
const DomainByteLength = 4
|
||||
|
||||
var maxKeys = int64(100000)
|
||||
var pubkeyCache, _ = ristretto.NewCache(&ristretto.Config{
|
||||
NumCounters: maxKeys,
|
||||
MaxCost: 1 << 22, // ~4mb is cache max size
|
||||
BufferItems: 64,
|
||||
})
|
||||
|
||||
// CurveOrder for the BLS12-381 curve.
|
||||
const CurveOrder = "52435875175126190479447740508185965837690552500527637822603658699938581184513"
|
||||
|
||||
// Signature used in the BLS signature scheme.
|
||||
type Signature struct {
|
||||
s *bls12.Sign
|
||||
}
|
||||
|
||||
// PublicKey used in the BLS signature scheme.
|
||||
type PublicKey struct {
|
||||
p *bls12.PublicKey
|
||||
}
|
||||
|
||||
// SecretKey used in the BLS signature scheme.
|
||||
type SecretKey struct {
|
||||
p *bls12.SecretKey
|
||||
}
|
||||
|
||||
// RandKey creates a new private key using a random method provided as an io.Reader.
|
||||
func RandKey() *SecretKey {
|
||||
secKey := &bls12.SecretKey{}
|
||||
secKey.SetByCSPRNG()
|
||||
return &SecretKey{secKey}
|
||||
}
|
||||
|
||||
// SecretKeyFromBytes creates a BLS private key from a BigEndian byte slice.
|
||||
func SecretKeyFromBytes(privKey []byte) (*SecretKey, error) {
|
||||
if len(privKey) != params.BeaconConfig().BLSSecretKeyLength {
|
||||
return nil, fmt.Errorf("secret key must be %d bytes", params.BeaconConfig().BLSSecretKeyLength)
|
||||
}
|
||||
secKey := &bls12.SecretKey{}
|
||||
err := secKey.Deserialize(privKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal bytes into secret key")
|
||||
}
|
||||
return &SecretKey{p: secKey}, err
|
||||
func SecretKeyFromBytes(privKey []byte) (SecretKey, error) {
|
||||
return herumi.SecretKeyFromBytes(privKey)
|
||||
}
|
||||
|
||||
// PublicKeyFromBytes creates a BLS public key from a BigEndian byte slice.
|
||||
func PublicKeyFromBytes(pubKey []byte) (*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()
|
||||
}
|
||||
p := &bls12.PublicKey{}
|
||||
err := p.Deserialize(pubKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal bytes into public key")
|
||||
}
|
||||
pubKeyObj := &PublicKey{p: p}
|
||||
copiedKey, err := pubKeyObj.Copy()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not copy public key")
|
||||
}
|
||||
pubkeyCache.Set(string(pubKey), copiedKey, 48)
|
||||
return pubKeyObj, nil
|
||||
func PublicKeyFromBytes(pubKey []byte) (PublicKey, error) {
|
||||
return herumi.PublicKeyFromBytes(pubKey)
|
||||
}
|
||||
|
||||
// SignatureFromBytes creates a BLS signature from a LittleEndian byte slice.
|
||||
func SignatureFromBytes(sig []byte) (*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 := &bls12.Sign{}
|
||||
err := signature.Deserialize(sig)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal bytes into signature")
|
||||
}
|
||||
return &Signature{s: signature}, nil
|
||||
}
|
||||
|
||||
// PublicKey obtains the public key corresponding to the BLS secret key.
|
||||
func (s *SecretKey) PublicKey() *PublicKey {
|
||||
return &PublicKey{p: s.p.GetPublicKey()}
|
||||
}
|
||||
|
||||
// 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 *SecretKey) Sign(msg []byte) *Signature {
|
||||
if featureconfig.Get().SkipBLSVerify {
|
||||
return &Signature{}
|
||||
}
|
||||
signature := s.p.SignByte(msg)
|
||||
return &Signature{s: signature}
|
||||
}
|
||||
|
||||
// Marshal a secret key into a LittleEndian byte slice.
|
||||
func (s *SecretKey) 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
|
||||
}
|
||||
|
||||
// Marshal a public key into a LittleEndian byte slice.
|
||||
func (p *PublicKey) Marshal() []byte {
|
||||
return p.p.Serialize()
|
||||
}
|
||||
|
||||
// Copy the public key to a new pointer reference.
|
||||
func (p *PublicKey) Copy() (*PublicKey, error) {
|
||||
np := *p.p
|
||||
return &PublicKey{p: &np}, nil
|
||||
}
|
||||
|
||||
// Aggregate two public keys.
|
||||
func (p *PublicKey) Aggregate(p2 *PublicKey) *PublicKey {
|
||||
if featureconfig.Get().SkipBLSVerify {
|
||||
return p
|
||||
}
|
||||
p.p.Add(p2.p)
|
||||
return p
|
||||
}
|
||||
|
||||
// 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 *PublicKey, msg []byte) bool {
|
||||
if featureconfig.Get().SkipBLSVerify {
|
||||
return true
|
||||
}
|
||||
return s.s.VerifyByte(pubKey.p, msg)
|
||||
}
|
||||
|
||||
// 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 []*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, 0, 32*len(msgs))
|
||||
rawKeys := make([]bls12.PublicKey, 0, len(pubKeys))
|
||||
for i := 0; i < size; i++ {
|
||||
msgSlices = append(msgSlices, msgs[i][:]...)
|
||||
rawKeys = append(rawKeys, *pubKeys[i].p)
|
||||
}
|
||||
// Use "NoCheck" because we do not care if the messages are unique or not.
|
||||
return s.s.AggregateVerifyNoCheck(rawKeys, msgSlices)
|
||||
}
|
||||
|
||||
// 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 []*PublicKey, msg [32]byte) bool {
|
||||
if featureconfig.Get().SkipBLSVerify {
|
||||
return true
|
||||
}
|
||||
if len(pubKeys) == 0 {
|
||||
return false
|
||||
}
|
||||
rawKeys := make([]bls12.PublicKey, len(pubKeys))
|
||||
for i := 0; i < len(pubKeys); i++ {
|
||||
rawKeys[i] = *pubKeys[i].p
|
||||
}
|
||||
|
||||
return s.s.FastAggregateVerify(rawKeys, msg[:])
|
||||
}
|
||||
|
||||
// NewAggregateSignature creates a blank aggregate signature.
|
||||
func NewAggregateSignature() *Signature {
|
||||
return &Signature{s: bls12.HashAndMapToSignature([]byte{'m', 'o', 'c', 'k'})}
|
||||
func SignatureFromBytes(sig []byte) (Signature, error) {
|
||||
return herumi.SignatureFromBytes(sig)
|
||||
}
|
||||
|
||||
// AggregateSignatures converts a list of signatures into a single, aggregated sig.
|
||||
func AggregateSignatures(sigs []*Signature) *Signature {
|
||||
if len(sigs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if featureconfig.Get().SkipBLSVerify {
|
||||
return sigs[0]
|
||||
}
|
||||
|
||||
// Copy signature
|
||||
signature := *sigs[0].s
|
||||
for i := 1; i < len(sigs); i++ {
|
||||
signature.Add(sigs[i].s)
|
||||
}
|
||||
return &Signature{s: &signature}
|
||||
func AggregateSignatures(sigs []iface.Signature) iface.Signature {
|
||||
return herumi.AggregateSignatures(sigs)
|
||||
}
|
||||
|
||||
// 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 []*Signature) *Signature {
|
||||
return AggregateSignatures(sigs)
|
||||
// NewAggregateSignature creates a blank aggregate signature.
|
||||
func NewAggregateSignature() iface.Signature {
|
||||
return herumi.NewAggregateSignature()
|
||||
}
|
||||
|
||||
// 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.Serialize()
|
||||
// RandKey creates a new private key using a random input.
|
||||
func RandKey() iface.SecretKey {
|
||||
return herumi.RandKey()
|
||||
}
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
package bls_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshal(t *testing.T) {
|
||||
b := bls.RandKey().Marshal()
|
||||
b32 := bytesutil.ToBytes32(b)
|
||||
pk, err := bls.SecretKeyFromBytes(b32[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pk2, err := bls.SecretKeyFromBytes(b32[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(pk.Marshal(), pk2.Marshal()) {
|
||||
t.Errorf("Keys not equal, received %#x == %#x", pk.Marshal(), pk2.Marshal())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
priv := bls.RandKey()
|
||||
pub := priv.PublicKey()
|
||||
msg := []byte("hello")
|
||||
sig := priv.Sign(msg)
|
||||
if !sig.Verify(pub, msg) {
|
||||
t.Error("Signature did not verify")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateVerify(t *testing.T) {
|
||||
pubkeys := make([]*bls.PublicKey, 0, 100)
|
||||
sigs := make([]*bls.Signature, 0, 100)
|
||||
var msgs [][32]byte
|
||||
for i := 0; i < 100; i++ {
|
||||
msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
|
||||
priv := bls.RandKey()
|
||||
pub := priv.PublicKey()
|
||||
sig := priv.Sign(msg[:])
|
||||
pubkeys = append(pubkeys, pub)
|
||||
sigs = append(sigs, sig)
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
aggSig := bls.Aggregate(sigs)
|
||||
if !aggSig.AggregateVerify(pubkeys, msgs) {
|
||||
t.Error("Signature did not verify")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFastAggregateVerify(t *testing.T) {
|
||||
pubkeys := make([]*bls.PublicKey, 0, 100)
|
||||
sigs := make([]*bls.Signature, 0, 100)
|
||||
msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
|
||||
for i := 0; i < 100; i++ {
|
||||
priv := bls.RandKey()
|
||||
pub := priv.PublicKey()
|
||||
sig := priv.Sign(msg[:])
|
||||
pubkeys = append(pubkeys, pub)
|
||||
sigs = append(sigs, sig)
|
||||
}
|
||||
aggSig := bls.AggregateSignatures(sigs)
|
||||
if !aggSig.FastAggregateVerify(pubkeys, msg) {
|
||||
t.Error("Signature did not verify")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFastAggregateVerify_ReturnsFalseOnEmptyPubKeyList(t *testing.T) {
|
||||
var pubkeys []*bls.PublicKey
|
||||
sigs := make([]*bls.Signature, 0, 100)
|
||||
msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
|
||||
|
||||
aggSig := bls.AggregateSignatures(sigs)
|
||||
if aggSig.FastAggregateVerify(pubkeys, msg) != false {
|
||||
t.Error("Expected FastAggregateVerify to return false with empty input " +
|
||||
"of public keys.")
|
||||
}
|
||||
}
|
||||
|
||||
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: err blsSecretKeyDeserialize ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
{
|
||||
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 := bls.SecretKeyFromBytes(test.input)
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("No error returned: expected %v", test.err)
|
||||
} else if test.err.Error() != err.Error() {
|
||||
t.Errorf("Unexpected error returned: expected %v, received %v", test.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error returned: %v", err)
|
||||
} else {
|
||||
if bytes.Compare(res.Marshal(), test.input) != 0 {
|
||||
t.Errorf("Unexpected result: expected %x, received %x", test.input, res.Marshal())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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: err blsPublicKeyDeserialize 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
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 := bls.PublicKeyFromBytes(test.input)
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("No error returned: expected %v", test.err)
|
||||
} else if test.err.Error() != err.Error() {
|
||||
t.Errorf("Unexpected error returned: expected %v, received %v", test.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error returned: %v", err)
|
||||
} else {
|
||||
if bytes.Compare(res.Marshal(), test.input) != 0 {
|
||||
t.Errorf("Unexpected result: expected %x, received %x", test.input, res.Marshal())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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: err blsSignatureDeserialize 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
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 := bls.SignatureFromBytes(test.input)
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("No error returned: expected %v", test.err)
|
||||
} else if test.err.Error() != err.Error() {
|
||||
t.Errorf("Unexpected error returned: expected %v, received %v", test.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error returned: %v", err)
|
||||
} else {
|
||||
if bytes.Compare(res.Marshal(), test.input) != 0 {
|
||||
t.Errorf("Unexpected result: expected %x, received %x", test.input, res.Marshal())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKey_Copy(t *testing.T) {
|
||||
pubkeyA := bls.RandKey().PublicKey()
|
||||
pubkeyBytes := pubkeyA.Marshal()
|
||||
|
||||
pubkeyB, err := pubkeyA.Copy()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pubkeyB.Aggregate(bls.RandKey().PublicKey())
|
||||
|
||||
if !bytes.Equal(pubkeyA.Marshal(), pubkeyBytes) {
|
||||
t.Fatal("Pubkey was mutated after copy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerialize(t *testing.T) {
|
||||
rk := bls.RandKey()
|
||||
b := rk.Marshal()
|
||||
|
||||
if _, err := bls.SecretKeyFromBytes(b); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
7
shared/bls/constants.go
Normal file
7
shared/bls/constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package bls
|
||||
|
||||
// DomainByteLength length of domain byte array.
|
||||
const DomainByteLength = 4
|
||||
|
||||
// CurveOrder for the BLS12-381 curve.
|
||||
const CurveOrder = "52435875175126190479447740508185965837690552500527637822603658699938581184513"
|
||||
66
shared/bls/herumi/BUILD.bazel
Normal file
66
shared/bls/herumi/BUILD.bazel
Normal file
@@ -0,0 +1,66 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
# gazelle:resolve go github.com/herumi/bls-eth-go-binary/bls @herumi_bls_eth_go_binary//:go_default_library
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"docs.go",
|
||||
"init.go",
|
||||
"public_key.go",
|
||||
"secret_key.go",
|
||||
"signature.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/bls/herumi",
|
||||
visibility = [
|
||||
"//shared/bls:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//shared/bls/iface:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_dgraph_io_ristretto//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@herumi_bls_eth_go_binary//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"public_key_test.go",
|
||||
"secret_key_test.go",
|
||||
"signature_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//shared/bls/iface:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
# 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",
|
||||
"@herumi_bls_eth_go_binary//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,23 +1,24 @@
|
||||
package bls_test
|
||||
package herumi_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
bls2 "github.com/herumi/bls-eth-go-binary/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"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 := bls2.Init(bls2.BLS12_381); err != nil {
|
||||
if err := bls.Init(bls.BLS12_381); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err := bls2.SetETHmode(bls2.EthModeDraft05); err != nil {
|
||||
if err := bls.SetETHmode(bls.EthModeDraft07); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
newGt := &bls2.GT{}
|
||||
newG1 := &bls2.G1{}
|
||||
newG2 := &bls2.G2{}
|
||||
newGt := &bls.GT{}
|
||||
newG1 := &bls.G1{}
|
||||
newG2 := &bls.G2{}
|
||||
|
||||
newGt.SetInt64(10)
|
||||
hash := hashutil.Hash([]byte{})
|
||||
@@ -33,12 +34,12 @@ func BenchmarkPairing(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bls2.Pairing(newGt, newG1, newG2)
|
||||
bls.Pairing(newGt, newG1, newG2)
|
||||
}
|
||||
|
||||
}
|
||||
func BenchmarkSignature_Verify(b *testing.B) {
|
||||
sk := bls.RandKey()
|
||||
sk := herumi.RandKey()
|
||||
|
||||
msg := []byte("Some msg")
|
||||
sig := sk.Sign(msg)
|
||||
@@ -54,18 +55,18 @@ func BenchmarkSignature_Verify(b *testing.B) {
|
||||
func BenchmarkSignature_AggregateVerify(b *testing.B) {
|
||||
sigN := 128 // MAX_ATTESTATIONS per block.
|
||||
|
||||
var pks []*bls.PublicKey
|
||||
var sigs []*bls.Signature
|
||||
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 := bls.RandKey()
|
||||
sk := herumi.RandKey()
|
||||
sig := sk.Sign(msg[:])
|
||||
pks = append(pks, sk.PublicKey())
|
||||
sigs = append(sigs, sig)
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
aggregated := bls.Aggregate(sigs)
|
||||
aggregated := herumi.Aggregate(sigs)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -77,12 +78,12 @@ func BenchmarkSignature_AggregateVerify(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkSecretKey_Marshal(b *testing.B) {
|
||||
key := bls.RandKey()
|
||||
key := herumi.RandKey()
|
||||
d := key.Marshal()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := bls.SecretKeyFromBytes(d)
|
||||
_, err := herumi.SecretKeyFromBytes(d)
|
||||
_ = err
|
||||
}
|
||||
}
|
||||
6
shared/bls/herumi/docs.go
Normal file
6
shared/bls/herumi/docs.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Package herumi 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 Herumi.
|
||||
package herumi
|
||||
12
shared/bls/herumi/init.go
Normal file
12
shared/bls/herumi/init.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package herumi
|
||||
|
||||
import "github.com/herumi/bls-eth-go-binary/bls"
|
||||
|
||||
func init() {
|
||||
if err := bls.Init(bls.BLS12_381); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := bls.SetETHmode(bls.EthModeDraft07); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
65
shared/bls/herumi/public_key.go
Normal file
65
shared/bls/herumi/public_key.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package herumi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dgraph-io/ristretto"
|
||||
bls12 "github.com/herumi/bls-eth-go-binary/bls"
|
||||
"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 *bls12.PublicKey
|
||||
}
|
||||
|
||||
// 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 := &bls12.PublicKey{}
|
||||
err := p.Deserialize(pubKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal bytes into public key")
|
||||
}
|
||||
pubKeyObj := &PublicKey{p: p}
|
||||
pubkeyCache.Set(string(pubKey), pubKeyObj.Copy(), 48)
|
||||
return pubKeyObj, nil
|
||||
}
|
||||
|
||||
// Marshal a public key into a LittleEndian byte slice.
|
||||
func (p *PublicKey) Marshal() []byte {
|
||||
return p.p.Serialize()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
p.p.Add(p2.(*PublicKey).p)
|
||||
return p
|
||||
}
|
||||
80
shared/bls/herumi/public_key_test.go
Normal file
80
shared/bls/herumi/public_key_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package herumi_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/herumi"
|
||||
)
|
||||
|
||||
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: err blsPublicKeyDeserialize 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
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 := herumi.PublicKeyFromBytes(test.input)
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("No error returned: expected %v", test.err)
|
||||
} else if test.err.Error() != err.Error() {
|
||||
t.Errorf("Unexpected error returned: expected %v, received %v", test.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error returned: %v", err)
|
||||
} else {
|
||||
if bytes.Compare(res.Marshal(), test.input) != 0 {
|
||||
t.Errorf("Unexpected result: expected %x, received %x", test.input, res.Marshal())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKey_Copy(t *testing.T) {
|
||||
pubkeyA := herumi.RandKey().PublicKey()
|
||||
pubkeyBytes := pubkeyA.Marshal()
|
||||
|
||||
pubkeyB := pubkeyA.Copy()
|
||||
pubkeyB.Aggregate(herumi.RandKey().PublicKey())
|
||||
|
||||
if !bytes.Equal(pubkeyA.Marshal(), pubkeyBytes) {
|
||||
t.Fatal("Pubkey was mutated after copy")
|
||||
}
|
||||
}
|
||||
67
shared/bls/herumi/secret_key.go
Normal file
67
shared/bls/herumi/secret_key.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package herumi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
bls12 "github.com/herumi/bls-eth-go-binary/bls"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// bls12SecretKey used in the BLS signature scheme.
|
||||
type bls12SecretKey struct {
|
||||
p *bls12.SecretKey
|
||||
}
|
||||
|
||||
// RandKey creates a new private key using a random method provided as an io.Reader.
|
||||
func RandKey() iface.SecretKey {
|
||||
secKey := &bls12.SecretKey{}
|
||||
secKey.SetByCSPRNG()
|
||||
return &bls12SecretKey{secKey}
|
||||
}
|
||||
|
||||
// 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 := &bls12.SecretKey{}
|
||||
err := secKey.Deserialize(privKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal bytes into secret key")
|
||||
}
|
||||
return &bls12SecretKey{p: secKey}, err
|
||||
}
|
||||
|
||||
// PublicKey obtains the public key corresponding to the BLS secret key.
|
||||
func (s *bls12SecretKey) PublicKey() iface.PublicKey {
|
||||
return &PublicKey{p: s.p.GetPublicKey()}
|
||||
}
|
||||
|
||||
// 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 := s.p.SignByte(msg)
|
||||
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
|
||||
}
|
||||
94
shared/bls/herumi/secret_key_test.go
Normal file
94
shared/bls/herumi/secret_key_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package herumi_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/herumi"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshal(t *testing.T) {
|
||||
b := herumi.RandKey().Marshal()
|
||||
b32 := bytesutil.ToBytes32(b)
|
||||
pk, err := herumi.SecretKeyFromBytes(b32[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pk2, err := herumi.SecretKeyFromBytes(b32[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(pk.Marshal(), pk2.Marshal()) {
|
||||
t.Errorf("Keys not equal, received %#x == %#x", pk.Marshal(), pk2.Marshal())
|
||||
}
|
||||
}
|
||||
|
||||
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: err blsSecretKeyDeserialize ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
{
|
||||
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 := herumi.SecretKeyFromBytes(test.input)
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("No error returned: expected %v", test.err)
|
||||
} else if test.err.Error() != err.Error() {
|
||||
t.Errorf("Unexpected error returned: expected %v, received %v", test.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error returned: %v", err)
|
||||
} else {
|
||||
if bytes.Compare(res.Marshal(), test.input) != 0 {
|
||||
t.Errorf("Unexpected result: expected %x, received %x", test.input, res.Marshal())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerialize(t *testing.T) {
|
||||
rk := herumi.RandKey()
|
||||
b := rk.Marshal()
|
||||
|
||||
if _, err := herumi.SecretKeyFromBytes(b); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
157
shared/bls/herumi/signature.go
Normal file
157
shared/bls/herumi/signature.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package herumi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
bls12 "github.com/herumi/bls-eth-go-binary/bls"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// Signature used in the BLS signature scheme.
|
||||
type Signature struct {
|
||||
s *bls12.Sign
|
||||
}
|
||||
|
||||
// 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 := &bls12.Sign{}
|
||||
err := signature.Deserialize(sig)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "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.VerifyByte(pubKey.(*PublicKey).p, msg)
|
||||
}
|
||||
|
||||
// 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, 0, 32*len(msgs))
|
||||
rawKeys := make([]bls12.PublicKey, 0, len(pubKeys))
|
||||
for i := 0; i < size; i++ {
|
||||
msgSlices = append(msgSlices, msgs[i][:]...)
|
||||
rawKeys = append(rawKeys, *(pubKeys[i].(*PublicKey).p))
|
||||
}
|
||||
// Use "NoCheck" because we do not care if the messages are unique or not.
|
||||
return s.s.AggregateVerifyNoCheck(rawKeys, msgSlices)
|
||||
}
|
||||
|
||||
// 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([]bls12.PublicKey, len(pubKeys))
|
||||
for i := 0; i < len(pubKeys); i++ {
|
||||
rawKeys[i] = *(pubKeys[i].(*PublicKey).p)
|
||||
}
|
||||
|
||||
return s.s.FastAggregateVerify(rawKeys, msg[:])
|
||||
}
|
||||
|
||||
// NewAggregateSignature creates a blank aggregate signature.
|
||||
func NewAggregateSignature() iface.Signature {
|
||||
return &Signature{s: bls12.HashAndMapToSignature([]byte{'m', 'o', 'c', 'k'})}
|
||||
}
|
||||
|
||||
// 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]
|
||||
}
|
||||
|
||||
signature := *sigs[0].Copy().(*Signature).s
|
||||
for i := 1; i < len(sigs); i++ {
|
||||
signature.Add(sigs[i].(*Signature).s)
|
||||
}
|
||||
return &Signature{s: &signature}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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.Serialize()
|
||||
}
|
||||
|
||||
// Copy returns a full deep copy of a signature.
|
||||
func (s *Signature) Copy() iface.Signature {
|
||||
return &Signature{s: &*s.s}
|
||||
}
|
||||
127
shared/bls/herumi/signature_test.go
Normal file
127
shared/bls/herumi/signature_test.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package herumi_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/herumi"
|
||||
)
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
priv := herumi.RandKey()
|
||||
pub := priv.PublicKey()
|
||||
msg := []byte("hello")
|
||||
sig := priv.Sign(msg)
|
||||
if !sig.Verify(pub, msg) {
|
||||
t.Error("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 := herumi.RandKey()
|
||||
pub := priv.PublicKey()
|
||||
sig := priv.Sign(msg[:])
|
||||
pubkeys = append(pubkeys, pub)
|
||||
sigs = append(sigs, sig)
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
aggSig := herumi.Aggregate(sigs)
|
||||
if !aggSig.AggregateVerify(pubkeys, msgs) {
|
||||
t.Error("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 := herumi.RandKey()
|
||||
pub := priv.PublicKey()
|
||||
sig := priv.Sign(msg[:])
|
||||
pubkeys = append(pubkeys, pub)
|
||||
sigs = append(sigs, sig)
|
||||
}
|
||||
aggSig := herumi.AggregateSignatures(sigs)
|
||||
if !aggSig.FastAggregateVerify(pubkeys, msg) {
|
||||
t.Error("Signature did not verify")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFastAggregateVerify_ReturnsFalseOnEmptyPubKeyList(t *testing.T) {
|
||||
var pubkeys []iface.PublicKey
|
||||
msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
|
||||
|
||||
aggSig := herumi.NewAggregateSignature()
|
||||
if aggSig.FastAggregateVerify(pubkeys, msg) != false {
|
||||
t.Error("Expected FastAggregateVerify to return false with empty input " +
|
||||
"of public keys.")
|
||||
}
|
||||
}
|
||||
|
||||
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: err blsSignatureDeserialize 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
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 := herumi.SignatureFromBytes(test.input)
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("No error returned: expected %v", test.err)
|
||||
} else if test.err.Error() != err.Error() {
|
||||
t.Errorf("Unexpected error returned: expected %v, received %v", test.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error returned: %v", err)
|
||||
} else {
|
||||
if bytes.Compare(res.Marshal(), test.input) != 0 {
|
||||
t.Errorf("Unexpected result: expected %x, received %x", test.input, res.Marshal())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
8
shared/bls/iface/BUILD.bazel
Normal file
8
shared/bls/iface/BUILD.bazel
Normal file
@@ -0,0 +1,8 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["interface.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/bls/iface",
|
||||
visibility = ["//shared/bls:__subpackages__"],
|
||||
)
|
||||
29
shared/bls/iface/interface.go
Normal file
29
shared/bls/iface/interface.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Package iface provides the BLS interfaces that are implemented by the various BLS wrappers.
|
||||
//
|
||||
// This package should not be used by downstream consumers. These interfaces are re-exporter by
|
||||
// github.com/prysmaticlabs/prysm/shared/bls. This package exists to prevent an import circular
|
||||
// dependency.
|
||||
package iface
|
||||
|
||||
// SecretKey represents a BLS secret or private key.
|
||||
type SecretKey interface {
|
||||
PublicKey() PublicKey
|
||||
Sign(msg []byte) Signature
|
||||
Marshal() []byte
|
||||
}
|
||||
|
||||
// PublicKey represents a BLS public key.
|
||||
type PublicKey interface {
|
||||
Marshal() []byte
|
||||
Copy() PublicKey
|
||||
Aggregate(p2 PublicKey) PublicKey
|
||||
}
|
||||
|
||||
// Signature represents a BLS signature.
|
||||
type Signature interface {
|
||||
Verify(pubKey PublicKey, msg []byte) bool
|
||||
AggregateVerify(pubKeys []PublicKey, msgs [][32]byte) bool
|
||||
FastAggregateVerify(pubKeys []PublicKey, msg [32]byte) bool
|
||||
Marshal() []byte
|
||||
Copy() Signature
|
||||
}
|
||||
14
shared/bls/interface.go
Normal file
14
shared/bls/interface.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package bls
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
)
|
||||
|
||||
// PublicKey represents a BLS public key.
|
||||
type PublicKey = iface.PublicKey
|
||||
|
||||
// SecretKey represents a BLS secret or private key.
|
||||
type SecretKey = iface.SecretKey
|
||||
|
||||
// Signature represents a BLS signature.
|
||||
type Signature = iface.Signature
|
||||
@@ -32,6 +32,7 @@ go_test(
|
||||
tags = ["spectest"],
|
||||
deps = [
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bls/iface:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
@@ -25,7 +26,7 @@ func TestAggregateYaml(t *testing.T) {
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
var sigs []*bls.Signature
|
||||
var sigs []iface.Signature
|
||||
for _, s := range test.Input {
|
||||
sigBytes, err := hex.DecodeString(s[2:])
|
||||
if err != nil {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
@@ -25,7 +26,7 @@ func TestAggregateVerifyYaml(t *testing.T) {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
pubkeys := make([]*bls.PublicKey, 0, len(test.Input.Pubkeys))
|
||||
pubkeys := make([]iface.PublicKey, 0, len(test.Input.Pubkeys))
|
||||
msgs := make([][32]byte, 0, len(test.Input.Messages))
|
||||
for _, pubKey := range test.Input.Pubkeys {
|
||||
pkBytes, err := hex.DecodeString(pubKey[2:])
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls/iface"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
@@ -25,7 +26,7 @@ func TestFastAggregateVerifyYaml(t *testing.T) {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
pubkeys := make([]*bls.PublicKey, len(test.Input.Pubkeys))
|
||||
pubkeys := make([]iface.PublicKey, len(test.Input.Pubkeys))
|
||||
for j, raw := range test.Input.Pubkeys {
|
||||
pkBytes, err := hex.DecodeString(raw[2:])
|
||||
if err != nil {
|
||||
|
||||
@@ -98,7 +98,7 @@ func generateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, offset int
|
||||
}
|
||||
|
||||
// DepositDataFromKeys generates a list of deposit data items from a set of BLS validator keys.
|
||||
func DepositDataFromKeys(privKeys []*bls.SecretKey, pubKeys []*bls.PublicKey) ([]*ethpb.Deposit_Data, [][]byte, error) {
|
||||
func DepositDataFromKeys(privKeys []bls.SecretKey, pubKeys []bls.PublicKey) ([]*ethpb.Deposit_Data, [][]byte, error) {
|
||||
type depositData struct {
|
||||
items []*ethpb.Deposit_Data
|
||||
roots [][]byte
|
||||
@@ -123,7 +123,7 @@ func DepositDataFromKeys(privKeys []*bls.SecretKey, pubKeys []*bls.PublicKey) ([
|
||||
return depositDataItems, depositDataRoots, nil
|
||||
}
|
||||
|
||||
func depositDataFromKeys(privKeys []*bls.SecretKey, pubKeys []*bls.PublicKey) ([]*ethpb.Deposit_Data, [][]byte, error) {
|
||||
func depositDataFromKeys(privKeys []bls.SecretKey, pubKeys []bls.PublicKey) ([]*ethpb.Deposit_Data, [][]byte, error) {
|
||||
dataRoots := make([][]byte, len(privKeys))
|
||||
depositDataItems := make([]*ethpb.Deposit_Data, len(privKeys))
|
||||
for i := 0; i < len(privKeys); i++ {
|
||||
@@ -142,7 +142,7 @@ func depositDataFromKeys(privKeys []*bls.SecretKey, pubKeys []*bls.PublicKey) ([
|
||||
}
|
||||
|
||||
// Generates a deposit data item from BLS keys and signs the hash tree root of the data.
|
||||
func createDepositData(privKey *bls.SecretKey, pubKey *bls.PublicKey) (*ethpb.Deposit_Data, error) {
|
||||
func createDepositData(privKey bls.SecretKey, pubKey bls.PublicKey) (*ethpb.Deposit_Data, error) {
|
||||
di := ðpb.Deposit_Data{
|
||||
PublicKey: pubKey.Marshal(),
|
||||
WithdrawalCredentials: withdrawalCredentialsHash(pubKey.Marshal()),
|
||||
|
||||
@@ -18,12 +18,12 @@ const (
|
||||
// DeterministicallyGenerateKeys creates BLS private keys using a fixed curve order according to
|
||||
// the algorithm specified in the Eth2.0-Specs interop mock start section found here:
|
||||
// https://github.com/ethereum/eth2.0-pm/blob/a085c9870f3956d6228ed2a40cd37f0c6580ecd7/interop/mocked_start/README.md
|
||||
func DeterministicallyGenerateKeys(startIndex, numKeys uint64) ([]*bls.SecretKey, []*bls.PublicKey, error) {
|
||||
privKeys := make([]*bls.SecretKey, numKeys)
|
||||
pubKeys := make([]*bls.PublicKey, numKeys)
|
||||
func DeterministicallyGenerateKeys(startIndex, numKeys uint64) ([]bls.SecretKey, []bls.PublicKey, error) {
|
||||
privKeys := make([]bls.SecretKey, numKeys)
|
||||
pubKeys := make([]bls.PublicKey, numKeys)
|
||||
type keys struct {
|
||||
secrets []*bls.SecretKey
|
||||
publics []*bls.PublicKey
|
||||
secrets []bls.SecretKey
|
||||
publics []bls.PublicKey
|
||||
}
|
||||
results, err := mputil.Scatter(int(numKeys), func(offset int, entries int, _ *sync.RWMutex) (interface{}, error) {
|
||||
secs, pubs, err := deterministicallyGenerateKeys(uint64(offset)+startIndex, uint64(entries))
|
||||
@@ -43,9 +43,9 @@ func DeterministicallyGenerateKeys(startIndex, numKeys uint64) ([]*bls.SecretKey
|
||||
return privKeys, pubKeys, nil
|
||||
}
|
||||
|
||||
func deterministicallyGenerateKeys(startIndex, numKeys uint64) ([]*bls.SecretKey, []*bls.PublicKey, error) {
|
||||
privKeys := make([]*bls.SecretKey, numKeys)
|
||||
pubKeys := make([]*bls.PublicKey, numKeys)
|
||||
func deterministicallyGenerateKeys(startIndex, numKeys uint64) ([]bls.SecretKey, []bls.PublicKey, error) {
|
||||
privKeys := make([]bls.SecretKey, numKeys)
|
||||
pubKeys := make([]bls.PublicKey, numKeys)
|
||||
for i := startIndex; i < startIndex+numKeys; i++ {
|
||||
enc := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint32(enc, uint32(i))
|
||||
|
||||
@@ -56,9 +56,9 @@ const (
|
||||
type Key struct {
|
||||
ID uuid.UUID // Version 4 "random" for unique id not derived from key data
|
||||
|
||||
PublicKey *bls.PublicKey // Represents the public key of the user.
|
||||
PublicKey bls.PublicKey // Represents the public key of the user.
|
||||
|
||||
SecretKey *bls.SecretKey // Represents the private key of the user.
|
||||
SecretKey bls.SecretKey // Represents the private key of the user.
|
||||
}
|
||||
|
||||
type keyStore interface {
|
||||
@@ -138,7 +138,7 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
||||
}
|
||||
|
||||
// NewKeyFromBLS creates a new keystore Key type using a BLS private key.
|
||||
func NewKeyFromBLS(blsKey *bls.SecretKey) (*Key, error) {
|
||||
func NewKeyFromBLS(blsKey bls.SecretKey) (*Key, error) {
|
||||
id := uuid.NewRandom()
|
||||
pubkey := blsKey.PublicKey()
|
||||
key := &Key{
|
||||
|
||||
@@ -51,7 +51,7 @@ func ensureInt(x interface{}) int {
|
||||
|
||||
// keyFileName implements the naming convention for keyfiles:
|
||||
// UTC--<created_at UTC ISO8601>-<first 8 character of address hex>
|
||||
func keyFileName(pubkey *bls.PublicKey) string {
|
||||
func keyFileName(pubkey bls.PublicKey) string {
|
||||
ts := roughtime.Now().UTC()
|
||||
return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(pubkey.Marshal())[:8])
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func NewBeaconBlock() *ethpb.SignedBeaconBlock {
|
||||
// Use BlockGenConfig to declare the conditions you would like the block generated under.
|
||||
func GenerateFullBlock(
|
||||
bState *stateTrie.BeaconState,
|
||||
privs []*bls.SecretKey,
|
||||
privs []bls.SecretKey,
|
||||
conf *BlockGenConfig,
|
||||
slot uint64,
|
||||
) (*ethpb.SignedBeaconBlock, error) {
|
||||
@@ -184,7 +184,7 @@ func GenerateFullBlock(
|
||||
// GenerateProposerSlashingForValidator for a specific validator index.
|
||||
func GenerateProposerSlashingForValidator(
|
||||
bState *stateTrie.BeaconState,
|
||||
priv *bls.SecretKey,
|
||||
priv bls.SecretKey,
|
||||
idx uint64,
|
||||
) (*ethpb.ProposerSlashing, error) {
|
||||
header1 := ðpb.SignedBeaconBlockHeader{
|
||||
@@ -230,7 +230,7 @@ func GenerateProposerSlashingForValidator(
|
||||
|
||||
func generateProposerSlashings(
|
||||
bState *stateTrie.BeaconState,
|
||||
privs []*bls.SecretKey,
|
||||
privs []bls.SecretKey,
|
||||
numSlashings uint64,
|
||||
) ([]*ethpb.ProposerSlashing, error) {
|
||||
proposerSlashings := make([]*ethpb.ProposerSlashing, numSlashings)
|
||||
@@ -251,7 +251,7 @@ func generateProposerSlashings(
|
||||
// GenerateAttesterSlashingForValidator for a specific validator index.
|
||||
func GenerateAttesterSlashingForValidator(
|
||||
bState *stateTrie.BeaconState,
|
||||
priv *bls.SecretKey,
|
||||
priv bls.SecretKey,
|
||||
idx uint64,
|
||||
) (*ethpb.AttesterSlashing, error) {
|
||||
currentEpoch := helpers.CurrentEpoch(bState)
|
||||
@@ -280,7 +280,7 @@ func GenerateAttesterSlashingForValidator(
|
||||
return nil, err
|
||||
}
|
||||
sig := priv.Sign(dataRoot[:])
|
||||
att1.Signature = bls.AggregateSignatures([]*bls.Signature{sig}).Marshal()
|
||||
att1.Signature = bls.AggregateSignatures([]bls.Signature{sig}).Marshal()
|
||||
|
||||
att2 := ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
@@ -302,7 +302,7 @@ func GenerateAttesterSlashingForValidator(
|
||||
return nil, err
|
||||
}
|
||||
sig = priv.Sign(dataRoot[:])
|
||||
att2.Signature = bls.AggregateSignatures([]*bls.Signature{sig}).Marshal()
|
||||
att2.Signature = bls.AggregateSignatures([]bls.Signature{sig}).Marshal()
|
||||
|
||||
return ðpb.AttesterSlashing{
|
||||
Attestation_1: att1,
|
||||
@@ -312,7 +312,7 @@ func GenerateAttesterSlashingForValidator(
|
||||
|
||||
func generateAttesterSlashings(
|
||||
bState *stateTrie.BeaconState,
|
||||
privs []*bls.SecretKey,
|
||||
privs []bls.SecretKey,
|
||||
numSlashings uint64,
|
||||
) ([]*ethpb.AttesterSlashing, error) {
|
||||
attesterSlashings := make([]*ethpb.AttesterSlashing, numSlashings)
|
||||
@@ -340,7 +340,7 @@ func generateAttesterSlashings(
|
||||
// for the same data with their aggregation bits split uniformly.
|
||||
//
|
||||
// If you request 4 attestations, but there are 8 committees, you will get 4 fully aggregated attestations.
|
||||
func GenerateAttestations(bState *stateTrie.BeaconState, privs []*bls.SecretKey, numToGen uint64, slot uint64, randomRoot bool) ([]*ethpb.Attestation, error) {
|
||||
func GenerateAttestations(bState *stateTrie.BeaconState, privs []bls.SecretKey, numToGen uint64, slot uint64, randomRoot bool) ([]*ethpb.Attestation, error) {
|
||||
currentEpoch := helpers.SlotToEpoch(slot)
|
||||
attestations := []*ethpb.Attestation{}
|
||||
generateHeadState := false
|
||||
@@ -447,7 +447,7 @@ func GenerateAttestations(bState *stateTrie.BeaconState, privs []*bls.SecretKey,
|
||||
bitsPerAtt := committeeSize / uint64(attsPerCommittee)
|
||||
for i := uint64(0); i < committeeSize; i += bitsPerAtt {
|
||||
aggregationBits := bitfield.NewBitlist(committeeSize)
|
||||
sigs := []*bls.Signature{}
|
||||
sigs := []bls.Signature{}
|
||||
for b := i; b < i+bitsPerAtt; b++ {
|
||||
aggregationBits.SetBitAt(b, true)
|
||||
sigs = append(sigs, privs[committee[b]].Sign(dataRoot[:]))
|
||||
@@ -491,7 +491,7 @@ func generateDepositsAndEth1Data(
|
||||
|
||||
func generateVoluntaryExits(
|
||||
bState *stateTrie.BeaconState,
|
||||
privs []*bls.SecretKey,
|
||||
privs []bls.SecretKey,
|
||||
numExits uint64,
|
||||
) ([]*ethpb.SignedVoluntaryExit, error) {
|
||||
currentEpoch := helpers.CurrentEpoch(bState)
|
||||
|
||||
@@ -23,7 +23,7 @@ var lock sync.Mutex
|
||||
|
||||
// Caches
|
||||
var cachedDeposits []*ethpb.Deposit
|
||||
var privKeys []*bls.SecretKey
|
||||
var privKeys []bls.SecretKey
|
||||
var trie *trieutil.SparseMerkleTrie
|
||||
|
||||
// DeterministicDepositsAndKeys returns the entered amount of deposits and secret keys.
|
||||
@@ -31,7 +31,7 @@ var trie *trieutil.SparseMerkleTrie
|
||||
// account is key n and the withdrawal account is key n+1. As such,
|
||||
// if all secret keys for n validators are required then numDeposits
|
||||
// should be n+1.
|
||||
func DeterministicDepositsAndKeys(numDeposits uint64) ([]*ethpb.Deposit, []*bls.SecretKey, error) {
|
||||
func DeterministicDepositsAndKeys(numDeposits uint64) ([]*ethpb.Deposit, []bls.SecretKey, error) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
var err error
|
||||
@@ -152,7 +152,7 @@ func DeterministicEth1Data(size int) (*ethpb.Eth1Data, error) {
|
||||
}
|
||||
|
||||
// DeterministicGenesisState returns a genesis state made using the deterministic deposits.
|
||||
func DeterministicGenesisState(t testing.TB, numValidators uint64) (*stateTrie.BeaconState, []*bls.SecretKey) {
|
||||
func DeterministicGenesisState(t testing.TB, numValidators uint64) (*stateTrie.BeaconState, []bls.SecretKey) {
|
||||
deposits, privKeys, err := DeterministicDepositsAndKeys(numValidators)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrapf(err, "failed to get %d deposits", numValidators))
|
||||
@@ -196,14 +196,14 @@ func DepositTrieFromDeposits(deposits []*ethpb.Deposit) (*trieutil.SparseMerkleT
|
||||
// ResetCache clears out the old trie, private keys and deposits.
|
||||
func ResetCache() {
|
||||
trie = nil
|
||||
privKeys = []*bls.SecretKey{}
|
||||
privKeys = []bls.SecretKey{}
|
||||
cachedDeposits = []*ethpb.Deposit{}
|
||||
}
|
||||
|
||||
// DeterministicDepositsAndKeysSameValidator returns the entered amount of deposits and secret keys
|
||||
// of the same validator. This is for negative test cases such as same deposits from same validators in a block don't
|
||||
// result in duplicated validator indices.
|
||||
func DeterministicDepositsAndKeysSameValidator(numDeposits uint64) ([]*ethpb.Deposit, []*bls.SecretKey, error) {
|
||||
func DeterministicDepositsAndKeysSameValidator(numDeposits uint64) ([]*ethpb.Deposit, []bls.SecretKey, error) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
var err error
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// RandaoReveal returns a signature of the requested epoch using the beacon proposer private key.
|
||||
func RandaoReveal(beaconState *stateTrie.BeaconState, epoch uint64, privKeys []*bls.SecretKey) ([]byte, error) {
|
||||
func RandaoReveal(beaconState *stateTrie.BeaconState, epoch uint64, privKeys []bls.SecretKey) ([]byte, error) {
|
||||
// We fetch the proposer's index as that is whom the RANDAO will be verified against.
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(beaconState)
|
||||
if err != nil {
|
||||
@@ -41,8 +41,8 @@ func RandaoReveal(beaconState *stateTrie.BeaconState, epoch uint64, privKeys []*
|
||||
func BlockSignature(
|
||||
bState *stateTrie.BeaconState,
|
||||
block *ethpb.BeaconBlock,
|
||||
privKeys []*bls.SecretKey,
|
||||
) (*bls.Signature, error) {
|
||||
privKeys []bls.SecretKey,
|
||||
) (bls.Signature, error) {
|
||||
var err error
|
||||
s, err := state.CalculateStateRoot(context.Background(), bState, ðpb.SignedBeaconBlock{Block: block})
|
||||
if err != nil {
|
||||
|
||||
@@ -71,7 +71,7 @@ func (ss *Server) IsSlashableAttestation(ctx context.Context, req *ethpb.Indexed
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubkeys := []*bls.PublicKey{}
|
||||
pubkeys := []bls.PublicKey{}
|
||||
for _, pkBytes := range pkMap {
|
||||
pk, err := bls.PublicKeyFromBytes(pkBytes[:])
|
||||
if err != nil {
|
||||
|
||||
@@ -86,7 +86,7 @@ func TestServer_IsSlashableAttestation(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var sig []*bls.Signature
|
||||
var sig []bls.Signature
|
||||
for _, idx := range incomingAtt.AttestingIndices {
|
||||
validatorSig := keys[idx].Sign(root[:])
|
||||
sig = append(sig, validatorSig)
|
||||
@@ -104,7 +104,7 @@ func TestServer_IsSlashableAttestation(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sig = []*bls.Signature{}
|
||||
sig = []bls.Signature{}
|
||||
for _, idx := range savedAttestation.AttestingIndices {
|
||||
validatorSig := keys[idx].Sign(root[:])
|
||||
sig = append(sig, validatorSig)
|
||||
@@ -195,7 +195,7 @@ func TestServer_IsSlashableAttestationNoUpdate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sig := []*bls.Signature{}
|
||||
sig := []bls.Signature{}
|
||||
for _, idx := range savedAttestation.AttestingIndices {
|
||||
validatorSig := keys[idx].Sign(root[:])
|
||||
sig = append(sig, validatorSig)
|
||||
|
||||
2
third_party/herumi/bls_eth_go_binary.BUILD
vendored
2
third_party/herumi/bls_eth_go_binary.BUILD
vendored
@@ -149,7 +149,7 @@ go_library(
|
||||
copts = OPTS,
|
||||
visibility = [
|
||||
# Additional access will require security approval.
|
||||
"@prysm//shared/bls:__pkg__",
|
||||
"@prysm//shared/bls/herumi:__pkg__",
|
||||
"@com_github_wealdtech_go_eth2_types_v2//:__pkg__",
|
||||
],
|
||||
clinkopts = select({
|
||||
|
||||
@@ -2,10 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/interop"
|
||||
"github.com/prysmaticlabs/prysm/tools/unencrypted-keys-gen/keygen"
|
||||
)
|
||||
|
||||
@@ -226,7 +226,7 @@ func (v *validator) signAtt(ctx context.Context, pubKey [48]byte, data *ethpb.At
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sig *bls.Signature
|
||||
var sig bls.Signature
|
||||
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
|
||||
sig, err = protectingKeymanager.SignAttestation(pubKey, bytesutil.ToBytes32(domain.SignatureDomain), data)
|
||||
} else {
|
||||
|
||||
@@ -189,7 +189,7 @@ func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch uint64
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get domain data")
|
||||
}
|
||||
var sig *bls.Signature
|
||||
var sig bls.Signature
|
||||
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
|
||||
bodyRoot, err := stateutil.BlockBodyRoot(b.Body)
|
||||
if err != nil {
|
||||
|
||||
@@ -180,7 +180,7 @@ func (v *ValidatorService) Status() error {
|
||||
}
|
||||
|
||||
// signObject signs a generic object, with protection if available.
|
||||
func (v *validator) signObject(pubKey [48]byte, object interface{}, domain []byte) (*bls.Signature, error) {
|
||||
func (v *validator) signObject(pubKey [48]byte, object interface{}, domain []byte) (bls.Signature, error) {
|
||||
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
|
||||
root, err := ssz.HashTreeRoot(object)
|
||||
if err != nil {
|
||||
|
||||
@@ -36,11 +36,11 @@ func keySetup() {
|
||||
copy(validatorPubKey[:], validatorKey.PublicKey.Marshal())
|
||||
keyMap[validatorPubKey] = validatorKey
|
||||
|
||||
sks := make([]*bls.SecretKey, 1)
|
||||
sks := make([]bls.SecretKey, 1)
|
||||
sks[0] = validatorKey.SecretKey
|
||||
testKeyManager = keymanager.NewDirect(sks)
|
||||
|
||||
sks = make([]*bls.SecretKey, 3)
|
||||
sks = make([]bls.SecretKey, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
vKey, err := keystore.NewKey()
|
||||
if err != nil {
|
||||
|
||||
@@ -809,7 +809,7 @@ func TestRolesAt_OK(t *testing.T) {
|
||||
v, m, finish := setup(t)
|
||||
defer finish()
|
||||
|
||||
sks := make([]*bls.SecretKey, 4)
|
||||
sks := make([]bls.SecretKey, 4)
|
||||
sks[0] = bls.RandKey()
|
||||
sks[1] = bls.RandKey()
|
||||
sks[2] = bls.RandKey()
|
||||
@@ -880,7 +880,7 @@ func TestRolesAt_DoesNotAssignProposer_Slot0(t *testing.T) {
|
||||
v, m, finish := setup(t)
|
||||
defer finish()
|
||||
|
||||
sks := make([]*bls.SecretKey, 3)
|
||||
sks := make([]bls.SecretKey, 3)
|
||||
sks[0] = bls.RandKey()
|
||||
sks[1] = bls.RandKey()
|
||||
sks[2] = bls.RandKey()
|
||||
|
||||
@@ -155,7 +155,7 @@ func (v *validator) signAtt(ctx context.Context, pubKey [48]byte, data *ethpb.At
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sig *bls.Signature
|
||||
var sig bls.Signature
|
||||
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
|
||||
sig, err = protectingKeymanager.SignAttestation(pubKey, bytesutil.ToBytes32(domain.SignatureDomain), data)
|
||||
} else {
|
||||
|
||||
@@ -191,7 +191,7 @@ func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch uint64
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get domain data")
|
||||
}
|
||||
var sig *bls.Signature
|
||||
var sig bls.Signature
|
||||
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
|
||||
bodyRoot, err := stateutil.BlockBodyRoot(b.Body)
|
||||
if err != nil {
|
||||
|
||||
@@ -172,7 +172,7 @@ func (v *ValidatorService) Status() error {
|
||||
}
|
||||
|
||||
// signObject signs a generic object, with protection if available.
|
||||
func (v *validator) signObject(pubKey [48]byte, object interface{}, domain []byte) (*bls.Signature, error) {
|
||||
func (v *validator) signObject(pubKey [48]byte, object interface{}, domain []byte) (bls.Signature, error) {
|
||||
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
|
||||
root, err := ssz.HashTreeRoot(object)
|
||||
if err != nil {
|
||||
|
||||
@@ -36,11 +36,11 @@ func keySetup() {
|
||||
copy(validatorPubKey[:], validatorKey.PublicKey.Marshal())
|
||||
keyMap[validatorPubKey] = validatorKey
|
||||
|
||||
sks := make([]*bls.SecretKey, 1)
|
||||
sks := make([]bls.SecretKey, 1)
|
||||
sks[0] = validatorKey.SecretKey
|
||||
testKeyManager = keymanager.NewDirect(sks)
|
||||
|
||||
sks = make([]*bls.SecretKey, 3)
|
||||
sks = make([]bls.SecretKey, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
vKey, err := keystore.NewKey()
|
||||
if err != nil {
|
||||
|
||||
@@ -639,7 +639,7 @@ func TestRolesAt_OK(t *testing.T) {
|
||||
v, m, finish := setup(t)
|
||||
defer finish()
|
||||
|
||||
sks := make([]*bls.SecretKey, 4)
|
||||
sks := make([]bls.SecretKey, 4)
|
||||
sks[0] = bls.RandKey()
|
||||
sks[1] = bls.RandKey()
|
||||
sks[2] = bls.RandKey()
|
||||
@@ -710,7 +710,7 @@ func TestRolesAt_DoesNotAssignProposer_Slot0(t *testing.T) {
|
||||
v, m, finish := setup(t)
|
||||
defer finish()
|
||||
|
||||
sks := make([]*bls.SecretKey, 3)
|
||||
sks := make([]bls.SecretKey, 3)
|
||||
sks[0] = bls.RandKey()
|
||||
sks[1] = bls.RandKey()
|
||||
sks[2] = bls.RandKey()
|
||||
|
||||
@@ -8,16 +8,16 @@ import (
|
||||
// Direct is a key manager that holds all secret keys directly.
|
||||
type Direct struct {
|
||||
// Key to the map is the bytes of the public key.
|
||||
publicKeys map[[48]byte]*bls.PublicKey
|
||||
publicKeys map[[48]byte]bls.PublicKey
|
||||
// Key to the map is the bytes of the public key.
|
||||
secretKeys map[[48]byte]*bls.SecretKey
|
||||
secretKeys map[[48]byte]bls.SecretKey
|
||||
}
|
||||
|
||||
// NewDirect creates a new direct key manager from the secret keys provided to it.
|
||||
func NewDirect(sks []*bls.SecretKey) *Direct {
|
||||
func NewDirect(sks []bls.SecretKey) *Direct {
|
||||
res := &Direct{
|
||||
publicKeys: make(map[[48]byte]*bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]*bls.SecretKey),
|
||||
publicKeys: make(map[[48]byte]bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]bls.SecretKey),
|
||||
}
|
||||
for _, sk := range sks {
|
||||
publicKey := sk.PublicKey()
|
||||
@@ -38,7 +38,7 @@ func (km *Direct) FetchValidatingKeys() ([][48]byte, error) {
|
||||
}
|
||||
|
||||
// Sign signs a message for the validator to broadcast.
|
||||
func (km *Direct) Sign(pubKey [48]byte, root [32]byte) (*bls.Signature, error) {
|
||||
func (km *Direct) Sign(pubKey [48]byte, root [32]byte) (bls.Signature, error) {
|
||||
if secretKey, exists := km.secretKeys[pubKey]; exists {
|
||||
return secretKey.Sign(root[:]), nil
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ func NewInterop(input string) (*Interop, string, error) {
|
||||
|
||||
km := &Interop{
|
||||
Direct: &Direct{
|
||||
publicKeys: make(map[[48]byte]*bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]*bls.SecretKey),
|
||||
publicKeys: make(map[[48]byte]bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]bls.SecretKey),
|
||||
},
|
||||
}
|
||||
for i := 0; uint64(i) < opts.Keys; i++ {
|
||||
|
||||
@@ -79,8 +79,8 @@ func NewKeystore(input string) (KeyManager, string, error) {
|
||||
|
||||
km := &Unencrypted{
|
||||
Direct: &Direct{
|
||||
publicKeys: make(map[[48]byte]*bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]*bls.SecretKey),
|
||||
publicKeys: make(map[[48]byte]bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]bls.SecretKey),
|
||||
},
|
||||
}
|
||||
for _, key := range keyMap {
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestDirectListValidatingKeysNil(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectListValidatingKeysSingle(t *testing.T) {
|
||||
sks := make([]*bls.SecretKey, 0)
|
||||
sks := make([]bls.SecretKey, 0)
|
||||
sks = append(sks, bls.RandKey())
|
||||
direct := keymanager.NewDirect(sks)
|
||||
keys, err := direct.FetchValidatingKeys()
|
||||
@@ -34,7 +34,7 @@ func TestDirectListValidatingKeysSingle(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectListValidatingKeysMultiple(t *testing.T) {
|
||||
sks := make([]*bls.SecretKey, 0)
|
||||
sks := make([]bls.SecretKey, 0)
|
||||
numKeys := 256
|
||||
for i := 0; i < numKeys; i++ {
|
||||
sks = append(sks, bls.RandKey())
|
||||
@@ -50,7 +50,7 @@ func TestDirectListValidatingKeysMultiple(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSignNoSuchKey(t *testing.T) {
|
||||
sks := make([]*bls.SecretKey, 0)
|
||||
sks := make([]bls.SecretKey, 0)
|
||||
// sks = append(sks, bls.RandKey())
|
||||
direct := keymanager.NewDirect(sks)
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestSignNoSuchKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
sks := make([]*bls.SecretKey, 0)
|
||||
sks := make([]bls.SecretKey, 0)
|
||||
sks = append(sks, bls.RandKey())
|
||||
direct := keymanager.NewDirect(sks)
|
||||
|
||||
|
||||
@@ -58,15 +58,15 @@ func NewUnencrypted(input string) (*Unencrypted, string, error) {
|
||||
if err != nil {
|
||||
return nil, unencryptedOptsHelp, err
|
||||
}
|
||||
sks := make([]*bls.SecretKey, 0, len(keyMap))
|
||||
sks := make([]bls.SecretKey, 0, len(keyMap))
|
||||
for _, key := range keyMap {
|
||||
sks = append(sks, key)
|
||||
}
|
||||
|
||||
km := &Unencrypted{
|
||||
Direct: &Direct{
|
||||
publicKeys: make(map[[48]byte]*bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]*bls.SecretKey),
|
||||
publicKeys: make(map[[48]byte]bls.PublicKey),
|
||||
secretKeys: make(map[[48]byte]bls.SecretKey),
|
||||
},
|
||||
}
|
||||
for i := 0; i < len(sks); i++ {
|
||||
@@ -87,7 +87,7 @@ type unencryptedKeys struct {
|
||||
}
|
||||
|
||||
// unencryptedKeysFromReader loads the unencrypted keys from the given reader.
|
||||
func unencryptedKeysFromReader(reader io.Reader) ([]*bls.SecretKey, error) {
|
||||
func unencryptedKeysFromReader(reader io.Reader) ([]bls.SecretKey, error) {
|
||||
log.Warn("Loading encrypted keys from disk. Do not do this in production!")
|
||||
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
@@ -99,7 +99,7 @@ func unencryptedKeysFromReader(reader io.Reader) ([]*bls.SecretKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]*bls.SecretKey, 0, len(ctnr.Keys))
|
||||
res := make([]bls.SecretKey, 0, len(ctnr.Keys))
|
||||
for i := range ctnr.Keys {
|
||||
secretKey, err := bls.SecretKeyFromBytes(ctnr.Keys[i].ValidatorKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -22,18 +22,18 @@ type KeyManager interface {
|
||||
FetchValidatingKeys() ([][48]byte, error)
|
||||
// Sign signs a message for the validator to broadcast.
|
||||
// Note that the domain should already be part of the root, but it is passed along for security purposes.
|
||||
Sign(pubKey [48]byte, root [32]byte) (*bls.Signature, error)
|
||||
Sign(pubKey [48]byte, root [32]byte) (bls.Signature, error)
|
||||
}
|
||||
|
||||
// ProtectingKeyManager provides access to a keymanager that protects its clients from slashing events.
|
||||
type ProtectingKeyManager interface {
|
||||
// SignGeneric signs a generic root.
|
||||
// Note that the domain should already be part of the root, but it is provided for authorisation purposes.
|
||||
SignGeneric(pubKey [48]byte, root [32]byte, domain [32]byte) (*bls.Signature, error)
|
||||
SignGeneric(pubKey [48]byte, root [32]byte, domain [32]byte) (bls.Signature, error)
|
||||
|
||||
// SignProposal signs a block proposal for the validator to broadcast.
|
||||
SignProposal(pubKey [48]byte, domain [32]byte, data *ethpb.BeaconBlockHeader) (*bls.Signature, error)
|
||||
SignProposal(pubKey [48]byte, domain [32]byte, data *ethpb.BeaconBlockHeader) (bls.Signature, error)
|
||||
|
||||
// SignAttestation signs an attestation for the validator to broadcast.
|
||||
SignAttestation(pubKey [48]byte, domain [32]byte, data *ethpb.AttestationData) (*bls.Signature, error)
|
||||
SignAttestation(pubKey [48]byte, domain [32]byte, data *ethpb.AttestationData) (bls.Signature, error)
|
||||
}
|
||||
|
||||
@@ -155,12 +155,12 @@ func (km *Remote) FetchValidatingKeys() ([][48]byte, error) {
|
||||
}
|
||||
|
||||
// Sign without protection is not supported by remote keymanagers.
|
||||
func (km *Remote) Sign(pubKey [48]byte, root [32]byte) (*bls.Signature, error) {
|
||||
func (km *Remote) Sign(pubKey [48]byte, root [32]byte) (bls.Signature, error) {
|
||||
return nil, errors.New("remote keymanager does not support unprotected signing")
|
||||
}
|
||||
|
||||
// SignGeneric signs a generic message for the validator to broadcast.
|
||||
func (km *Remote) SignGeneric(pubKey [48]byte, root [32]byte, domain [32]byte) (*bls.Signature, error) {
|
||||
func (km *Remote) SignGeneric(pubKey [48]byte, root [32]byte, domain [32]byte) (bls.Signature, error) {
|
||||
accountInfo, exists := km.accounts[pubKey]
|
||||
if !exists {
|
||||
return nil, ErrNoSuchKey
|
||||
@@ -186,7 +186,7 @@ func (km *Remote) SignGeneric(pubKey [48]byte, root [32]byte, domain [32]byte) (
|
||||
}
|
||||
|
||||
// SignProposal signs a block proposal for the validator to broadcast.
|
||||
func (km *Remote) SignProposal(pubKey [48]byte, domain [32]byte, data *ethpb.BeaconBlockHeader) (*bls.Signature, error) {
|
||||
func (km *Remote) SignProposal(pubKey [48]byte, domain [32]byte, data *ethpb.BeaconBlockHeader) (bls.Signature, error) {
|
||||
accountInfo, exists := km.accounts[pubKey]
|
||||
if !exists {
|
||||
return nil, ErrNoSuchKey
|
||||
@@ -218,7 +218,7 @@ func (km *Remote) SignProposal(pubKey [48]byte, domain [32]byte, data *ethpb.Bea
|
||||
}
|
||||
|
||||
// SignAttestation signs an attestation for the validator to broadcast.
|
||||
func (km *Remote) SignAttestation(pubKey [48]byte, domain [32]byte, data *ethpb.AttestationData) (*bls.Signature, error) {
|
||||
func (km *Remote) SignAttestation(pubKey [48]byte, domain [32]byte, data *ethpb.AttestationData) (bls.Signature, error) {
|
||||
accountInfo, exists := km.accounts[pubKey]
|
||||
if !exists {
|
||||
return nil, ErrNoSuchKey
|
||||
|
||||
@@ -134,7 +134,7 @@ func (km *Wallet) FetchValidatingKeys() ([][48]byte, error) {
|
||||
}
|
||||
|
||||
// Sign signs a message for the validator to broadcast.
|
||||
func (km *Wallet) Sign(pubKey [48]byte, root [32]byte) (*bls.Signature, error) {
|
||||
func (km *Wallet) Sign(pubKey [48]byte, root [32]byte) (bls.Signature, error) {
|
||||
account, exists := km.accounts[pubKey]
|
||||
if !exists {
|
||||
return nil, ErrNoSuchKey
|
||||
|
||||
Reference in New Issue
Block a user