Skip to content

Commit 53580ce

Browse files
author
Nitesh
committed
feat: add comprehensive input validation for token operations
- Add validation package with functions for amount, address, token type, metadata - Add custom error types for validation failures - Use validation in Issue, Transfer, and Redeem methods - Fix test compilation errors in sherdlock - Fix mutex unlock bug in cachedFetcher.update() Signed-off-by: Nitesh <nitesh@example.com>
1 parent f157056 commit 53580ce

6 files changed

Lines changed: 834 additions & 103 deletions

File tree

token/request.go

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/hyperledger-labs/fabric-token-sdk/token/driver"
2121
"github.com/hyperledger-labs/fabric-token-sdk/token/driver/protos-go/request"
2222
"github.com/hyperledger-labs/fabric-token-sdk/token/services/utils"
23+
"github.com/hyperledger-labs/fabric-token-sdk/token/services/validation"
2324
"github.com/hyperledger-labs/fabric-token-sdk/token/token"
2425
"go.uber.org/zap/zapcore"
2526
)
@@ -283,24 +284,30 @@ func (r *Request) ID() RequestAnchor {
283284
// Additional options can be passed to customize the action.
284285
func (r *Request) Issue(ctx context.Context, wallet *IssuerWallet, receiver Identity, typ token.Type, q uint64, opts ...IssueOption) (*IssueAction, error) {
285286
logger.DebugfContext(ctx, "Start issue")
286-
logger.DebugfContext(ctx, "Done issue")
287+
287288
if wallet == nil {
288289
return nil, errors.Errorf("wallet is nil")
289290
}
290-
if typ == "" {
291-
return nil, errors.Errorf("type is empty")
291+
if err := validation.ValidateTokenType(string(typ)); err != nil {
292+
return nil, errors.Wrap(err, "invalid token type")
293+
}
294+
if err := validation.ValidateAmount(q, 0); err != nil {
295+
return nil, errors.Wrap(err, "invalid amount")
296+
}
297+
if receiver.IsNone() {
298+
return nil, errors.Errorf("all recipients should be defined")
292299
}
293-
if q == 0 {
294-
return nil, errors.Errorf("q is zero")
300+
if r.TokenService == nil || r.TokenService.PublicParametersManager() == nil || r.TokenService.PublicParametersManager().PublicParameters() == nil {
301+
return nil, errors.Errorf("token service is not properly initialized")
295302
}
303+
304+
// Validate amount doesn't exceed max token value
296305
maxTokenValue := r.TokenService.PublicParametersManager().PublicParameters().MaxTokenValue()
297306
if q > maxTokenValue {
298-
return nil, errors.Errorf("q is larger than max token value [%d]", maxTokenValue)
307+
return nil, errors.Errorf("amount exceeds max token value [%d]", maxTokenValue)
299308
}
300309

301-
if receiver.IsNone() {
302-
return nil, errors.Errorf("all recipients should be defined")
303-
}
310+
logger.DebugfContext(ctx, "Done issue")
304311

305312
id, err := wallet.GetIssuerIdentity(typ)
306313
if err != nil {
@@ -312,6 +319,11 @@ func (r *Request) Issue(ctx context.Context, wallet *IssuerWallet, receiver Iden
312319
return nil, errors.WithMessagef(err, "failed compiling options [%v]", opts)
313320
}
314321

322+
// Validate metadata using the validation package
323+
if err := validation.ValidateMetadata(opt.Attributes); err != nil {
324+
return nil, errors.Wrap(err, "invalid metadata")
325+
}
326+
315327
// Compute Issue
316328
action, metaRaw, err := r.TokenService.tms.IssueService().Issue(
317329
ctx,
@@ -343,15 +355,51 @@ func (r *Request) Issue(ctx context.Context, wallet *IssuerWallet, receiver Iden
343355
// In other words, owners[0] will receives values[0], and so on.
344356
// Additional options can be passed to customize the action.
345357
func (r *Request) Transfer(ctx context.Context, wallet *OwnerWallet, typ token.Type, values []uint64, owners []Identity, opts ...TransferOption) (*TransferAction, error) {
346-
for _, v := range values {
358+
if wallet == nil {
359+
return nil, errors.Errorf("wallet is nil")
360+
}
361+
if r.TokenService == nil || r.TokenService.PublicParametersManager() == nil || r.TokenService.PublicParametersManager().PublicParameters() == nil {
362+
return nil, errors.Errorf("token service is not properly initialized")
363+
}
364+
365+
// Validate token type
366+
if err := validation.ValidateTokenType(string(typ)); err != nil {
367+
return nil, errors.Wrap(err, "invalid token type")
368+
}
369+
370+
// Validate values using the validation package
371+
maxTokenValue := r.TokenService.PublicParametersManager().PublicParameters().MaxTokenValue()
372+
for i, v := range values {
347373
if v == 0 {
348-
return nil, errors.Errorf("value is zero")
374+
return nil, errors.Errorf("value at index %d is zero", i)
375+
}
376+
if v > maxTokenValue {
377+
return nil, errors.Errorf("value at index %d exceeds max token value [%d]", i, maxTokenValue)
349378
}
350379
}
380+
381+
// Validate owners match values length
382+
if len(owners) != len(values) {
383+
return nil, errors.Errorf("number of owners [%d] does not match number of values [%d]", len(owners), len(values))
384+
}
385+
386+
// Validate all owners are defined
387+
for i, owner := range owners {
388+
if owner.IsNone() {
389+
return nil, errors.Errorf("owner at index %d is not defined", i)
390+
}
391+
}
392+
351393
opt, err := CompileTransferOptions(opts...)
352394
if err != nil {
353395
return nil, errors.WithMessagef(err, "failed compiling options [%v]", opts)
354396
}
397+
398+
// Validate metadata using the validation package
399+
if err := validation.ValidateMetadata(opt.Attributes); err != nil {
400+
return nil, errors.Wrap(err, "invalid metadata")
401+
}
402+
355403
tokenIDs, outputTokens, err := r.prepareTransfer(ctx, false, wallet, typ, values, owners, opt)
356404
if err != nil {
357405
return nil, errors.Wrap(err, "failed preparing transfer")
@@ -397,10 +445,37 @@ func (r *Request) Transfer(ctx context.Context, wallet *OwnerWallet, typ token.T
397445
// The action redeems tokens of the passed type for a total amount matching the passed value.
398446
// Additional options can be passed to customize the action.
399447
func (r *Request) Redeem(ctx context.Context, wallet *OwnerWallet, typ token.Type, value uint64, opts ...TransferOption) (*TransferAction, error) {
448+
if wallet == nil {
449+
return nil, errors.Errorf("wallet is nil")
450+
}
451+
if r.TokenService == nil || r.TokenService.PublicParametersManager() == nil || r.TokenService.PublicParametersManager().PublicParameters() == nil {
452+
return nil, errors.Errorf("token service is not properly initialized")
453+
}
454+
455+
// Validate token type
456+
if err := validation.ValidateTokenType(string(typ)); err != nil {
457+
return nil, errors.Wrap(err, "invalid token type")
458+
}
459+
460+
// Validate value doesn't exceed max
461+
maxTokenValue := r.TokenService.PublicParametersManager().PublicParameters().MaxTokenValue()
462+
if value == 0 {
463+
return nil, errors.Errorf("redeem value is zero")
464+
}
465+
if value > maxTokenValue {
466+
return nil, errors.Errorf("redeem value exceeds max token value [%d]", maxTokenValue)
467+
}
468+
400469
opt, err := CompileTransferOptions(opts...)
401470
if err != nil {
402471
return nil, errors.WithMessagef(err, "failed compiling options [%v]", opts)
403472
}
473+
474+
// Validate metadata using the validation package
475+
if err := validation.ValidateMetadata(opt.Attributes); err != nil {
476+
return nil, errors.Wrap(err, "invalid metadata")
477+
}
478+
404479
tokenIDs, outputTokens, err := r.prepareTransfer(ctx, true, wallet, typ, []uint64{value}, []Identity{nil}, opt)
405480
if err != nil {
406481
return nil, errors.Wrap(err, "failed preparing transfer")

token/request_test.go

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -525,15 +525,15 @@ func TestRequest_Issue(t *testing.T) {
525525
wallet := &IssuerWallet{}
526526
_, err := req.Issue(ctx, wallet, Identity("receiver"), "", 100)
527527
require.Error(t, err)
528-
assert.Contains(t, err.Error(), "type is empty")
528+
assert.Contains(t, err.Error(), "invalid token type")
529529
})
530530

531531
t.Run("zero quantity", func(t *testing.T) {
532532
req := NewRequest(nil, "test-anchor")
533533
wallet := &IssuerWallet{}
534534
_, err := req.Issue(ctx, wallet, Identity("receiver"), "USD", 0)
535535
require.Error(t, err)
536-
assert.Contains(t, err.Error(), "q is zero")
536+
assert.Contains(t, err.Error(), "invalid amount")
537537
})
538538

539539
t.Run("none receiver", func(t *testing.T) {
@@ -577,28 +577,59 @@ func TestRequest_Issue(t *testing.T) {
577577
mockWallet := &IssuerWallet{}
578578
_, err := req.Issue(ctx, mockWallet, Identity("receiver"), "USD", 200)
579579
require.Error(t, err)
580-
assert.Contains(t, err.Error(), "q is larger than max token value")
580+
assert.Contains(t, err.Error(), "amount exceeds max token value")
581581
})
582582
}
583583

584584
// TestRequest_Transfer tests the Transfer function
585585
func TestRequest_Transfer(t *testing.T) {
586586
ctx := t.Context()
587587

588-
t.Run("zero value", func(t *testing.T) {
588+
t.Run("nil wallet", func(t *testing.T) {
589589
req := NewRequest(nil, "test-anchor")
590+
_, err := req.Transfer(ctx, nil, "USD", []uint64{100}, []Identity{Identity("receiver")})
591+
require.Error(t, err)
592+
assert.Contains(t, err.Error(), "wallet is nil")
593+
})
594+
595+
t.Run("zero value", func(t *testing.T) {
596+
mockPP := &driver2.PublicParameters{}
597+
mockPP.MaxTokenValueReturns(1000000)
598+
599+
mockPPM := &driver2.PublicParamsManager{}
600+
mockPPM.PublicParametersReturns(mockPP)
601+
602+
tms := &ManagementService{
603+
publicParametersManager: &PublicParametersManager{
604+
ppm: mockPPM,
605+
pp: &PublicParameters{PublicParameters: mockPP},
606+
},
607+
}
608+
req := NewRequest(tms, "test-anchor")
590609
wallet := &OwnerWallet{}
591610
_, err := req.Transfer(ctx, wallet, "USD", []uint64{0, 100}, []Identity{Identity("receiver1"), Identity("receiver2")})
592611
require.Error(t, err)
593-
assert.Contains(t, err.Error(), "value is zero")
612+
assert.Contains(t, err.Error(), "value at index 0 is zero")
594613
})
595614

596615
t.Run("multiple zero values", func(t *testing.T) {
597-
req := NewRequest(nil, "test-anchor")
616+
mockPP := &driver2.PublicParameters{}
617+
mockPP.MaxTokenValueReturns(1000000)
618+
619+
mockPPM := &driver2.PublicParamsManager{}
620+
mockPPM.PublicParametersReturns(mockPP)
621+
622+
tms := &ManagementService{
623+
publicParametersManager: &PublicParametersManager{
624+
ppm: mockPPM,
625+
pp: &PublicParameters{PublicParameters: mockPP},
626+
},
627+
}
628+
req := NewRequest(tms, "test-anchor")
598629
wallet := &OwnerWallet{}
599630
_, err := req.Transfer(ctx, wallet, "USD", []uint64{100, 0}, []Identity{Identity("receiver1"), Identity("receiver2")})
600631
require.Error(t, err)
601-
assert.Contains(t, err.Error(), "value is zero")
632+
assert.Contains(t, err.Error(), "value at index 1 is zero")
602633
})
603634
}
604635

0 commit comments

Comments
 (0)