Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
02169cc
style: use idiomatic zero-value and any declarations
piotr-roslaniec Jul 22, 2026
25e8d6a
test(ethereum): cover timestamp-based block search
piotr-roslaniec Jul 22, 2026
3e07e8e
refactor(libp2p): name the repeated metrics-recorder interface
piotr-roslaniec Jul 22, 2026
e87ff60
refactor(tbtcpg): extract shared capped-fee estimation
piotr-roslaniec Jul 22, 2026
4426a0c
refactor(spv): dedupe unproven-transaction search and drop dead metri…
piotr-roslaniec Jul 22, 2026
e6b688f
fix(beacon): abort protocol setup when channel filter cannot be set
piotr-roslaniec Jul 22, 2026
1e30c3b
refactor(gjkr): drop test-only receivedQualifiedSharesT field
piotr-roslaniec Jul 22, 2026
f84f483
test(tbtc): synchronize follower routine instead of sleeping
piotr-roslaniec Jul 22, 2026
978157b
fix(libp2p): keep SetMetricsRecorder param anonymous for cmd wiring
piotr-roslaniec Jul 22, 2026
f4050e3
test(spv): cover shared unproven-transaction search helper
piotr-roslaniec Jul 22, 2026
1c91e18
refactor(beacon): extract shared broadcast-channel filter helper
piotr-roslaniec Jul 22, 2026
e363e77
test(beacon): cover broadcast-channel filter abort path
piotr-roslaniec Jul 22, 2026
eb9e67a
fix(spv): clamp unproven-search start block to avoid uint64 underflow
piotr-roslaniec Jul 23, 2026
7d669ca
style(spv): drop stray blank line left by metrics-singleton removal
piotr-roslaniec Jul 23, 2026
82ed79a
test(ethereum): cover timestamp forward-compensation branch
piotr-roslaniec Jul 23, 2026
25fd084
refactor(ethereum): use errors.New for out-of-range test sentinel
piotr-roslaniec Jul 23, 2026
5465e7d
docs(gjkr): drop stale duplicated doc comment on shares-group helper
piotr-roslaniec Jul 23, 2026
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
8 changes: 3 additions & 5 deletions pkg/beacon/gjkr/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,9 @@ type CommitmentsVerifyingMember struct {
// Shares calculated for the current member by peer group members which passed
// the validation.
//
// receivedQualifiedSharesS are defined as `s_ji` and receivedQualifiedSharesT are
// defined as `t_ji` across the protocol specification.
// TODO remove receivedQualifiedSharesT - exists only for unit tests purpose
receivedQualifiedSharesS, receivedQualifiedSharesT map[group.MemberIndex]*big.Int
// receivedQualifiedSharesS are defined as `s_ji` across the protocol
// specification.
receivedQualifiedSharesS map[group.MemberIndex]*big.Int
// Commitments to secret shares polynomial coefficients received from
// other group members.
receivedPeerCommitments map[group.MemberIndex][]*bn256.G1
Expand Down Expand Up @@ -285,7 +284,6 @@ func (cm *CommittingMember) InitializeCommitmentsVerification() *CommitmentsVeri
return &CommitmentsVerifyingMember{
CommittingMember: cm,
receivedQualifiedSharesS: make(map[group.MemberIndex]*big.Int),
receivedQualifiedSharesT: make(map[group.MemberIndex]*big.Int),
receivedPeerCommitments: make(map[group.MemberIndex][]*bn256.G1),
}
}
Expand Down
2 changes: 0 additions & 2 deletions pkg/beacon/gjkr/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ func (cvm *CommitmentsVerifyingMember) VerifyReceivedSharesAndCommitmentsMessage
break
}
cvm.receivedQualifiedSharesS[commitmentsMessage.senderID] = shareS
cvm.receivedQualifiedSharesT[commitmentsMessage.senderID] = shareT
break
}
}
Expand Down Expand Up @@ -766,7 +765,6 @@ func (sjm *SharesJustifyingMember) discardReceivedShares(
memberID group.MemberIndex,
) {
delete(sjm.receivedQualifiedSharesS, memberID)
delete(sjm.receivedQualifiedSharesT, memberID)
}

