Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3844e59
refactor(tbtc): introduce named types and dedupe repeated constructs
piotr-roslaniec Jul 22, 2026
9d0cf20
style: use idiomatic zero-value and any declarations
piotr-roslaniec Jul 22, 2026
a1a9a58
test(ethereum): cover timestamp-based block search
piotr-roslaniec Jul 22, 2026
744a628
refactor(libp2p): name the repeated metrics-recorder interface
piotr-roslaniec Jul 22, 2026
2a2d8df
refactor(tbtcpg): extract shared capped-fee estimation
piotr-roslaniec Jul 22, 2026
821e6e5
refactor(spv): dedupe unproven-transaction search and drop dead metri…
piotr-roslaniec Jul 22, 2026
030c2db
fix(beacon): abort protocol setup when channel filter cannot be set
piotr-roslaniec Jul 22, 2026
4f77741
refactor(gjkr): drop test-only receivedQualifiedSharesT field
piotr-roslaniec Jul 22, 2026
bac929b
test(tbtc): synchronize follower routine instead of sleeping
piotr-roslaniec Jul 22, 2026
3df336d
fix(libp2p): keep SetMetricsRecorder param anonymous for cmd wiring
piotr-roslaniec Jul 22, 2026
087c331
test(spv): cover shared unproven-transaction search helper
piotr-roslaniec Jul 22, 2026
a9b62cd
refactor(beacon): extract shared broadcast-channel filter helper
piotr-roslaniec Jul 22, 2026
126fef5
test(beacon): cover broadcast-channel filter abort path
piotr-roslaniec Jul 22, 2026
de74fc1
fix(spv): clamp unproven-search start block to avoid uint64 underflow
piotr-roslaniec Jul 23, 2026
0ae7736
style(spv): drop stray blank line left by metrics-singleton removal
piotr-roslaniec Jul 23, 2026
c8288c7
test(ethereum): cover timestamp forward-compensation branch
piotr-roslaniec Jul 23, 2026
f9c8b40
refactor(ethereum): use errors.New for out-of-range test sentinel
piotr-roslaniec Jul 23, 2026
8e628e3
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