-
Notifications
You must be signed in to change notification settings - Fork 761
BLS Components for Simplex #3993
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+323
−0
Merged
Changes from 7 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
dde076d
add simplex pkg and bls structs
samliok c5be0a4
add simplex to go mod + lint
samliok 52d343e
add log to createVerifier
samliok d0f0102
Merge branch 'master' into simplex-bls
samliok 458d540
separate into utils file
samliok 49aa0bb
style improvements
samliok 4dfdb34
use GetValidatorSet for consistent validator set
samliok 475a5ef
Merge branch 'master' into simplex-bls
samliok 8c78c03
pass in membership set to config
samliok 14ad42f
remove comment
samliok 2580d63
Merge branch 'master' into simplex-bls
samliok ed4422b
test_util simplified
samliok 88b06d5
go get
samliok f3410c3
add codec.Manager
samliok 46d949a
Merge branch 'master' into simplex-bls
samliok 3e86cd5
use network id over subnet id
samliok 317c9b5
add table tests for bls verifier
samliok 6596297
lint
samliok 5892918
testing cleanup, comment fixes, remove println
samliok 9f5c59f
update docs of config struct
samliok File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package simplex | ||
|
||
import ( | ||
"context" | ||
"encoding/asn1" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/ava-labs/simplex" | ||
"go.uber.org/zap" | ||
|
||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/utils/crypto/bls" | ||
) | ||
|
||
var ( | ||
errSignatureVerificationFailed = errors.New("signature verification failed") | ||
errSignerNotFound = errors.New("signer not found in the membership set") | ||
simplexLabel = []byte("simplex") | ||
) | ||
|
||
var _ simplex.Signer = (*BLSSigner)(nil) | ||
|
||
type SignFunc func(msg []byte) (*bls.Signature, error) | ||
|
||
// BLSSigner signs messages encoded with the provided ChainID and SubnetID. | ||
// using the SignBLS function. | ||
type BLSSigner struct { | ||
chainID ids.ID | ||
subnetID ids.ID | ||
// signBLS is passed in because we support both software and hardware BLS signing. | ||
signBLS SignFunc | ||
} | ||
|
||
type BLSVerifier struct { | ||
nodeID2PK map[ids.NodeID]bls.PublicKey | ||
subnetID ids.ID | ||
chainID ids.ID | ||
} | ||
|
||
func NewBLSAuth(config *Config) (BLSSigner, BLSVerifier, error) { | ||
verifier, err := createVerifier(config) | ||
|
||
return BLSSigner{ | ||
chainID: config.Ctx.ChainID, | ||
subnetID: config.Ctx.SubnetID, | ||
signBLS: config.SignBLS, | ||
}, verifier, err | ||
} | ||
|
||
// Sign returns a signature on the given message using BLS signature scheme. | ||
// It encodes the message to sign with the chain ID, and subnet ID, | ||
func (s *BLSSigner) Sign(message []byte) ([]byte, error) { | ||
message2Sign, err := encodeMessageToSign(message, s.chainID, s.subnetID) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to encode message to sign: %w", err) | ||
} | ||
|
||
sig, err := s.signBLS(message2Sign) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
sigBytes := bls.SignatureToBytes(sig) | ||
return sigBytes, nil | ||
} | ||
StephenButtolph marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
type encodedSimplexSignedPayload struct { | ||
Message []byte | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ChainID []byte | ||
SubnetID []byte | ||
Label []byte | ||
} | ||
|
||
func encodeMessageToSign(message []byte, chainID ids.ID, subnetID ids.ID) ([]byte, error) { | ||
encodedSimplexMessage := encodedSimplexSignedPayload{ | ||
Message: message, | ||
ChainID: chainID[:], | ||
SubnetID: subnetID[:], | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Label: simplexLabel, | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return asn1.Marshal(encodedSimplexMessage) | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
func (v BLSVerifier) Verify(message []byte, signature []byte, signer simplex.NodeID) error { | ||
if len(signer) != ids.NodeIDLen { | ||
return fmt.Errorf("expected signer to be %d bytes but got %d bytes", ids.NodeIDLen, len(signer)) | ||
} | ||
|
||
key := ids.NodeID(signer) | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pk, exists := v.nodeID2PK[key] | ||
if !exists { | ||
return fmt.Errorf("%w: signer %x", errSignerNotFound, key) | ||
} | ||
|
||
sig, err := bls.SignatureFromBytes(signature) | ||
if err != nil { | ||
return fmt.Errorf("failed to parse signature: %w", err) | ||
} | ||
|
||
message2Verify, err := encodeMessageToSign(message, v.chainID, v.subnetID) | ||
if err != nil { | ||
return fmt.Errorf("failed to encode message to verify: %w", err) | ||
} | ||
|
||
if !bls.Verify(&pk, sig, message2Verify) { | ||
return errSignatureVerificationFailed | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func createVerifier(config *Config) (BLSVerifier, error) { | ||
verifier := BLSVerifier{ | ||
nodeID2PK: make(map[ids.NodeID]bls.PublicKey), | ||
subnetID: config.Ctx.SubnetID, | ||
chainID: config.Ctx.ChainID, | ||
} | ||
|
||
nodes, err := config.Validators.GetValidatorSet(context.Background(), 0, config.Ctx.SubnetID) | ||
StephenButtolph marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
config.Log.Error("failed to get validator set", zap.Error(err), zap.Stringer("subnetID", config.Ctx.SubnetID)) | ||
return BLSVerifier{}, err | ||
} | ||
|
||
for _, node := range nodes { | ||
verifier.nodeID2PK[node.NodeID] = *node.PublicKey | ||
} | ||
|
||
return verifier, nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package simplex | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/utils/crypto/bls" | ||
) | ||
|
||
func TestBLSSignVerify(t *testing.T) { | ||
config, err := newEngineConfig() | ||
require.NoError(t, err) | ||
|
||
signer, verifier, err := NewBLSAuth(config) | ||
require.NoError(t, err) | ||
|
||
msg := "Begin at the beginning, and go on till you come to the end: then stop" | ||
|
||
sig, err := signer.Sign([]byte(msg)) | ||
require.NoError(t, err) | ||
|
||
err = verifier.Verify([]byte(msg), sig, config.Ctx.NodeID[:]) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestSignerNotInMemberSet(t *testing.T) { | ||
config, err := newEngineConfig() | ||
require.NoError(t, err) | ||
signer, verifier, err := NewBLSAuth(config) | ||
require.NoError(t, err) | ||
|
||
msg := "Begin at the beginning, and go on till you come to the end: then stop" | ||
|
||
sig, err := signer.Sign([]byte(msg)) | ||
require.NoError(t, err) | ||
|
||
notInMembershipSet := ids.GenerateTestNodeID() | ||
err = verifier.Verify([]byte(msg), sig, notInMembershipSet[:]) | ||
require.ErrorIs(t, err, errSignerNotFound) | ||
} | ||
|
||
func TestSignerInvalidMessageEncoding(t *testing.T) { | ||
config, err := newEngineConfig() | ||
require.NoError(t, err) | ||
|
||
// sign a message with invalid encoding | ||
dummyMsg := []byte("dummy message") | ||
sig, err := config.SignBLS(dummyMsg) | ||
require.NoError(t, err) | ||
|
||
sigBytes := bls.SignatureToBytes(sig) | ||
|
||
_, verifier, err := NewBLSAuth(config) | ||
require.NoError(t, err) | ||
err = verifier.Verify(dummyMsg, sigBytes, config.Ctx.NodeID[:]) | ||
require.ErrorIs(t, err, errSignatureVerificationFailed) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package simplex | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/snow/validators" | ||
"github.com/ava-labs/avalanchego/utils/logging" | ||
) | ||
|
||
type ValidatorInfo interface { | ||
// GetValidatorSet returns the validators of the provided subnet at the | ||
// requested P-chain height. | ||
// The returned map should not be modified. | ||
GetValidatorSet( | ||
ctx context.Context, | ||
height uint64, | ||
subnetID ids.ID, | ||
) (map[ids.NodeID]*validators.GetValidatorOutput, error) | ||
} | ||
|
||
// Config wraps all the parameters needed for a simplex engine | ||
type Config struct { | ||
Ctx SimplexChainContext | ||
Log logging.Logger | ||
Validators ValidatorInfo | ||
SignBLS SignFunc | ||
} | ||
|
||
// Context is information about the current execution. | ||
// [SubnetID] is the ID of the subnet this context exists within. | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// [ChainID] is the ID of the chain this context exists within. | ||
// [NodeID] is the ID of this node | ||
StephenButtolph marked this conversation as resolved.
Show resolved
Hide resolved
|
||
type SimplexChainContext struct { | ||
NodeID ids.NodeID | ||
ChainID ids.ID | ||
SubnetID ids.ID | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package simplex | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/snow/validators" | ||
"github.com/ava-labs/avalanchego/utils/crypto/bls" | ||
"github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" | ||
) | ||
|
||
var _ ValidatorInfo = (*testValidatorInfo)(nil) | ||
|
||
// testValidatorInfo is a mock implementation of ValidatorInfo for testing purposes. | ||
// it assumes all validators are in the same subnet and returns all of them for any subnetID. | ||
type testValidatorInfo struct { | ||
validators map[ids.NodeID]*validators.GetValidatorOutput | ||
} | ||
|
||
func (t *testValidatorInfo) GetValidatorSet( | ||
context.Context, | ||
uint64, | ||
ids.ID, | ||
) (map[ids.NodeID]*validators.GetValidatorOutput, error) { | ||
return t.validators, nil | ||
} | ||
|
||
func newTestValidatorInfo(nodeIds []ids.NodeID, pks []*bls.PublicKey) *testValidatorInfo { | ||
if len(nodeIds) != len(pks) { | ||
panic("nodeIds and pks must have the same length") | ||
} | ||
|
||
vds := make(map[ids.NodeID]*validators.GetValidatorOutput, len(pks)) | ||
for i, pk := range pks { | ||
validator := &validators.GetValidatorOutput{ | ||
PublicKey: pk, | ||
NodeID: nodeIds[i], | ||
} | ||
vds[nodeIds[i]] = validator | ||
} | ||
// all we need is to generate the public keys for the validators | ||
return &testValidatorInfo{ | ||
validators: vds, | ||
} | ||
} | ||
|
||
func newEngineConfig() (*Config, error) { | ||
ls, err := localsigner.New() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
nodeID := ids.GenerateTestNodeID() | ||
|
||
simplexChainContext := SimplexChainContext{ | ||
NodeID: nodeID, | ||
ChainID: ids.GenerateTestID(), | ||
SubnetID: ids.GenerateTestID(), | ||
} | ||
|
||
return &Config{ | ||
Ctx: simplexChainContext, | ||
Validators: newTestValidatorInfo([]ids.NodeID{nodeID}, []*bls.PublicKey{ls.PublicKey()}), | ||
SignBLS: ls.Sign, | ||
}, nil | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.