Skip to content

Commit 3330e04

Browse files
committed
Add documentation to metadata.go and metadata_test.go
Add tests to achieve 100% coverage for token/metadata.go IssueMetadata#Match does not check issuer identity #1388
1 parent 64f7303 commit 3330e04

File tree

3 files changed

+480
-246
lines changed

3 files changed

+480
-246
lines changed

token/actions.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ type IssueAction struct {
1616
a driver.IssueAction
1717
}
1818

19+
// NewIssueAction returns a new IssueAction for the given driver.IssueAction
20+
func NewIssueAction(a driver.IssueAction) *IssueAction {
21+
return &IssueAction{a: a}
22+
}
23+
1924
// Serialize returns the byte representation of the action.
2025
func (i *IssueAction) Serialize() ([]byte, error) {
2126
return i.a.Serialize()

token/metadata.go

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,32 @@ import (
1717
"github.com/hyperledger-labs/fabric-token-sdk/token/token"
1818
)
1919

20-
// Metadata contains the metadata of a Token Request
20+
// Metadata contains the metadata of a Token Request.
21+
// This metadata is used to unscramble the content of the actions in the Token Request.
22+
// It includes information about issuers, owners, and extra signers.
2123
type Metadata struct {
22-
TokenService driver.TokensService
23-
WalletService driver.WalletService
24+
// TokenService is the service used to handle tokens.
25+
TokenService driver.TokensService
26+
// WalletService is the service used to handle wallets and identities.
27+
WalletService driver.WalletService
28+
// TokenRequestMetadata contains the metadata of the Token Request as defined by the driver.
2429
TokenRequestMetadata *driver.TokenRequestMetadata
25-
Logger logging.Logger
30+
// Logger is the logger used by this struct.
31+
Logger logging.Logger
2632
}
2733

2834
// SpentTokenID returns the token IDs of the tokens that were spent by the Token Request this metadata is associated with.
35+
// It iterates over all issue and transfer actions and collects the IDs of the input tokens.
2936
func (m *Metadata) SpentTokenID() []*token.ID {
3037
var res []*token.ID
38+
// Collect token IDs from issue actions.
39+
// Note: issue actions usually don't have inputs, but some drivers might use them (e.g., for token upgrades).
3140
for _, issue := range m.TokenRequestMetadata.Issues {
3241
for _, input := range issue.Inputs {
3342
res = append(res, input.TokenID)
3443
}
3544
}
45+
// Collect token IDs from transfer actions.
3646
for _, transfer := range m.TokenRequestMetadata.Transfers {
3747
for _, input := range transfer.Inputs {
3848
res = append(res, input.TokenID)
@@ -43,25 +53,30 @@ func (m *Metadata) SpentTokenID() []*token.ID {
4353
}
4454

4555
// FilterBy returns a new Metadata containing only the metadata that matches the given enrollment IDs.
46-
// For Issue actions, for each issue:
47-
// - The sender;
48-
// - The returned metadata will contain only the outputs whose owner has the given enrollment IDs.
49-
// For Transfer actions, for each action:
50-
// - The list of token IDs will be empty;
51-
// - The returned metadata will contain only the outputs whose owner has the given enrollment IDs;
52-
// - The senders are included if and only if there is at least one output whose owner has the given enrollment IDs.
53-
// Application metadata is always included
56+
// This is used to share with a party only the metadata they are entitled to see.
57+
//
58+
// For Issue actions:
59+
// - The issuer information is always included.
60+
// - Only metadata for outputs owned by the given enrollment IDs is included.
61+
//
62+
// For Transfer actions:
63+
// - Only metadata for outputs owned by the given enrollment IDs is included.
64+
// - Sender information is included if and only if there is at least one output owned by the given enrollment IDs.
65+
//
66+
// Application metadata is always included.
5467
func (m *Metadata) FilterBy(ctx context.Context, eIDs ...string) (*Metadata, error) {
5568
if len(eIDs) == 0 {
5669
return m, nil
5770
}
5871

5972
eIDSet := collections.NewSet(eIDs...)
6073

74+
// Filter issue metadata.
6175
issues, err := m.filterIssues(ctx, m.TokenRequestMetadata.Issues, eIDSet)
6276
if err != nil {
6377
return nil, errors.WithMessagef(err, "failed filtering issues")
6478
}
79+
// Filter transfer metadata.
6580
transfers, err := m.filterTransfers(ctx, m.TokenRequestMetadata.Transfers, eIDSet)
6681
if err != nil {
6782
return nil, errors.WithMessagef(err, "failed filtering transfers")
@@ -77,7 +92,6 @@ func (m *Metadata) FilterBy(ctx context.Context, eIDs ...string) (*Metadata, err
7792
Logger: m.Logger,
7893
}
7994

80-
// TODO: update this log
8195
m.Logger.Debugf("filtered metadata for [% x] from [%d:%d] to [%d:%d]",
8296
eIDs,
8397
len(m.TokenRequestMetadata.Issues), len(m.TokenRequestMetadata.Transfers),
@@ -86,6 +100,7 @@ func (m *Metadata) FilterBy(ctx context.Context, eIDs ...string) (*Metadata, err
86100
return clone, nil
87101
}
88102

103+
// filterIssues filters the issue metadata based on the provided enrollment IDs.
89104
func (m *Metadata) filterIssues(ctx context.Context, issues []*driver.IssueMetadata, eIDSet collections.Set[string]) ([]*driver.IssueMetadata, error) {
90105
cloned := make([]*driver.IssueMetadata, 0, len(issues))
91106
for _, issue := range m.TokenRequestMetadata.Issues {
@@ -98,12 +113,15 @@ func (m *Metadata) filterIssues(ctx context.Context, issues []*driver.IssueMetad
98113

99114
counter := 0
100115
for _, output := range issue.Outputs {
116+
// Check if any of the receivers of the output matches the enrollment IDs.
101117
if found, err := m.contains(ctx, output.Receivers, eIDSet); err != nil {
102118
return nil, errors.WithMessagef(err, "failed checking receivers")
103119
} else if found {
120+
// If matched, include the full output metadata.
104121
clone.Outputs = append(clone.Outputs, output)
105122
counter++
106123
} else {
124+
// If not matched, include a nil entry to preserve the indexing.
107125
clone.Outputs = append(clone.Outputs, nil)
108126
}
109127
}
@@ -115,6 +133,7 @@ func (m *Metadata) filterIssues(ctx context.Context, issues []*driver.IssueMetad
115133
return cloned, nil
116134
}
117135

136+
// filterTransfers filters the transfer metadata based on the provided enrollment IDs.
118137
func (m *Metadata) filterTransfers(ctx context.Context, issues []*driver.TransferMetadata, eIDSet collections.Set[string]) ([]*driver.TransferMetadata, error) {
119138
cloned := make([]*driver.TransferMetadata, 0, len(issues))
120139
for _, transfer := range m.TokenRequestMetadata.Transfers {
@@ -124,8 +143,7 @@ func (m *Metadata) filterTransfers(ctx context.Context, issues []*driver.Transfe
124143
ExtraSigners: transfer.ExtraSigners,
125144
}
126145

127-
// Filter outputs
128-
// if the receiver has the given enrollment ID, add it. Otherwise, add empty entries
146+
// Filter outputs: if the receiver has the given enrollment ID, add it. Otherwise, add empty entries.
129147
counter := 0
130148
for _, output := range transfer.Outputs {
131149
if found, err := m.contains(ctx, output.Receivers, eIDSet); err != nil {
@@ -138,13 +156,11 @@ func (m *Metadata) filterTransfers(ctx context.Context, issues []*driver.Transfe
138156
}
139157
}
140158

141-
// if counter == 0, it means that this transfer does not contain any output for the given enrollment IDs.
142-
// Therefore, no metadata should be given to the passed enrollment IDs.
143-
// if counter > 0, it means that this transfer contains at least one output for the given enrollment IDs.
144-
// Append the senders to the transfer metadata.
159+
// Prepare empty input metadata entries.
145160
for range transfer.Inputs {
146161
clone.Inputs = append(clone.Inputs, &driver.TransferInputMetadata{})
147162
}
163+
// If at least one output matched, include the sender information for all inputs.
148164
if counter > 0 {
149165
for i, input := range transfer.Inputs {
150166
clone.Inputs[i].Senders = input.Senders
@@ -158,13 +174,15 @@ func (m *Metadata) filterTransfers(ctx context.Context, issues []*driver.Transfe
158174
return cloned, nil
159175
}
160176

177+
// contains checks if any of the given auditable identities matches the provided enrollment IDs.
161178
func (m *Metadata) contains(ctx context.Context, receivers []*driver.AuditableIdentity, eIDSet collections.Set[string]) (bool, error) {
162179
for _, receiver := range receivers {
163-
// If the receiver has the given enrollment ID, add it
180+
// Resolve the enrollment ID of the receiver.
164181
recipientEID, err := m.WalletService.GetEnrollmentID(ctx, receiver.Identity, receiver.AuditInfo)
165182
if err != nil {
166183
return false, errors.Wrap(err, "failed getting enrollment ID")
167184
}
185+
// Check if the enrollment ID is in the set.
168186
if eIDSet.Contains(recipientEID) {
169187
logger.Debugf("eid [%s] found in list [%v]", recipientEID, eIDSet)
170188

@@ -177,7 +195,7 @@ func (m *Metadata) contains(ctx context.Context, receivers []*driver.AuditableId
177195
return false, nil
178196
}
179197

180-
// Issue returns the i-th issue metadata, if present
198+
// Issue returns the i-th issue metadata, if present.
181199
func (m *Metadata) Issue(i int) (*IssueMetadata, error) {
182200
if i >= len(m.TokenRequestMetadata.Issues) {
183201
return nil, errors.Errorf("index [%d] out of range [0:%d]", i, len(m.TokenRequestMetadata.Issues))
@@ -186,7 +204,7 @@ func (m *Metadata) Issue(i int) (*IssueMetadata, error) {
186204
return &IssueMetadata{IssueMetadata: m.TokenRequestMetadata.Issues[i]}, nil
187205
}
188206

189-
// Transfer returns the i-th transfer metadata, if present
207+
// Transfer returns the i-th transfer metadata, if present.
190208
func (m *Metadata) Transfer(i int) (*TransferMetadata, error) {
191209
if i >= len(m.TokenRequestMetadata.Transfers) {
192210
return nil, errors.Errorf("index [%d] out of range [0:%d]", i, len(m.TokenRequestMetadata.Transfers))
@@ -195,48 +213,53 @@ func (m *Metadata) Transfer(i int) (*TransferMetadata, error) {
195213
return &TransferMetadata{TransferMetadata: m.TokenRequestMetadata.Transfers[i]}, nil
196214
}
197215

198-
// IssueMetadata contains the metadata of an issue action
216+
// IssueMetadata contains the metadata of an issue action.
199217
type IssueMetadata struct {
200218
*driver.IssueMetadata
201219
}
202220

203-
// Match returns true if the given action matches this metadata
221+
// Match returns true if the given action matches this metadata.
222+
// It performs a deep check of inputs, outputs, extra signers, and the issuer identity.
204223
func (m *IssueMetadata) Match(action *IssueAction) error {
205224
if action == nil {
206225
return errors.New("can't match issue metadata to issue action: nil issue action")
207226
}
208227

209-
// validate action
228+
// Validate the action's structure.
210229
if err := action.Validate(); err != nil {
211230
return errors.Wrap(err, "failed validating issue action")
212231
}
213232

214-
// check inputs
233+
// Check that the number of inputs matches.
215234
if len(m.Inputs) != action.NumInputs() {
216235
return errors.Errorf("expected [%d] inputs but got [%d]", len(m.Inputs), action.NumInputs())
217236
}
218237

219-
// check outputs
238+
// Check that the number of outputs matches.
220239
if len(m.Outputs) != action.NumOutputs() {
221240
return errors.Errorf("expected [%d] outputs but got [%d]", len(m.Outputs), action.NumOutputs())
222241
}
223242

224-
// extra signer
225-
extraSigner := action.a.ExtraSigners()
226-
if len(m.ExtraSigners) != len(extraSigner) {
227-
return errors.Errorf("expected [%d] extra signers but got [%d]", len(extraSigner), len(m.ExtraSigners))
243+
// Check that the extra signers are the same.
244+
extraSigners := action.a.ExtraSigners()
245+
if len(m.ExtraSigners) != len(extraSigners) {
246+
return errors.Errorf("expected [%d] extra signers but got [%d]", len(extraSigners), len(m.ExtraSigners))
228247
}
229-
// check that the extra signers are the same
230-
for i, signer := range extraSigner {
248+
for i, signer := range extraSigners {
231249
if !slices.ContainsFunc(m.ExtraSigners, signer.Equal) {
232250
return errors.Errorf("expected extra signer [%s] but got [%s]", signer, m.ExtraSigners[i])
233251
}
234252
}
235253

254+
// Check that the issuer identity matches.
255+
if !m.Issuer.Identity.Equal(action.GetIssuer()) {
256+
return errors.Errorf("expected issuer [%s] but got [%s]", m.Issuer.Identity, action.GetIssuer())
257+
}
258+
236259
return nil
237260
}
238261

239-
// IsOutputAbsent returns true if the given output's metadata is absent
262+
// IsOutputAbsent returns true if the j-th output's metadata is absent (e.g., filtered out).
240263
func (m *IssueMetadata) IsOutputAbsent(j int) bool {
241264
if j < 0 || j >= len(m.Outputs) {
242265
return true
@@ -245,63 +268,64 @@ func (m *IssueMetadata) IsOutputAbsent(j int) bool {
245268
return m.Outputs[j] == nil
246269
}
247270

248-
// TransferMetadata contains the metadata of a transfer action
271+
// TransferMetadata contains the metadata of a transfer action.
249272
type TransferMetadata struct {
250273
*driver.TransferMetadata
251274
}
252275

253-
// Match returns true if the given action matches this metadata
276+
// Match returns true if the given action matches this metadata.
277+
// It performs a deep check of inputs, outputs, extra signers, and the issuer identity (if present).
254278
func (m *TransferMetadata) Match(action *TransferAction) error {
255279
if action == nil {
256280
return errors.New("can't match transfer metadata to transfer action: nil issue action")
257281
}
258282

259-
// validate action
283+
// Validate the action's structure.
260284
if err := action.Validate(); err != nil {
261285
return errors.Wrap(err, "failed validating issue action")
262286
}
263287

264-
// inputs
288+
// Check that the number of inputs matches.
265289
if len(m.Inputs) != action.NumInputs() {
266290
return errors.Errorf("expected [%d] inputs but got [%d]", len(m.Inputs), action.NumInputs())
267291
}
268292

269-
// outputs
293+
// Check that the number of outputs matches.
270294
if len(m.Outputs) != action.NumOutputs() {
271295
return errors.Errorf("expected [%d] outputs but got [%d]", len(m.Outputs), action.NumOutputs())
272296
}
273297

274-
// extra signer
275-
extraSigner := action.ExtraSigners()
276-
if len(m.ExtraSigners) != len(extraSigner) {
277-
return errors.Errorf("expected [%d] extra signers but got [%d]", len(m.ExtraSigners), len(extraSigner))
298+
// Check that the extra signers are the same.
299+
extraSigners := action.ExtraSigners()
300+
if len(m.ExtraSigners) != len(extraSigners) {
301+
return errors.Errorf("expected [%d] extra signers but got [%d]", len(m.ExtraSigners), len(extraSigners))
278302
}
279-
// check that the extra signers are the same
280-
for i, signer := range extraSigner {
303+
for i, signer := range extraSigners {
281304
if !signer.Equal(m.ExtraSigners[i]) {
282305
return errors.Errorf("expected extra signer [%s] but got [%s]", m.ExtraSigners[i], signer)
283306
}
284307
}
285308

309+
// Check that the issuer identity matches, if present in the metadata.
286310
if !m.Issuer.Equal(action.GetIssuer()) {
287311
return errors.Errorf("expected issuer [%s] but got [%s]", m.Issuer, action.GetIssuer().Bytes())
288312
}
289313

290314
return nil
291315
}
292316

293-
// IsOutputAbsent returns true if the given output's metadata is absent
317+
// IsOutputAbsent returns true if the j-th output's metadata is absent (e.g., filtered out).
294318
func (m *TransferMetadata) IsOutputAbsent(j int) bool {
295-
if j >= len(m.Outputs) {
319+
if j < 0 || j >= len(m.Outputs) {
296320
return true
297321
}
298322

299323
return m.Outputs[j] == nil
300324
}
301325

302-
// IsInputAbsent returns true if the given input's metadata is absent
326+
// IsInputAbsent returns true if the j-th input's metadata is absent (e.g., filtered out).
303327
func (m *TransferMetadata) IsInputAbsent(j int) bool {
304-
if j >= len(m.Inputs) {
328+
if j < 0 || j >= len(m.Inputs) {
305329
return true
306330
}
307331

0 commit comments

Comments
 (0)