Skip to content

Commit 7666a1b

Browse files
committed
Add LoggingMisbehaviorService
1 parent 93e176c commit 7666a1b

File tree

4 files changed

+158
-0
lines changed

4 files changed

+158
-0
lines changed

pkg/misbehavior/interface.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package misbehavior
2+
3+
type MisbehaviorService interface {
4+
SafetyFailure(report *SafetyFailureReport) error
5+
// TODO:nm add liveness failures
6+
}

pkg/misbehavior/misbehavior.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package misbehavior
2+
3+
import (
4+
"errors"
5+
6+
"go.uber.org/zap"
7+
)
8+
9+
// LoggingMisbehaviorService provides an implementation of the MisbehaviorService interface that
10+
// logs misbehavior reports without storing them or forwarding to the network.
11+
type LoggingMisbehaviorService struct {
12+
log *zap.Logger
13+
}
14+
15+
func NewLoggingMisbehaviorService(log *zap.Logger) *LoggingMisbehaviorService {
16+
return &LoggingMisbehaviorService{
17+
log: log.Named("misbehavior"),
18+
}
19+
}
20+
21+
func (m *LoggingMisbehaviorService) SafetyFailure(report *SafetyFailureReport) error {
22+
if report == nil {
23+
return errors.New("report is nil")
24+
}
25+
m.log.Warn(
26+
"misbehavior detected",
27+
zap.String("misbehavior_type", report.misbehaviorType.String()),
28+
zap.Uint32("misbehaving_node_id", report.misbehavingNodeId),
29+
zap.Bool("submitted_by_node", report.submittedByNode),
30+
zap.Any("envelopes", report.envelopes),
31+
)
32+
33+
return nil
34+
}

pkg/misbehavior/misbehavior_test.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package misbehavior
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
"github.com/xmtp/xmtpd/pkg/envelopes"
8+
proto "github.com/xmtp/xmtpd/pkg/proto/xmtpv4/message_api"
9+
testEnvelopes "github.com/xmtp/xmtpd/pkg/testutils/envelopes"
10+
"go.uber.org/zap"
11+
"go.uber.org/zap/zapcore"
12+
"go.uber.org/zap/zaptest/observer"
13+
)
14+
15+
func TestLoggingMisbehaviorService(t *testing.T) {
16+
env1, err := envelopes.NewOriginatorEnvelope(testEnvelopes.CreateOriginatorEnvelope(t, 1, 1))
17+
require.NoError(t, err)
18+
env2, err := envelopes.NewOriginatorEnvelope(testEnvelopes.CreateOriginatorEnvelope(t, 1, 2))
19+
require.NoError(t, err)
20+
21+
report, err := NewSafetyFailureReport(
22+
1,
23+
proto.Misbehavior_MISBEHAVIOR_DUPLICATE_SEQUENCE_ID,
24+
true,
25+
[]*envelopes.OriginatorEnvelope{env1, env2},
26+
)
27+
require.NoError(t, err)
28+
29+
core, observedLogs := observer.New(zapcore.DebugLevel)
30+
logger := zap.New(core)
31+
service := NewLoggingMisbehaviorService(logger)
32+
33+
err = service.SafetyFailure(report)
34+
require.NoError(t, err)
35+
36+
logs := observedLogs.All()
37+
require.Len(t, logs, 1)
38+
logEntry := logs[0]
39+
require.Equal(t, "misbehavior detected", logEntry.Message)
40+
require.Equal(t, "MISBEHAVIOR_DUPLICATE_SEQUENCE_ID", logEntry.ContextMap()["misbehavior_type"])
41+
require.Equal(t, uint32(1), logEntry.ContextMap()["misbehaving_node_id"])
42+
require.Equal(t, true, logEntry.ContextMap()["submitted_by_node"])
43+
require.Len(t, logEntry.ContextMap()["envelopes"], 2)
44+
}
45+
46+
func TestNewSafetyFailureReportValidations(t *testing.T) {
47+
// Test case: No envelopes provided
48+
_, err := NewSafetyFailureReport(
49+
1,
50+
proto.Misbehavior_MISBEHAVIOR_DUPLICATE_SEQUENCE_ID,
51+
true,
52+
nil,
53+
)
54+
require.Error(t, err)
55+
require.Equal(t, "no envelopes provided", err.Error())
56+
57+
// Test case: Misbehaving node ID is zero
58+
env, _ := envelopes.NewOriginatorEnvelope(testEnvelopes.CreateOriginatorEnvelope(t, 1, 1))
59+
_, err = NewSafetyFailureReport(
60+
0,
61+
proto.Misbehavior_MISBEHAVIOR_DUPLICATE_SEQUENCE_ID,
62+
true,
63+
[]*envelopes.OriginatorEnvelope{env},
64+
)
65+
require.Error(t, err)
66+
require.Equal(t, "misbehaving node id is required", err.Error())
67+
68+
// Test case: Misbehavior type is unspecified
69+
_, err = NewSafetyFailureReport(
70+
1,
71+
proto.Misbehavior_MISBEHAVIOR_UNSPECIFIED,
72+
true,
73+
[]*envelopes.OriginatorEnvelope{env},
74+
)
75+
require.Error(t, err)
76+
require.Equal(t, "misbehavior type is required", err.Error())
77+
}

pkg/misbehavior/report.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package misbehavior
2+
3+
import (
4+
"errors"
5+
6+
"github.com/xmtp/xmtpd/pkg/envelopes"
7+
proto "github.com/xmtp/xmtpd/pkg/proto/xmtpv4/message_api"
8+
)
9+
10+
type SafetyFailureReport struct {
11+
misbehavingNodeId uint32
12+
misbehaviorType proto.Misbehavior
13+
submittedByNode bool
14+
envelopes []*envelopes.OriginatorEnvelope
15+
}
16+
17+
func NewSafetyFailureReport(
18+
misbehavingNodeId uint32,
19+
misbehaviorType proto.Misbehavior,
20+
submittedByNode bool,
21+
envs []*envelopes.OriginatorEnvelope,
22+
) (*SafetyFailureReport, error) {
23+
if len(envs) == 0 {
24+
return nil, errors.New("no envelopes provided")
25+
}
26+
27+
if misbehavingNodeId == 0 {
28+
return nil, errors.New("misbehaving node id is required")
29+
}
30+
31+
if misbehaviorType == proto.Misbehavior_MISBEHAVIOR_UNSPECIFIED {
32+
return nil, errors.New("misbehavior type is required")
33+
}
34+
35+
return &SafetyFailureReport{
36+
misbehavingNodeId: misbehavingNodeId,
37+
misbehaviorType: misbehaviorType,
38+
submittedByNode: submittedByNode,
39+
envelopes: envs,
40+
}, nil
41+
}

0 commit comments

Comments
 (0)