Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ jobs:
docker run \
--workdir /go/src/github.com/keep-network/keep-core \
go-build-env \
gotestsum -- -timeout 15m
gotestsum -- -timeout 15m ./...

- name: Build Docker Runtime Image
if: github.event_name != 'workflow_dispatch'
Expand Down
4 changes: 3 additions & 1 deletion cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,13 @@ func initBitcoinElectrumFlags(cmd *cobra.Command, cfg *config.Config) {

// Initialize flags for Network configuration.
func initNetworkFlags(cmd *cobra.Command, cfg *config.Config) {
// TODO: Remove in v3.0.0 along with isBootstrap() in start.go and
// the LibP2P.Bootstrap config field.
cmd.Flags().BoolVar(
&cfg.LibP2P.Bootstrap,
"network.bootstrap",
false,
"[DEPRECATED] Run the client in bootstrap mode. This flag is deprecated and will be removed in a future release.",
"[DEPRECATED: remove in v3.0] Run the client in bootstrap mode. This flag is deprecated and will be removed in v3.0.",
)

cmd.Flags().StringSliceVar(
Expand Down
2 changes: 1 addition & 1 deletion cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func initializeNetwork(
) (net.Provider, error) {
firewall := firewall.AnyApplicationPolicy(
applications,
firewall.EmptyAllowList,
firewall.EmptyAllowList(),
)

netProvider, err := libp2p.Connect(
Expand Down
3 changes: 3 additions & 0 deletions config/_peers/mainnet
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
# TODO: Add at least one additional mainnet peer across a different
# operator/ASN before production rollout. A single peer is a SPOF for
# initial peer discovery of fresh nodes.
/ip4/143.198.18.229/tcp/3919/ipfs/16Uiu2HAmDP4Z6LCogRMictJ6deGs4DRo99A5JTz5u3CLMg7URxC6
4 changes: 2 additions & 2 deletions config/peers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func TestResolvePeers(t *testing.T) {
"sepolia network": {
network: network.Testnet,
expectedPeers: []string{
"/dns4/PLACEHOLDER-operator-1.test.example.com/tcp/3920/ipfs/16Uiu2HAmDrk2Bh4VNPUJfKRHTE2CvH9xfKzN4KFnmRJbGLkJFDqL",
"/dns4/PLACEHOLDER-operator-2.test.example.com/tcp/3920/ipfs/16Uiu2HAm3ex8rGzwFpWYbRreRUiX9JEYCKxp7KDMzB8RZ6fQWnMa",
"/dns4/keep-operator-1.test.keep-nodes.io/tcp/3920/ipfs/16Uiu2HAmDrk2Bh4VNPUJfKRHTE2CvH9xfKzN4KFnmRJbGLkJFDqL",
"/dns4/keep-operator-2.test.keep-nodes.io/tcp/3920/ipfs/16Uiu2HAm3ex8rGzwFpWYbRreRUiX9JEYCKxp7KDMzB8RZ6fQWnMa",
},
},
"developer network": {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.24

toolchain go1.24.1


replace (
github.com/bnb-chain/tss-lib => github.com/threshold-network/tss-lib v0.0.0-20230901144531-2e712689cfbe
// btcd in version v.0.23 extracted `btcd/btcec` to a separate package `btcd/btcec/v2`.
Expand Down
4 changes: 4 additions & 0 deletions pkg/clientinfo/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import (
type Source func() float64

// Names under which metrics are exposed.
//
// NOTE: ConnectedWellknownPeersCountMetricName was renamed from
// "connected_bootstrap_count" in v2.6.0. Update any Prometheus queries or
// Grafana dashboards that reference the old name.
const (
ConnectedPeersCountMetricName = "connected_peers_count"
ConnectedWellknownPeersCountMetricName = "connected_wellknown_peers_count"
Expand Down
68 changes: 0 additions & 68 deletions pkg/clientinfo/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,74 +10,6 @@ import (
"github.com/keep-network/keep-core/pkg/operator"
)

// TestConnectedWellknownPeersCountMetricName verifies that the metric constant
// for well-known peers connectivity has the correct string value used by
// Prometheus for metric registration.
func TestConnectedWellknownPeersCountMetricName(t *testing.T) {
expected := "connected_wellknown_peers_count"
actual := ConnectedWellknownPeersCountMetricName

if actual != expected {
t.Errorf(
"expected metric name %q, got %q",
expected,
actual,
)
}
}

// TestMetricConstants verifies that all metric name constants are defined with
// the expected non-empty string values. This ensures no accidental changes to
// metric names that would break Prometheus queries and Grafana dashboards.
func TestMetricConstants(t *testing.T) {
tests := []struct {
name string
constant string
expected string
}{
{
name: "connected peers count",
constant: ConnectedPeersCountMetricName,
expected: "connected_peers_count",
},
{
name: "connected wellknown peers count",
constant: ConnectedWellknownPeersCountMetricName,
expected: "connected_wellknown_peers_count",
},
{
name: "eth connectivity",
constant: EthConnectivityMetricName,
expected: "eth_connectivity",
},
{
name: "btc connectivity",
constant: BtcConnectivityMetricName,
expected: "btc_connectivity",
},
{
name: "client info",
constant: ClientInfoMetricName,
expected: "client_info",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if tc.constant != tc.expected {
t.Errorf(
"expected metric name %q, got %q",
tc.expected,
tc.constant,
)
}
if tc.constant == "" {
t.Error("metric name constant must not be empty")
}
})
}
}

// mockTransportIdentifier implements net.TransportIdentifier for testing.
type mockTransportIdentifier struct{}

Expand Down
12 changes: 10 additions & 2 deletions pkg/firewall/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,16 @@ func (al *AllowList) Contains(operatorPublicKey *operator.PublicKey) bool {
return al.allowedPublicKeys[operatorPublicKey.String()]
}

// EmptyAllowList represents an empty firewall allowlist.
var EmptyAllowList = NewAllowList([]*operator.PublicKey{})
// emptyAllowList is the singleton empty allowlist used in production.
// All peers must pass IsRecognized checks; no bypass is available.
var emptyAllowList = NewAllowList([]*operator.PublicKey{})

// EmptyAllowList returns the empty firewall allowlist. In production, this
// ensures all peers are subject to on-chain staking verification with no
// AllowList bypass.
func EmptyAllowList() *AllowList {
return emptyAllowList
}

const (
// PositiveIsRecognizedCachePeriod is the time period the cache maintains
Expand Down
28 changes: 14 additions & 14 deletions pkg/firewall/firewall_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const cachingPeriod = time.Second
func TestValidate_PeerNotRecognized_NoApplications(t *testing.T) {
policy := &anyApplicationPolicy{
applications: []Application{},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down Expand Up @@ -44,7 +44,7 @@ func TestValidate_PeerNotRecognized_MultipleApplications(t *testing.T) {
applications: []Application{
newMockApplication(),
newMockApplication()},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand All @@ -71,7 +71,7 @@ func TestValidate_PeerRecognized_FirstApplicationRecognizes(t *testing.T) {
applications: []Application{
application,
newMockApplication()},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down Expand Up @@ -100,7 +100,7 @@ func TestValidate_PeerRecognized_SecondApplicationRecognizes(t *testing.T) {
applications: []Application{
newMockApplication(),
application},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down Expand Up @@ -139,7 +139,7 @@ func TestValidate_PeerNotRecognized_FirstApplicationReturnedError(t *testing.T)
applications: []Application{
application1,
application2},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand All @@ -164,7 +164,7 @@ func TestValidate_PeerRecognized_Cached(t *testing.T) {

policy := &anyApplicationPolicy{
applications: []Application{application},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down Expand Up @@ -203,7 +203,7 @@ func TestValidate_PeerNotRecognized_CacheEmptied(t *testing.T) {

policy := &anyApplicationPolicy{
applications: []Application{application},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down Expand Up @@ -238,7 +238,7 @@ func TestValidate_PeerNotRecognized_Cached(t *testing.T) {
application := newMockApplication()
policy := &anyApplicationPolicy{
applications: []Application{application},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down Expand Up @@ -273,7 +273,7 @@ func TestValidate_PeerRecognized_CacheEmptied(t *testing.T) {

policy := &anyApplicationPolicy{
applications: []Application{application},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down Expand Up @@ -338,11 +338,11 @@ func TestValidate_EmptyAllowList_RecognizedPeerAccepted(t *testing.T) {
err: nil,
})

// With EmptyAllowList, a recognized peer must pass validation through
// With EmptyAllowList(), a recognized peer must pass validation through
// the IsRecognized path, not through an AllowList bypass.
policy := &anyApplicationPolicy{
applications: []Application{application},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand All @@ -361,11 +361,11 @@ func TestValidate_EmptyAllowList_UnrecognizedPeerRejected(t *testing.T) {
t.Fatal(err)
}

// With EmptyAllowList, a peer not recognized by any application must
// With EmptyAllowList(), a peer not recognized by any application must
// be rejected. No AllowList bypass is available.
policy := &anyApplicationPolicy{
applications: []Application{newMockApplication()},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand All @@ -388,7 +388,7 @@ func TestValidate_EmptyAllowList_PreviouslyAllowlistedPeerMustPassIsRecognized(t
// The peer is not recognized by the application and must be rejected.
policy := &anyApplicationPolicy{
applications: []Application{newMockApplication()},
allowList: EmptyAllowList,
allowList: EmptyAllowList(),
positiveResultCache: cache.NewTimeCache(cachingPeriod),
negativeResultCache: cache.NewTimeCache(cachingPeriod),
}
Expand Down
73 changes: 63 additions & 10 deletions pkg/tbtc/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,22 +344,27 @@ func TestNode_RunCoordinationLayer(t *testing.T) {
if signer.wallet.publicKey.Equal(walletPublicKey) {
result, ok := map[uint64]*coordinationResult{
900: {
window: window,
proposal: &mockCoordinationProposal{ActionDepositSweep},
},
// Omit window at block 1800 to make sure the layer doesn't
// crash if no result is produced.
2700: {
window: window,
proposal: &mockCoordinationProposal{ActionRedemption},
},
// Put some trash value to make sure coordination windows
// are distributed correctly.
2705: {
window: window,
proposal: &mockCoordinationProposal{ActionMovingFunds},
},
3600: {
window: window,
proposal: &mockCoordinationProposal{ActionNoop},
},
4500: {
window: window,
proposal: &mockCoordinationProposal{ActionMovedFundsSweep},
},
}[window.coordinationBlock]
Expand Down Expand Up @@ -405,6 +410,10 @@ loop:
for {
select {
case result := <-processedResultsChan:
if result == nil {
continue
}

processedResults = append(processedResults, result)

// Once the second-last coordination window is processed, stop the
Expand All @@ -425,24 +434,68 @@ loop:
3,
len(processedResults),
)
testutils.AssertStringsEqual(

resultActionsByWindow := make(map[uint64]WalletActionType, len(processedResults))
for _, result := range processedResults {
resultActionsByWindow[result.window.coordinationBlock] =
result.proposal.ActionType()
}

testutils.AssertIntsEqual(
t,
"first result",
ActionDepositSweep.String(),
processedResults[0].proposal.ActionType().String(),
"processed coordination windows count",
3,
len(resultActionsByWindow),
)

firstAction, ok := resultActionsByWindow[900]
if !ok {
t.Fatal("expected coordination result for window at block 900")
}
testutils.AssertStringsEqual(
t,
"second result",
ActionRedemption.String(),
processedResults[1].proposal.ActionType().String(),
"result for block 900",
ActionDepositSweep.String(),
firstAction.String(),
)

secondAction, ok := resultActionsByWindow[2700]
if !ok {
t.Fatal("expected coordination result for window at block 2700")
}
testutils.AssertStringsEqual(
t,
"third result",
ActionNoop.String(),
processedResults[2].proposal.ActionType().String(),
"result for block 2700",
ActionRedemption.String(),
secondAction.String(),
)

if _, ok := resultActionsByWindow[2705]; ok {
t.Fatal("unexpected coordination result for non-window block 2705")
}

// Result processing is asynchronous, so by the time the test cancels the
// coordination layer after the third processed result, either the 3600
// window or the subsequent 4500 window may already be in flight.
if thirdAction, ok := resultActionsByWindow[3600]; ok {
testutils.AssertStringsEqual(
t,
"result for block 3600",
ActionNoop.String(),
thirdAction.String(),
)
} else {
fourthAction, ok := resultActionsByWindow[4500]
if !ok {
t.Fatal("expected coordination result for block 3600 or 4500")
}
testutils.AssertStringsEqual(
t,
"result for block 4500",
ActionMovedFundsSweep.String(),
fourthAction.String(),
)
}
}

type mockCoordinationProposal struct {
Expand Down
Loading
Loading