Skip to content

Commit dce8845

Browse files
authored
Implement CheckAvailability RPC (#152)
1 parent d320239 commit dce8845

10 files changed

Lines changed: 161 additions & 6 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.5.0
9+
github.com/code-payments/ocp-protobuf-api v1.6.0
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.5.0 h1:xaGRPK9P4iZfTvwzLbSj7pIP44bPKQM7JDUulcXLiMw=
82-
github.com/code-payments/ocp-protobuf-api v1.5.0/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8=
81+
github.com/code-payments/ocp-protobuf-api v1.6.0 h1:oxXgz/kVj7Erbq39mjWR6ReixBFIeI0a3TIO9E5XNdc=
82+
github.com/code-payments/ocp-protobuf-api v1.6.0/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/data/currency/memory/store.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,18 @@ func (s *store) CountMints(ctx context.Context) (uint64, error) {
331331
return uint64(len(s.metadataRecords)), nil
332332
}
333333

334+
func (s *store) IsNameAvailable(_ context.Context, name string) (bool, error) {
335+
s.mu.Lock()
336+
defer s.mu.Unlock()
337+
338+
for _, item := range s.metadataRecords {
339+
if strings.EqualFold(item.Name, name) {
340+
return false, nil
341+
}
342+
}
343+
return true, nil
344+
}
345+
334346
func (s *store) PutHistoricalReserveRecord(ctx context.Context, data *currency.ReserveRecord) error {
335347
if err := data.Validate(); err != nil {
336348
return err

ocp/data/currency/postgres/model.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,18 @@ func dbCountMetadataByState(ctx context.Context, db *sqlx.DB, state currency.Met
549549
return res, nil
550550
}
551551

552+
func dbIsNameAvailable(ctx context.Context, db *sqlx.DB, name string) (bool, error) {
553+
var count uint64
554+
err := db.GetContext(ctx, &count,
555+
`SELECT COUNT(*) FROM `+metadataTableName+` WHERE LOWER(name) = LOWER($1)`,
556+
name,
557+
)
558+
if err != nil {
559+
return false, err
560+
}
561+
return count == 0, nil
562+
}
563+
552564
func dbGetReserveByMintAndTime(ctx context.Context, db *sqlx.DB, mint string, t time.Time, ordering q.Ordering) (*historicalReserveModel, error) {
553565
res := &historicalReserveModel{}
554566
err := db.GetContext(ctx, res,

ocp/data/currency/postgres/store.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ func (s *store) CountMetadataByState(ctx context.Context, state currency.Metadat
149149
return dbCountMetadataByState(ctx, s.db, state)
150150
}
151151

152+
func (s *store) IsNameAvailable(ctx context.Context, name string) (bool, error) {
153+
return dbIsNameAvailable(ctx, s.db, name)
154+
}
155+
152156
func (s *store) PutHistoricalReserveRecord(ctx context.Context, record *currency.ReserveRecord) error {
153157
model, err := toHistoricalReserveModel(record)
154158
if err != nil {

ocp/data/currency/store.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ type Store interface {
7272
// CountMints returns the total number of currency creator mints
7373
CountMints(ctx context.Context) (uint64, error)
7474

75+
// IsNameAvailable checks whether a currency name is available for use.
76+
// The check is case-insensitive.
77+
IsNameAvailable(ctx context.Context, name string) (bool, error)
78+
7579
// PutHistoricalReserveRecord puts a currency creator mint reserve records into the store.
7680
PutHistoricalReserveRecord(ctx context.Context, record *ReserveRecord) error
7781

ocp/data/currency/tests/tests.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func RunTests(t *testing.T, s currency.Store, teardown func()) {
2020
testMetadataRoundTrip,
2121
testMetadataSaveWithVersioning,
2222
testMetadataUniqueNameConstraint,
23+
testIsNameAvailable,
2324
testGetAllMetadataByState,
2425
testGetAllMints,
2526
testCountMints,
@@ -282,6 +283,72 @@ func testMetadataUniqueNameConstraint(t *testing.T, s currency.Store) {
282283
assert.Equal(t, currency.ErrDuplicateCurrency, s.SaveMetadata(context.Background(), record2))
283284
}
284285

286+
func testIsNameAvailable(t *testing.T, s currency.Store) {
287+
ctx := context.Background()
288+
289+
// Name should be available when no records exist
290+
available, err := s.IsNameAvailable(ctx, "TestCurrency")
291+
require.NoError(t, err)
292+
assert.True(t, available)
293+
294+
// Save a metadata record
295+
record := &currency.MetadataRecord{
296+
Name: "TestCurrency",
297+
Symbol: "TC",
298+
Description: "A test currency",
299+
ImageUrl: "https://example.com/tc.png",
300+
BillColors: []string{"#000000"},
301+
SocialLinks: []currency.SocialLink{{Type: currency.SocialLinkTypeWebsite, Value: "https://example.com"}},
302+
303+
Seed: "nameseed1",
304+
Authority: "nameauth1",
305+
306+
Mint: "namemint11111111111111111111111111111111111111",
307+
MintBump: 255,
308+
Decimals: currencycreator.DefaultMintDecimals,
309+
310+
CurrencyConfig: "nameconfig111111111111111111111111111111111",
311+
CurrencyConfigBump: 255,
312+
313+
LiquidityPool: "namepool11111111111111111111111111111111111",
314+
LiquidityPoolBump: 255,
315+
316+
VaultMint: "namevmint1111111111111111111111111111111111",
317+
VaultMintBump: 255,
318+
319+
VaultCore: "namevcore1111111111111111111111111111111111",
320+
VaultCoreBump: 255,
321+
322+
SellFeeBps: currencycreator.DefaultSellFeeBps,
323+
324+
Alt: "namealt111111111111111111111111111111111111111",
325+
326+
CreatedBy: "namecreator1",
327+
CreatedAt: time.Now(),
328+
}
329+
330+
require.NoError(t, s.SaveMetadata(ctx, record))
331+
332+
// Exact name should not be available
333+
available, err = s.IsNameAvailable(ctx, "TestCurrency")
334+
require.NoError(t, err)
335+
assert.False(t, available)
336+
337+
// Case-insensitive match should not be available
338+
available, err = s.IsNameAvailable(ctx, "testcurrency")
339+
require.NoError(t, err)
340+
assert.False(t, available)
341+
342+
available, err = s.IsNameAvailable(ctx, "TESTCURRENCY")
343+
require.NoError(t, err)
344+
assert.False(t, available)
345+
346+
// Different name should be available
347+
available, err = s.IsNameAvailable(ctx, "OtherCurrency")
348+
require.NoError(t, err)
349+
assert.True(t, available)
350+
}
351+
285352
func testGetAllMetadataByState(t *testing.T, s currency.Store) {
286353
t.Run("testGetAllMetadataByState", func(t *testing.T) {
287354
ctx := context.Background()

ocp/data/internal.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ type DatabaseData interface {
142142
GetCurrencyMetadataCountByState(ctx context.Context, state currency.MetadataState) (uint64, error)
143143
GetAllCurrencyMints(ctx context.Context) ([]string, error)
144144
CountCurrencyMints(ctx context.Context) (uint64, error)
145+
IsCurrencyNameAvailable(ctx context.Context, name string) (bool, error)
145146
PutHistoricalCurrencyReserve(ctx context.Context, record *currency.ReserveRecord) error
146147
GetCurrencyReserveAtTime(ctx context.Context, mint string, t time.Time) (*currency.ReserveRecord, error)
147148
GetCurrencyReserveHistory(ctx context.Context, mint string, opts ...query.Option) ([]*currency.ReserveRecord, error)
@@ -558,6 +559,9 @@ func (dp *DatabaseProvider) GetAllCurrencyMints(ctx context.Context) ([]string,
558559
func (dp *DatabaseProvider) CountCurrencyMints(ctx context.Context) (uint64, error) {
559560
return dp.currencies.CountMints(ctx)
560561
}
562+
func (dp *DatabaseProvider) IsCurrencyNameAvailable(ctx context.Context, name string) (bool, error) {
563+
return dp.currencies.IsNameAvailable(ctx, name)
564+
}
561565
func (dp *DatabaseProvider) PutHistoricalCurrencyReserve(ctx context.Context, record *currency.ReserveRecord) error {
562566
return dp.currencies.PutHistoricalReserveRecord(ctx, record)
563567
}

ocp/rpc/currency/availability.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package currency
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
"go.uber.org/zap"
8+
"google.golang.org/grpc/codes"
9+
"google.golang.org/grpc/status"
10+
11+
currencypb "github.com/code-payments/ocp-protobuf-api/generated/go/currency/v1"
12+
13+
"github.com/code-payments/ocp-server/grpc/client"
14+
"github.com/code-payments/ocp-server/ocp/common"
15+
)
16+
17+
var reservedCurrencyNames = []string{
18+
common.CoreMintName,
19+
}
20+
21+
func (s *currencyServer) CheckAvailability(ctx context.Context, req *currencypb.CheckAvailabilityRequest) (*currencypb.CheckAvailabilityResponse, error) {
22+
log := s.log.With(zap.String("method", "CheckAvailability"))
23+
log = client.InjectLoggingMetadata(ctx, log)
24+
25+
name := strings.TrimSpace(req.Name)
26+
27+
if isReservedCurrencyName(name) {
28+
return &currencypb.CheckAvailabilityResponse{
29+
Result: currencypb.CheckAvailabilityResponse_OK,
30+
IsAvailable: false,
31+
}, nil
32+
}
33+
34+
available, err := s.data.IsCurrencyNameAvailable(ctx, name)
35+
if err != nil {
36+
log.With(zap.Error(err)).Warn("failed to check currency name availability")
37+
return nil, status.Error(codes.Internal, "")
38+
}
39+
40+
return &currencypb.CheckAvailabilityResponse{
41+
Result: currencypb.CheckAvailabilityResponse_OK,
42+
IsAvailable: available,
43+
}, nil
44+
}
45+
46+
func isReservedCurrencyName(name string) bool {
47+
for _, reserved := range reservedCurrencyNames {
48+
if strings.EqualFold(name, reserved) {
49+
return true
50+
}
51+
}
52+
return false
53+
}

ocp/rpc/currency/launch.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ func (s *currencyServer) Launch(ctx context.Context, req *currencypb.LaunchReque
5757
}
5858

5959
name := strings.TrimSpace(req.Name)
60-
switch strings.ToLower(name) {
61-
case strings.ToLower(common.CoreMintName):
60+
if isReservedCurrencyName(name) {
6261
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_DENIED}, nil
6362
}
6463

@@ -232,7 +231,7 @@ func (s *currencyServer) Launch(ctx context.Context, req *currencypb.LaunchReque
232231
return s.data.SaveVmMetadata(ctx, vmMetadataRecord)
233232
})
234233
if err == currency.ErrDuplicateCurrency {
235-
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_EXISTS}, nil
234+
return &currencypb.LaunchResponse{Result: currencypb.LaunchResponse_NAME_EXISTS}, nil
236235
} else if err != nil {
237236
log.With(zap.Error(err)).Warn("failed to save currency and vm metadata")
238237
return nil, status.Error(codes.Internal, "")

0 commit comments

Comments
 (0)