mirror of
https://github.com/luxfi/coreth.git
synced 2026-10-30 17:00:51 +00:00
This module would not build against the warp it resolves. warp v1.24 removed the
UnsignedMessage / Message split, and the rename is a rotation, not a substitution
— every name shifted one place along:
UnsignedMessage -> Message (the signed subject)
NewUnsignedMessage -> NewMessage
ParseUnsignedMessage -> ParseMessage
Message -> Envelope (subject + lanes)
NewMessage -> NewEnvelope
ParseMessage -> ParseEnvelope
VerifyMessage -> VerifyEnvelope
.UnsignedMessage -> .Message
.Signature -> .Beam
Applying it in the obvious order silently conflates the two Message meanings, so
the rewrite goes through placeholders.
The fold is a security fix upstream, not cosmetic: Message now carries the Pulsar
lineage (SourceNebulaRoot, SourceKeyEraID, SourceGeneration, HashSuiteID) inside
the signed subject. Under the old split the BLS Beam signed only the unsigned
body and that lineage was Beam-unauthenticated.
VerifyEnvelope takes the same five arguments VerifyMessage did and is documented
as the canonical classical path, so verification semantics are unchanged here.
VerifyWithOptions is the opt-in door to the Corona and ML-DSA lanes; nothing in
this module opts in, so no PQ policy is decided by this commit.
Two shape changes fall out of Envelope holding concrete lanes:
- Beam is a BitSetSignature value, not an interface, so the signer-count
type-assertion in the gas path is unreachable and its unknown-signature-type
branch is gone. A parsed envelope always has a Beam to count.
- Envelope.Bytes reports c14n failures instead of panicking, so the RPC path
surfaces that error rather than returning a truncated message.
NewEnvelope also takes the Corona and ML-DSA cert-set lanes; the test aggregator
is Beam-only and passes nil for both.
Builds clean, go vet clean, warp + precompile + aggregator suites pass.
Co-authored-by: Hanzo Dev <[email protected]>
692 lines
20 KiB
Go
692 lines
20 KiB
Go
//go:build skip
|
|
|
|
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package warp
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"testing"
|
|
|
|
validators "github.com/luxfi/consensus/validator"
|
|
"github.com/luxfi/consensus/validator/validatorsmock"
|
|
"github.com/luxfi/constants"
|
|
"github.com/luxfi/crypto/bls"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/math/set"
|
|
"github.com/luxfi/warp"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
)
|
|
|
|
type signatureTest struct {
|
|
name string
|
|
stateF func(*gomock.Controller) validators.State
|
|
quorumNum uint64
|
|
quorumDen uint64
|
|
msgF func(*require.Assertions) *warp.Envelope
|
|
verifyErr error
|
|
canonicalErr error
|
|
}
|
|
|
|
// This test copies the test coverage from https://github.com/luxfi/node/blob/0117ab96/vms/platformvm/warp/signature_test.go#L137.
|
|
// These tests are only expected to fail if there is a breaking change in Luxd that unexpectedly changes behavior.
|
|
func TestSignatureVerification(t *testing.T) {
|
|
tests := []signatureTest{
|
|
{
|
|
name: "can't get chainID",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, errTest)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
canonicalErr: errTest,
|
|
},
|
|
{
|
|
name: "can't get validator set",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(nil, errTest)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
canonicalErr: errTest,
|
|
},
|
|
{
|
|
name: "weight overflow",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
|
|
testVdrs[0].nodeID: {
|
|
NodeID: testVdrs[0].nodeID,
|
|
PublicKey: testVdrs[0].vdr.PublicKey,
|
|
Weight: math.MaxUint64,
|
|
},
|
|
testVdrs[1].nodeID: {
|
|
NodeID: testVdrs[1].nodeID,
|
|
PublicKey: testVdrs[1].vdr.PublicKey,
|
|
Weight: math.MaxUint64,
|
|
},
|
|
}, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: make([]byte, 8),
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
canonicalErr: warp.ErrWeightOverflow,
|
|
},
|
|
{
|
|
name: "invalid bit set index",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: make([]byte, 1),
|
|
Signature: [bls.SignatureLen]byte{},
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: warp.ErrInvalidBitSet,
|
|
},
|
|
{
|
|
name: "unknown index",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
signers := set.NewBits()
|
|
signers.Add(3) // vdr oob
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: [bls.SignatureLen]byte{},
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: warp.ErrUnknownValidator,
|
|
},
|
|
{
|
|
name: "insufficient weight",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 1,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
// [signers] has weight from [vdr[0], vdr[1]],
|
|
// which is 6, which is less than 9
|
|
signers := set.NewBits()
|
|
signers.Add(0)
|
|
signers.Add(1)
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr0Sig, err := testVdrs[0].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
vdr1Sig, err := testVdrs[1].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr0Sig, vdr1Sig})
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: warp.ErrInsufficientWeight,
|
|
},
|
|
{
|
|
name: "can't parse sig",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
signers := set.NewBits()
|
|
signers.Add(0)
|
|
signers.Add(1)
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: [bls.SignatureLen]byte{},
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: warp.ErrParseSignature,
|
|
},
|
|
{
|
|
name: "no validators",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(nil, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr0Sig, err := testVdrs[0].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(vdr0Sig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: nil,
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: bls.ErrNoPublicKeys,
|
|
},
|
|
{
|
|
name: "invalid signature (substitute)",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 3,
|
|
quorumDen: 5,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
signers := set.NewBits()
|
|
signers.Add(0)
|
|
signers.Add(1)
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr0Sig, err := testVdrs[0].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
// Give sig from vdr[2] even though the bit vector says it
|
|
// should be from vdr[1]
|
|
vdr2Sig, err := testVdrs[2].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr0Sig, vdr2Sig})
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: warp.ErrInvalidSignature,
|
|
},
|
|
{
|
|
name: "invalid signature (missing one)",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 3,
|
|
quorumDen: 5,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
signers := set.NewBits()
|
|
signers.Add(0)
|
|
signers.Add(1)
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr0Sig, err := testVdrs[0].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
// Don't give the sig from vdr[1]
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(vdr0Sig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: warp.ErrInvalidSignature,
|
|
},
|
|
{
|
|
name: "invalid signature (extra one)",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 3,
|
|
quorumDen: 5,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
signers := set.NewBits()
|
|
signers.Add(0)
|
|
signers.Add(1)
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr0Sig, err := testVdrs[0].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
vdr1Sig, err := testVdrs[1].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
// Give sig from vdr[2] even though the bit vector doesn't have
|
|
// it
|
|
vdr2Sig, err := testVdrs[2].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr0Sig, vdr1Sig, vdr2Sig})
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: warp.ErrInvalidSignature,
|
|
},
|
|
{
|
|
name: "valid signature",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 2,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
// [signers] has weight from [vdr[1], vdr[2]],
|
|
// which is 6, which is greater than 4.5
|
|
signers := set.NewBits()
|
|
signers.Add(1)
|
|
signers.Add(2)
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr1Sig, err := testVdrs[1].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
vdr2Sig, err := testVdrs[2].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr1Sig, vdr2Sig})
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: nil,
|
|
},
|
|
{
|
|
name: "valid signature (boundary)",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(vdrs, nil)
|
|
return state
|
|
},
|
|
quorumNum: 2,
|
|
quorumDen: 3,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
// [signers] has weight from [vdr[1], vdr[2]],
|
|
// which is 6, which meets the minimum 6
|
|
signers := set.NewBits()
|
|
signers.Add(1)
|
|
signers.Add(2)
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr1Sig, err := testVdrs[1].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
vdr2Sig, err := testVdrs[2].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr1Sig, vdr2Sig})
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: nil,
|
|
},
|
|
{
|
|
name: "valid signature (missing key)",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
|
|
testVdrs[0].nodeID: {
|
|
NodeID: testVdrs[0].nodeID,
|
|
PublicKey: nil,
|
|
Weight: testVdrs[0].vdr.Weight,
|
|
},
|
|
testVdrs[1].nodeID: {
|
|
NodeID: testVdrs[1].nodeID,
|
|
PublicKey: testVdrs[1].vdr.PublicKey,
|
|
Weight: testVdrs[1].vdr.Weight,
|
|
},
|
|
testVdrs[2].nodeID: {
|
|
NodeID: testVdrs[2].nodeID,
|
|
PublicKey: testVdrs[2].vdr.PublicKey,
|
|
Weight: testVdrs[2].vdr.Weight,
|
|
},
|
|
}, nil)
|
|
return state
|
|
},
|
|
quorumNum: 1,
|
|
quorumDen: 3,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
// [signers] has weight from [vdr2, vdr3],
|
|
// which is 6, which is greater than 3
|
|
signers := set.NewBits()
|
|
// Note: the bits are shifted because vdr[0]'s key was zeroed
|
|
signers.Add(0) // vdr[1]
|
|
signers.Add(1) // vdr[2]
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
vdr1Sig, err := testVdrs[1].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
vdr2Sig, err := testVdrs[2].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr1Sig, vdr2Sig})
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: nil,
|
|
},
|
|
{
|
|
name: "valid signature (duplicate key)",
|
|
stateF: func(ctrl *gomock.Controller) validators.State {
|
|
state := validatorsmock.NewState(ctrl)
|
|
state.EXPECT().GetChainID(gomock.Any(), sourceChainID).Return(sourceChainID, nil)
|
|
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceChainID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
|
|
testVdrs[0].nodeID: {
|
|
NodeID: testVdrs[0].nodeID,
|
|
PublicKey: nil,
|
|
Weight: testVdrs[0].vdr.Weight,
|
|
},
|
|
testVdrs[1].nodeID: {
|
|
NodeID: testVdrs[1].nodeID,
|
|
PublicKey: testVdrs[2].vdr.PublicKey,
|
|
Weight: testVdrs[1].vdr.Weight,
|
|
},
|
|
testVdrs[2].nodeID: {
|
|
NodeID: testVdrs[2].nodeID,
|
|
PublicKey: testVdrs[2].vdr.PublicKey,
|
|
Weight: testVdrs[2].vdr.Weight,
|
|
},
|
|
}, nil)
|
|
return state
|
|
},
|
|
quorumNum: 2,
|
|
quorumDen: 3,
|
|
msgF: func(require *require.Assertions) *warp.Envelope {
|
|
unsignedMsg, err := warp.NewMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedPayloadBytes,
|
|
)
|
|
require.NoError(err)
|
|
|
|
// [signers] has weight from [vdr2, vdr3],
|
|
// which is 6, which meets the minimum 6
|
|
signers := set.NewBits()
|
|
// Note: the bits are shifted because vdr[0]'s key was zeroed
|
|
// Note: vdr[1] and vdr[2] were combined because of a shared pk
|
|
signers.Add(0) // vdr[1] + vdr[2]
|
|
|
|
unsignedBytes := unsignedMsg.Bytes()
|
|
// Because vdr[1] and vdr[2] share a key, only one of them sign.
|
|
vdr2Sig, err := testVdrs[2].sk.Sign(unsignedBytes)
|
|
require.NoError(err)
|
|
aggSigBytes := [bls.SignatureLen]byte{}
|
|
copy(aggSigBytes[:], bls.SignatureToBytes(vdr2Sig))
|
|
|
|
msg, err := warp.NewEnvelope(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: signers.Bytes(),
|
|
Signature: aggSigBytes,
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
return msg
|
|
},
|
|
verifyErr: nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
msg := tt.msgF(require)
|
|
pChainState := tt.stateF(ctrl)
|
|
|
|
validatorSet, err := warp.GetCanonicalValidatorSetFromChainID(
|
|
context.Background(),
|
|
pChainState,
|
|
pChainHeight,
|
|
msg.Message.SourceChainID,
|
|
)
|
|
require.ErrorIs(err, tt.canonicalErr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = msg.Signature.Verify(
|
|
&msg.Message,
|
|
constants.UnitTestID,
|
|
validatorSet,
|
|
tt.quorumNum,
|
|
tt.quorumDen,
|
|
)
|
|
require.ErrorIs(err, tt.verifyErr)
|
|
})
|
|
}
|
|
}
|