// Inspects evidence log looking for ephemeral public key message sent in phase
Expand Down
34 changes: 21 additions & 13 deletions pkg/beacon/gjkr/protocol_accusations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func TestResolveSecretSharesAccusations(t *testing.T) {
}
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
members, err := initializeSharesJustifyingMemberGroup(
members, receivedSharesT, err := initializeSharesJustifyingMemberGroup(
dishonestThreshold,
groupSize,
)
Expand All @@ -145,7 +145,7 @@ func TestResolveSecretSharesAccusations(t *testing.T) {

accuser := findSharesJustifyingMemberByID(members, test.accuserID)
modifiedShareS := accuser.receivedQualifiedSharesS[test.accusedID]
modifiedShareT := accuser.receivedQualifiedSharesT[test.accusedID]
modifiedShareT := receivedSharesT[test.accuserID][test.accusedID]

if test.modifyShareS != nil {
modifiedShareS = test.modifyShareS(modifiedShareS)
Expand Down Expand Up @@ -422,7 +422,7 @@ func TestResolveSecretSharesAccusationsIncorrectAccussedMemberId(t *testing.T) {

for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
members, err := initializeSharesJustifyingMemberGroup(
members, _, err := initializeSharesJustifyingMemberGroup(
dishonestThreshold,
groupSize,
)
Expand Down Expand Up @@ -540,19 +540,20 @@ func findCoefficientsJustifyingMemberByID(
return nil
}

// InitializeSharesJustifyingMemberGroup generates a group of members and simulates
// shares calculation and commitments sharing betwen members (Phases 3 and 4).
// It generates coefficients for each group member, calculates commitments and
// shares for each peer member individually. At the end it stores values for each
// member just like they would be received from peers.
// initializeSharesJustifyingMemberGroup initializes a group of shares
// justifying members with simulated received shares and commitments. It also
// returns the received `t_ji` shares keyed by receiver then sender member
// index; these are not stored on the member (only `s_ji` is production state)
// but some accusation tests need them to reconstruct the peer shares message.
func initializeSharesJustifyingMemberGroup(dishonestThreshold, groupSize int) (
[]*SharesJustifyingMember,
map[group.MemberIndex]map[group.MemberIndex]*big.Int,
error,
) {
commitmentsVerifyingMembers, err :=
initializeCommitmentsVerifiyingMembersGroup(dishonestThreshold, groupSize)
if err != nil {
return nil, fmt.Errorf("group initialization failed [%s]", err)
return nil, nil, fmt.Errorf("group initialization failed [%s]", err)
}

var sharesJustifyingMembers []*SharesJustifyingMember
Expand All @@ -567,14 +568,18 @@ func initializeSharesJustifyingMemberGroup(dishonestThreshold, groupSize int) (
groupCoefficientsB := make(map[group.MemberIndex][]*big.Int, groupSize)
groupCommitments := make(map[group.MemberIndex][]*bn256.G1, groupSize)

// receivedSharesT keeps the `t_ji` shares received by each member from its
// peers, keyed by receiver then sender member index.
receivedSharesT := make(map[group.MemberIndex]map[group.MemberIndex]*big.Int)

for _, m := range sharesJustifyingMembers {
memberCoefficientsA, err := generatePolynomial(dishonestThreshold)
if err != nil {
return nil, fmt.Errorf("polynomial generation failed [%s]", err)
return nil, nil, fmt.Errorf("polynomial generation failed [%s]", err)
}
memberCoefficientsB, err := generatePolynomial(dishonestThreshold)
if err != nil {
return nil, fmt.Errorf("polynomial generation failed [%s]", err)
return nil, nil, fmt.Errorf("polynomial generation failed [%s]", err)
}

// polynomial is of degree dishonestThreshold so it has
Expand All @@ -598,13 +603,16 @@ func initializeSharesJustifyingMemberGroup(dishonestThreshold, groupSize int) (
for _, p := range sharesJustifyingMembers {
if m.ID != p.ID {
p.receivedQualifiedSharesS[m.ID] = m.evaluateMemberShare(p.ID, groupCoefficientsA[m.ID])
p.receivedQualifiedSharesT[m.ID] = m.evaluateMemberShare(p.ID, groupCoefficientsB[m.ID])
if receivedSharesT[p.ID] == nil {
receivedSharesT[p.ID] = make(map[group.MemberIndex]*big.Int)
}
receivedSharesT[p.ID][m.ID] = m.evaluateMemberShare(p.ID, groupCoefficientsB[m.ID])
p.receivedPeerCommitments[m.ID] = groupCommitments[m.ID]
}
}
}

return sharesJustifyingMembers, nil
return sharesJustifyingMembers, receivedSharesT, nil
}

// initializePointsJustifyingMemberGroup generates a group of members and
Expand Down
6 changes: 0 additions & 6 deletions pkg/beacon/gjkr/protocol_commitments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,6 @@ func assertValidSharesAndCommitments(
len(verifyingMember.receivedQualifiedSharesS),
)
}
if len(verifyingMember.receivedQualifiedSharesT) != expectedReceivedSharesLength {
t.Errorf("\nexpected: %v received shares T\nactual: %v\n",
expectedReceivedSharesLength,
len(verifyingMember.receivedQualifiedSharesT),
)
}
if len(verifyingMember.receivedPeerCommitments) != groupSize-1 {
t.Errorf("\nexpected: %v received commitments\nactual: %v\n",
expectedReceivedSharesLength,
Expand Down
2 changes: 1 addition & 1 deletion pkg/beacon/gjkr/protocol_sharing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func initializeQualifiedMembersGroup(dishonestThreshold, groupSize int) (
[]*QualifiedMember,
error,
) {
sharesJustifyingMembers, err := initializeSharesJustifyingMemberGroup(
sharesJustifyingMembers, _, err := initializeSharesJustifyingMemberGroup(
dishonestThreshold,
groupSize,
)
Expand Down
6 changes: 0 additions & 6 deletions pkg/beacon/gjkr/protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,6 @@ func TestRoundTrip(t *testing.T) {
len(member.receivedQualifiedSharesS),
)
}
if len(member.receivedQualifiedSharesT) != groupSize-1 {
t.Fatalf("\nexpected: %d received shares T\nactual: %d\n",
groupSize-1,
len(member.receivedQualifiedSharesT),
)
}
member.CombineMemberShares()
}

Expand Down
48 changes: 34 additions & 14 deletions pkg/beacon/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ func (n *node) IsInGroup(groupPublicKey []byte) bool {
return len(n.groupRegistry.GetGroup(groupPublicKey)) > 0
}

// setBroadcastChannelFilter installs the given membership filter on the
// broadcast channel so that only messages from operators in the group are
// accepted. It returns an error if the filter cannot be set; callers must abort
// on that error instead of proceeding on an unfiltered channel, which would
// accept messages from operators outside the group.
func setBroadcastChannelFilter(
channelLogger *zap.SugaredLogger,
channel net.BroadcastChannel,
filter net.BroadcastChannelFilter,
) error {
if err := channel.SetFilter(filter); err != nil {
channelLogger.Errorf(
"could not set filter for channel [%v]: [%v]",
channel.Name(),
err,
)
return err
}

return nil
}

// JoinDKGIfEligible takes a seed value and undergoes the process of the
// distributed key generation if this node's operator proves to be eligible for
// the group generated by that seed. This is an interactive on-chain process,
Expand Down Expand Up @@ -133,13 +155,12 @@ func (n *node) JoinDKGIfEligible(
signing,
)

err = broadcastChannel.SetFilter(membershipValidator.IsInGroup)
if err != nil {
dkgLogger.Errorf(
"could not set filter for channel [%v]: [%v]",
broadcastChannel.Name(),
err,
)
if err = setBroadcastChannelFilter(
dkgLogger,
broadcastChannel,
membershipValidator.IsInGroup,
); err != nil {
return
}

for _, index := range indexes {
Expand Down Expand Up @@ -358,13 +379,12 @@ func (n *node) GenerateRelayEntry(
n.beaconChain.Signing(),
)

err = channel.SetFilter(membershipValidator.IsInGroup)
if err != nil {
relayLogger.Errorf(
"could not set filter for channel [%v]: [%v]",
channel.Name(),
err,
)
if err = setBroadcastChannelFilter(
relayLogger,
channel,
membershipValidator.IsInGroup,
); err != nil {
return
}

blockCounter, err := n.beaconChain.BlockCounter()
Expand Down
61 changes: 61 additions & 0 deletions pkg/beacon/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,72 @@ import (
"math/big"
"testing"

"go.uber.org/zap"

"github.com/keep-network/keep-core/pkg/chain/local_v1"
"github.com/keep-network/keep-core/pkg/net"
"github.com/keep-network/keep-core/pkg/operator"
)

var relayEntryTimeout = uint64(15)

// filterErrorChannel is a broadcast channel whose SetFilter result is
// controllable, used to exercise the membership-filter abort path.
type filterErrorChannel struct {
net.BroadcastChannel
setFilterErr error
}

func (c *filterErrorChannel) SetFilter(net.BroadcastChannelFilter) error {
return c.setFilterErr
}

func (c *filterErrorChannel) Name() string {
return "test-channel"
}

// TestSetBroadcastChannelFilter verifies that the membership filter is required
// before a node proceeds on a group channel: when the filter cannot be set the
// helper surfaces the error so the caller aborts, rather than proceeding on an
// unfiltered channel that would accept messages from operators outside the
// group.
func TestSetBroadcastChannelFilter(t *testing.T) {
filter := func(*operator.PublicKey) bool { return true }

tests := map[string]struct {
setFilterErr error
expectError bool
}{
"filter set successfully": {
setFilterErr: nil,
expectError: false,
},
"filter cannot be set": {
setFilterErr: fmt.Errorf("cannot set filter"),
expectError: true,
},
}

for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
channel := &filterErrorChannel{setFilterErr: test.setFilterErr}

err := setBroadcastChannelFilter(
zap.NewNop().Sugar(),
channel,
filter,
)

if test.expectError && err == nil {
t.Fatal("expected an error, got nil")
}
if !test.expectError && err != nil {
t.Fatalf("unexpected error: [%v]", err)
}
})
}
}

func TestMonitorRelayEntryOnChain_EntrySubmitted(t *testing.T) {
localChain := local_v1.Connect(5, 3)

Expand Down
2 changes: 1 addition & 1 deletion pkg/bitcoin/electrum/electrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,7 @@ func connectWithRetry(
return result, err
}

func requestWithRetry[K interface{}](
func requestWithRetry[K any](
c *Connection,
requestFn func(ctx context.Context, client *electrum.Client) (K, error),
requestName string,
Expand Down
2 changes: 1 addition & 1 deletion pkg/chain/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (a Addresses) String() string {
}

var sb strings.Builder
var i = 0
var i int

sb.WriteString("[")
for i = 0; i < len(a)-1; i++ {
Expand Down
Loading
Loading