Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions pkg/beacon/entry/entry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package entry

import (
"math/big"
"testing"

bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare"

"github.com/keep-network/keep-core/pkg/bls"
"github.com/keep-network/keep-core/pkg/protocol/group"
)

// extractAndValidateShare is the gatekeeper that decides whether a signature
// share received from another group member is trustworthy. A member that
// accepted a bad share would contribute to a corrupt relay entry, so each
// rejection path matters as much as the accept path.
func TestExtractAndValidateShare(t *testing.T) {
senderID := group.MemberIndex(2)
secretKey := big.NewInt(1234567)

// previousEntry is the G1 point being signed; the share is the point
// signed with the sender's secret key and the public key share is the
// matching G2 public key.
previousEntry := new(bn256.G1).ScalarBaseMult(big.NewInt(7))
validShare := bls.SignG1(secretKey, previousEntry)
publicKeyShare := new(bn256.G2).ScalarBaseMult(secretKey)

t.Run("valid share", func(t *testing.T) {
message := NewSignatureShareMessage(senderID, validShare.Marshal(), "s")
shares := map[group.MemberIndex]*bn256.G2{senderID: publicKeyShare}

share, err := extractAndValidateShare(message, shares, previousEntry)
if err != nil {
t.Fatalf("unexpected error: [%v]", err)
}
if share == nil {
t.Fatal("expected a non-nil share")
}
// The returned share must be the one carried by the message.
if !bls.VerifyG1(publicKeyShare, previousEntry, share) {
t.Error("returned share does not verify against the public key share")
}
})

t.Run("unparseable share bytes", func(t *testing.T) {
message := NewSignatureShareMessage(senderID, []byte{0x01, 0x02}, "s")
shares := map[group.MemberIndex]*bn256.G2{senderID: publicKeyShare}

if _, err := extractAndValidateShare(message, shares, previousEntry); err == nil {
t.Error("expected an error for share bytes that are not a valid G1 point")
}
})

t.Run("no public key share for sender", func(t *testing.T) {
message := NewSignatureShareMessage(senderID, validShare.Marshal(), "s")
shares := map[group.MemberIndex]*bn256.G2{} // sender absent

if _, err := extractAndValidateShare(message, shares, previousEntry); err == nil {
t.Error("expected an error when the sender has no known public key share")
}
})

t.Run("share does not verify", func(t *testing.T) {
// A share signed with a different secret key than the public key share
// advertises must be rejected.
wrongShare := bls.SignG1(big.NewInt(7654321), previousEntry)
message := NewSignatureShareMessage(senderID, wrongShare.Marshal(), "s")
shares := map[group.MemberIndex]*bn256.G2{senderID: publicKeyShare}

if _, err := extractAndValidateShare(message, shares, previousEntry); err == nil {
t.Error("expected an error for a share that does not verify")
}
})
}
67 changes: 67 additions & 0 deletions pkg/beacon/entry/message_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package entry

import (
"testing"

"google.golang.org/protobuf/proto"

"github.com/keep-network/keep-core/internal/testutils"
"github.com/keep-network/keep-core/pkg/beacon/entry/gen/pb"
"github.com/keep-network/keep-core/pkg/protocol/group"
)

func TestNewSignatureShareMessage(t *testing.T) {
senderID := group.MemberIndex(7)
shareBytes := []byte{0x01, 0x02, 0x03}
sessionID := "session-1"

message := NewSignatureShareMessage(senderID, shareBytes, sessionID)

if message.SenderID() != senderID {
t.Errorf(
"unexpected sender ID\nexpected: [%v]\nactual: [%v]",
senderID,
message.SenderID(),
)
}
testutils.AssertBytesEqual(t, shareBytes, message.shareBytes)
if message.sessionID != sessionID {
t.Errorf(
"unexpected session ID\nexpected: [%v]\nactual: [%v]",
sessionID,
message.sessionID,
)
}
}

// The Type string is the wire identifier used to route this message on the
// broadcast channel. Changing it silently would break network compatibility
// with other clients, so it must stay stable.
func TestSignatureShareMessageType(t *testing.T) {
message := &SignatureShareMessage{}

if got := message.Type(); got != "relay/signature/share" {
t.Errorf(
"unexpected message type\nexpected: [relay/signature/share]\nactual: [%v]",
got,
)
}
}

// MemberIndex is a uint8 in the protocol but uint32 on the wire; Unmarshal must
// reject values that would overflow the uint8 rather than silently truncating
// them into a valid-looking but wrong member index.
func TestUnmarshalRejectsOverflowingMemberIndex(t *testing.T) {
overflowing, err := proto.Marshal(&pb.SignatureShare{
SenderID: 256, // maxMemberIndex is 255
Share: []byte{0x01},
SessionID: "session-1",
})
if err != nil {
t.Fatalf("failed to marshal test fixture: [%v]", err)
}

if err := (&SignatureShareMessage{}).Unmarshal(overflowing); err == nil {
t.Fatal("expected an error for a member index exceeding the uint8 range")
}
}
Loading