Skip to content

Commit 9b81758

Browse files
authored
Integrate moderation into the currency service (#172)
1 parent e401a9e commit 9b81758

7 files changed

Lines changed: 96 additions & 5 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
filippo.io/edwards25519 v1.1.0
77
github.com/aws/aws-sdk-go-v2 v0.17.0
88
github.com/code-payments/code-vm-indexer v1.2.0
9-
github.com/code-payments/ocp-protobuf-api v1.7.1-0.20260401115005-c2265cbd04b0
9+
github.com/code-payments/ocp-protobuf-api v1.8.1
1010
github.com/emirpasic/gods v1.12.0
1111
github.com/envoyproxy/protoc-gen-validate v1.2.1
1212
github.com/golang/protobuf v1.5.4

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I
7878
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
7979
github.com/code-payments/code-vm-indexer v1.2.0 h1:rSHpBMiT9BKgmKcXg/VIoi/h0t7jNxGx07Qz59m+6Q0=
8080
github.com/code-payments/code-vm-indexer v1.2.0/go.mod h1:vn91YN2qNqb+gGJeZe2+l+TNxVmEEiRHXXnIn2Y40h8=
81-
github.com/code-payments/ocp-protobuf-api v1.7.1-0.20260401115005-c2265cbd04b0 h1:zQdbkGJKttqckZ8GH66fx0g6D6UB/WHT7z5MNiBHMIE=
82-
github.com/code-payments/ocp-protobuf-api v1.7.1-0.20260401115005-c2265cbd04b0/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8=
81+
github.com/code-payments/ocp-protobuf-api v1.8.1 h1:IaCVADbbTUtZwf0Rk8Pf8PygsancuOXc+A3CcTG/74w=
82+
github.com/code-payments/ocp-protobuf-api v1.8.1/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8=
8383
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw=
8484
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
8585
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=

ocp/integration/moderation.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package integration
2+
3+
import (
4+
"context"
5+
6+
"github.com/code-payments/ocp-server/ocp/common"
7+
)
8+
9+
// Moderation is an OCP integration enabling custom moderation rules
10+
type Moderation interface {
11+
// ValidateAttestation validates a moderation attestation for a piece of moderated content
12+
ValidateAttestation(ctx context.Context, owner *common.Account, rawAttestation []byte, content any) (bool, error)
13+
}
14+
15+
type allowEverythingModerationIntegration struct {
16+
}
17+
18+
// NewAllowEverythingModerationIntegration returns a default Moderation integration that allows everything
19+
func NewAllowEverythingModerationIntegration() Moderation {
20+
return &allowEverythingModerationIntegration{}
21+
}
22+
23+
func (i *allowEverythingModerationIntegration) ValidateAttestation(_ context.Context, _ *common.Account, _ []byte, _ any) (bool, error) {
24+
return true, nil
25+
}

ocp/rpc/currency/icon.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ func (s *currencyServer) UpdateIcon(ctx context.Context, req *currencypb.UpdateI
7272
return &currencypb.UpdateIconResponse{Result: currencypb.UpdateIconResponse_DENIED}, nil
7373
}
7474

75+
isModerated, err := s.moderation.ValidateAttestation(ctx, ownerAccount, req.ModerationAttestation.RawValue, req.Icon)
76+
if err != nil {
77+
log.With(zap.Error(err)).Warn("failed to validate moderation attestation")
78+
return nil, status.Error(codes.Internal, "")
79+
} else if !isModerated {
80+
return &currencypb.UpdateIconResponse{Result: currencypb.UpdateIconResponse_DENIED}, nil
81+
}
82+
7583
processed, ext, contentType, err := processIcon(req.Icon)
7684
if err == errInvalidIcon {
7785
return &currencypb.UpdateIconResponse{Result: currencypb.UpdateIconResponse_INVALID_ICON}, nil

ocp/rpc/currency/launch.go

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ func (s *currencyServer) Launch(ctx context.Context, req *currencypb.LaunchReque
6363
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
6464
}
6565

66+
isModerated, err := s.moderation.ValidateAttestation(ctx, ownerAccount, req.NameModerationAttestation.RawValue, req.Name)
67+
if err != nil {
68+
log.With(zap.Error(err)).Warn("failed to validate name moderation attestation")
69+
return nil, status.Error(codes.Internal, "")
70+
} else if !isModerated {
71+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
72+
}
73+
6674
symbol := strings.TrimSpace(req.Symbol)
6775
if len(symbol) == 0 {
6876
symbol = strings.ToUpper(strings.Map(
@@ -77,15 +85,53 @@ func (s *currencyServer) Launch(ctx context.Context, req *currencypb.LaunchReque
7785
if len(symbol) > currencycreator.MaxCurrencyConfigAccountSymbolLength {
7886
symbol = symbol[0:currencycreator.MaxCurrencyConfigAccountSymbolLength]
7987
}
80-
} else if symbol != req.Symbol {
81-
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
88+
} else {
89+
if symbol != req.Symbol {
90+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
91+
}
92+
if req.SymbolModerationAttestation == nil {
93+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
94+
}
95+
isModerated, err := s.moderation.ValidateAttestation(ctx, ownerAccount, req.SymbolModerationAttestation.RawValue, req.Symbol)
96+
if err != nil {
97+
log.With(zap.Error(err)).Warn("failed to validate symbol moderation attestation")
98+
return nil, status.Error(codes.Internal, "")
99+
} else if !isModerated {
100+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
101+
}
82102
}
83103

84104
description := strings.TrimSpace(req.Description)
105+
if description != req.Description {
106+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
107+
}
108+
if len(req.Description) > 0 {
109+
if req.DescriptionModerationAttestation == nil {
110+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
111+
}
112+
isModerated, err := s.moderation.ValidateAttestation(ctx, ownerAccount, req.DescriptionModerationAttestation.RawValue, req.Description)
113+
if err != nil {
114+
log.With(zap.Error(err)).Warn("failed to validate description moderation attestation")
115+
return nil, status.Error(codes.Internal, "")
116+
} else if !isModerated {
117+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
118+
}
119+
}
85120

86121
var processedIcon []byte
87122
var iconExt, iconContentType string
88123
if len(req.Icon) > 0 {
124+
if req.IconModerationAttestation == nil {
125+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
126+
}
127+
isModerated, err := s.moderation.ValidateAttestation(ctx, ownerAccount, req.IconModerationAttestation.RawValue, req.Icon)
128+
if err != nil {
129+
log.With(zap.Error(err)).Warn("failed to validate icon moderation attestation")
130+
return nil, status.Error(codes.Internal, "")
131+
} else if !isModerated {
132+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
133+
}
134+
89135
processedIcon, iconExt, iconContentType, err = processIcon(req.Icon)
90136
if err == errInvalidIcon {
91137
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_INVALID_ICON}, nil

ocp/rpc/currency/metadata.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ func (s *currencyServer) UpdateMetadata(ctx context.Context, req *currencypb.Upd
8585
}
8686

8787
if req.NewDescription != nil {
88+
isModerated, err := s.moderation.ValidateAttestation(ctx, ownerAccount, req.NewDescription.ModerationAttestation.RawValue, req.NewDescription.Value)
89+
if err != nil {
90+
log.With(zap.Error(err)).Warn("failed to validate description moderation attestation")
91+
return nil, status.Error(codes.Internal, "")
92+
} else if !isModerated {
93+
return &currencypb.UpdateMetadataResponse{Result: currencypb.UpdateMetadataResponse_DENIED}, nil
94+
}
95+
8896
metadataRecord.Description = req.NewDescription.Value
8997
}
9098

ocp/rpc/currency/server.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
auth_util "github.com/code-payments/ocp-server/ocp/auth"
1313
currency_util "github.com/code-payments/ocp-server/ocp/currency"
1414
ocp_data "github.com/code-payments/ocp-server/ocp/data"
15+
"github.com/code-payments/ocp-server/ocp/integration"
1516
)
1617

1718
type currencyServer struct {
@@ -24,6 +25,7 @@ type currencyServer struct {
2425
auth *auth_util.RPCSignatureVerifier
2526

2627
antispamGuard *antispam.Guard
28+
moderation integration.Moderation
2729

2830
s3Client *s3.Client
2931

@@ -40,6 +42,7 @@ func NewCurrencyServer(
4042
data ocp_data.Provider,
4143
mintDataProvider *currency_util.MintDataProvider,
4244
antispamGuard *antispam.Guard,
45+
moderation integration.Moderation,
4346
s3Client *s3.Client,
4447
configProvider ConfigProvider,
4548
) currencypb.CurrencyServer {
@@ -56,6 +59,7 @@ func NewCurrencyServer(
5659
auth: auth_util.NewRPCSignatureVerifier(log, data),
5760

5861
antispamGuard: antispamGuard,
62+
moderation: moderation,
5963

6064
s3Client: s3Client,
6165

0 commit comments

Comments
 (0)