Skip to content

Commit 45e776e

Browse files
committed
refactor(auditor): resolve the TMS through the provider in Audit too
Audit took the TMS from the request while Append resolved it through the provider, letting a caller influence which wallet service attributes the record. Both now resolve through the provider; a provider error fails the audit. Signed-off-by: Evan <evanyan@sign.global>
1 parent bd3ccbf commit 45e776e

3 files changed

Lines changed: 74 additions & 36 deletions

File tree

token/services/auditor/auditor.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,15 @@ func (a *Service) Audit(ctx context.Context, tx Transaction) (*token.InputStream
159159
start := time.Now()
160160
logger.DebugfContext(ctx, "audit transaction [%s]....", tx.ID())
161161
request := tx.Request()
162+
// the TMS comes from the provider, as in Append, so the request cannot
163+
// influence which wallet service attributes the record
164+
tms, err := a.tmsProvider.TokenManagementService(token.WithTMSID(a.tmsID))
165+
if err != nil {
166+
return nil, nil, err
167+
}
162168
// the record is completed before the enrollment IDs are collected, so that
163169
// the locks cover the enrollment ID every input is finally booked under
164-
record, err := newRequestWrapper(request, request.TokenService).AuditRecord(ctx)
170+
record, err := newRequestWrapper(request, tms).AuditRecord(ctx)
165171
if err != nil {
166172
return nil, nil, errors.WithMessagef(err, "failed getting transaction audit record")
167173
}

token/services/auditor/auditor_internal_test.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -961,9 +961,22 @@ type stubTMSWithExtensions struct {
961961
dep.TokenManagementServiceWithExtensions
962962
}
963963

964-
type stubTMSProvider struct{}
964+
// tmsExt adapts a ManagementService to the type the TMS provider returns;
965+
// SetTokenManagementService is never exercised by the auditor.
966+
type tmsExt struct{ *token.ManagementService }
967+
968+
func (tmsExt) SetTokenManagementService(*token.Request) error { return nil }
969+
970+
// stubTMSProvider returns tms when set, an inert stub otherwise.
971+
type stubTMSProvider struct {
972+
tms dep.TokenManagementServiceWithExtensions
973+
}
965974

966975
func (s *stubTMSProvider) TokenManagementService(...token.ServiceOption) (dep.TokenManagementServiceWithExtensions, error) {
976+
if s.tms != nil {
977+
return s.tms, nil
978+
}
979+
967980
return &stubTMSWithExtensions{}, nil
968981
}
969982

@@ -1234,9 +1247,11 @@ func newExactSpendTestContext(t *testing.T, payer, recipient driver.Identity, pa
12341247
svc := &Service{
12351248
networkProvider: &stubNetworkProvider{net: network.NewNetwork(&stubNetworkDriver{}, nil)},
12361249
auditDB: auditDB,
1237-
tmsProvider: &stubTMSProvider{},
1238-
metrics: newMetrics(nil),
1239-
lockConfig: DefaultLockConfig(),
1250+
// the gap filling resolves through the provider-returned TMS, so the
1251+
// provider hands out the same TMS the request is built over
1252+
tmsProvider: &stubTMSProvider{tms: tmsExt{tms}},
1253+
metrics: newMetrics(nil),
1254+
lockConfig: DefaultLockConfig(),
12401255
}
12411256

12421257
return &exactSpendTestContext{

token/services/auditor/auditor_test.go

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,26 @@ func newFakeStore() *auditmock.AuditTransactionStore {
122122
return fakeStore
123123
}
124124

125-
// newTestService creates a Service with the given auditDB and checkService for testing.
126-
func newTestService(auditDB *auditdb.StoreService, checkService auditor.CheckService) *auditor.Service {
125+
// tmsWithExtensions adapts a ManagementService to the type the TMS provider
126+
// returns; SetTokenManagementService is never exercised by the auditor.
127+
type tmsWithExtensions struct{ *token.ManagementService }
128+
129+
func (tmsWithExtensions) SetTokenManagementService(*token.Request) error { return nil }
130+
131+
// newTestService creates a Service with the given auditDB and checkService for
132+
// testing. Audit and Append resolve the TMS through the provider, so a working
133+
// one is always wired in.
134+
func newTestService(t *testing.T, auditDB *auditdb.StoreService, checkService auditor.CheckService) *auditor.Service {
135+
t.Helper()
136+
tmsProv := &depmock.TokenManagementServiceProvider{}
137+
tmsProv.TokenManagementServiceReturns(tmsWithExtensions{newTestManagementService(t)}, nil)
138+
127139
return auditor.NewService(
128140
token.TMSID{},
129141
nil, // networkProvider
130142
auditDB,
131143
nil, // tokenDB
132-
nil, // tmsProvider
144+
tmsProv,
133145
nil, // finalityTracer
134146
nil, // metricsProvider
135147
checkService,
@@ -149,7 +161,7 @@ func newStubNetwork() *network.Network {
149161
func TestService_Check_ReturnsIssues(t *testing.T) {
150162
cs := &auditmock.CheckService{}
151163
cs.CheckReturns([]string{"tx-aaa", "tx-bbb"}, nil)
152-
svc := newTestService(newTestStoreService(t, newFakeStore()), cs)
164+
svc := newTestService(t, newTestStoreService(t, newFakeStore()), cs)
153165
got, err := svc.Check(context.Background())
154166
require.NoError(t, err)
155167
assert.Equal(t, []string{"tx-aaa", "tx-bbb"}, got)
@@ -159,15 +171,15 @@ func TestService_Check_ReturnsError(t *testing.T) {
159171
expectedErr := errors.New("check failed")
160172
cs := &auditmock.CheckService{}
161173
cs.CheckReturns(nil, expectedErr)
162-
svc := newTestService(newTestStoreService(t, newFakeStore()), cs)
174+
svc := newTestService(t, newTestStoreService(t, newFakeStore()), cs)
163175
_, err := svc.Check(context.Background())
164176
assert.ErrorIs(t, err, expectedErr)
165177
}
166178

167179
func TestService_Check_EmptyIssues(t *testing.T) {
168180
cs := &auditmock.CheckService{}
169181
cs.CheckReturns([]string{}, nil)
170-
svc := newTestService(newTestStoreService(t, newFakeStore()), cs)
182+
svc := newTestService(t, newTestStoreService(t, newFakeStore()), cs)
171183
got, err := svc.Check(context.Background())
172184
require.NoError(t, err)
173185
assert.Empty(t, got)
@@ -194,7 +206,7 @@ func TestGetByTMSID_GetServiceError_ReturnsNil(t *testing.T) {
194206
// ---------------------------------------------------------------------------
195207

196208
func TestService_Release_IncrementsCounter(t *testing.T) {
197-
svc := newTestService(newTestStoreService(t, newFakeStore()), nil)
209+
svc := newTestService(t, newTestStoreService(t, newFakeStore()), nil)
198210
tx := &auditmock.Transaction{}
199211
tx.IDReturns("tx-release")
200212
tx.RequestReturns(&token.Request{Anchor: "tx-release"})
@@ -204,7 +216,7 @@ func TestService_Release_IncrementsCounter(t *testing.T) {
204216
}
205217

206218
func TestService_SetStatus_Success(t *testing.T) {
207-
svc := newTestService(newTestStoreService(t, newFakeStore()), nil)
219+
svc := newTestService(t, newTestStoreService(t, newFakeStore()), nil)
208220
err := svc.SetStatus(context.Background(), "tx-set", auditdb.Confirmed, "ok")
209221
assert.NoError(t, err)
210222
}
@@ -213,15 +225,15 @@ func TestService_SetStatus_Error(t *testing.T) {
213225
expectedErr := errors.New("db write error")
214226
fakeStore := newFakeStore()
215227
fakeStore.SetStatusReturns(expectedErr)
216-
svc := newTestService(newTestStoreService(t, fakeStore), nil)
228+
svc := newTestService(t, newTestStoreService(t, fakeStore), nil)
217229
err := svc.SetStatus(context.Background(), "tx-set", auditdb.Confirmed, "ok")
218230
assert.ErrorIs(t, err, expectedErr)
219231
}
220232

221233
func TestService_GetStatus_Success(t *testing.T) {
222234
fakeStore := newFakeStore()
223235
fakeStore.GetStatusReturns(auditdb.Confirmed, "done", nil)
224-
svc := newTestService(newTestStoreService(t, fakeStore), nil)
236+
svc := newTestService(t, newTestStoreService(t, fakeStore), nil)
225237
status, msg, err := svc.GetStatus(context.Background(), "tx-get")
226238
require.NoError(t, err)
227239
assert.Equal(t, auditdb.Confirmed, status)
@@ -232,7 +244,7 @@ func TestService_GetStatus_Error(t *testing.T) {
232244
expectedErr := errors.New("db read error")
233245
fakeStore := newFakeStore()
234246
fakeStore.GetStatusReturns(0, "", expectedErr)
235-
svc := newTestService(newTestStoreService(t, fakeStore), nil)
247+
svc := newTestService(t, newTestStoreService(t, fakeStore), nil)
236248
_, _, err := svc.GetStatus(context.Background(), "tx-get")
237249
assert.ErrorIs(t, err, expectedErr)
238250
}
@@ -241,7 +253,7 @@ func TestService_GetTokenRequest_Success(t *testing.T) {
241253
data := []byte("raw-token-request")
242254
fakeStore := newFakeStore()
243255
fakeStore.GetTokenRequestReturns(data, nil)
244-
svc := newTestService(newTestStoreService(t, fakeStore), nil)
256+
svc := newTestService(t, newTestStoreService(t, fakeStore), nil)
245257
got, err := svc.GetTokenRequest(context.Background(), "tx-tok")
246258
require.NoError(t, err)
247259
assert.Equal(t, data, got)
@@ -251,7 +263,7 @@ func TestService_GetTokenRequest_Error(t *testing.T) {
251263
expectedErr := errors.New("not found")
252264
fakeStore := newFakeStore()
253265
fakeStore.GetTokenRequestReturns(nil, expectedErr)
254-
svc := newTestService(newTestStoreService(t, fakeStore), nil)
266+
svc := newTestService(t, newTestStoreService(t, fakeStore), nil)
255267
_, err := svc.GetTokenRequest(context.Background(), "tx-tok")
256268
assert.ErrorIs(t, err, expectedErr)
257269
}
@@ -261,7 +273,7 @@ func TestService_GetTokenRequest_Error(t *testing.T) {
261273
// ---------------------------------------------------------------------------
262274

263275
func TestService_Validate(t *testing.T) {
264-
svc := newTestService(nil, nil)
276+
svc := newTestService(t, nil, nil)
265277
assert.Panics(t, func() {
266278
_ = svc.Validate(context.Background(), &token.Request{})
267279
})
@@ -290,7 +302,7 @@ func TestService_Audit_AuditRecordError(t *testing.T) {
290302
)
291303
require.NoError(t, err)
292304

293-
svc := newTestService(newTestStoreService(t, newFakeStore()), nil)
305+
svc := newTestService(t, newTestStoreService(t, newFakeStore()), nil)
294306
tx := &auditmock.Transaction{}
295307
tx.IDReturns("tx-err")
296308
tx.RequestReturns(token.NewRequest(badTMS, token.RequestAnchor("tx-err")))
@@ -301,7 +313,7 @@ func TestService_Audit_AuditRecordError(t *testing.T) {
301313
}
302314

303315
func TestService_Audit_Success(t *testing.T) {
304-
svc := newTestService(newTestStoreService(t, newFakeStore()), nil)
316+
svc := newTestService(t, newTestStoreService(t, newFakeStore()), nil)
305317
tx := &auditmock.Transaction{}
306318
tx.IDReturns("tx-audit-ok")
307319
tx.RequestReturns(token.NewRequest(newTestManagementService(t), token.RequestAnchor("tx-audit-ok")))
@@ -316,7 +328,7 @@ func TestService_Audit_DBCleanSuccess(t *testing.T) {
316328
fakeStore := newFakeStore()
317329
fakeStore.GetStatusReturns(0, "", errors.New("db status err"))
318330

319-
svc := newTestService(newTestStoreService(t, fakeStore), nil)
331+
svc := newTestService(t, newTestStoreService(t, fakeStore), nil)
320332
tx := &auditmock.Transaction{}
321333
tx.IDReturns("tx-aud-err")
322334
tx.RequestReturns(token.NewRequest(newTestManagementService(t), token.RequestAnchor("tx-aud-err")))
@@ -331,7 +343,7 @@ func TestService_Audit_NotUnknown(t *testing.T) {
331343
fakeStore := newFakeStore()
332344
fakeStore.GetStatusReturns(dbdriver.Pending, "", nil)
333345

334-
svc := newTestService(newTestStoreService(t, fakeStore), nil)
346+
svc := newTestService(t, newTestStoreService(t, fakeStore), nil)
335347
tx := &auditmock.Transaction{}
336348
tx.IDReturns("tx-aud-not-unknown")
337349
tx.RequestReturns(token.NewRequest(newTestManagementService(t), token.RequestAnchor("tx-aud-not-unknown")))
@@ -342,7 +354,10 @@ func TestService_Audit_NotUnknown(t *testing.T) {
342354
assert.NotNil(t, outputs)
343355
}
344356

345-
func TestService_Audit_TMSProviderIrrelevant(t *testing.T) {
357+
// Audit resolves the TMS through the provider, as Append does, so the request
358+
// cannot influence which wallet service attributes the record: a provider
359+
// error fails the audit.
360+
func TestService_Audit_TMSProviderError(t *testing.T) {
346361
tmsProv := &depmock.TokenManagementServiceProvider{}
347362
tmsProv.TokenManagementServiceReturns(nil, errors.New("tms err"))
348363

@@ -355,10 +370,9 @@ func TestService_Audit_TMSProviderIrrelevant(t *testing.T) {
355370
tx.IDReturns("tx-aud-tms-err")
356371
tx.RequestReturns(token.NewRequest(newTestManagementService(t), token.RequestAnchor("tx-aud-tms-err")))
357372

358-
inputs, outputs, err := svc.Audit(context.Background(), tx)
359-
require.NoError(t, err)
360-
assert.NotNil(t, inputs)
361-
assert.NotNil(t, outputs)
373+
_, _, err := svc.Audit(context.Background(), tx)
374+
require.Error(t, err)
375+
assert.Contains(t, err.Error(), "tms err")
362376
}
363377

364378
// ---------------------------------------------------------------------------
@@ -702,7 +716,7 @@ func TestService_Audit_LocksReleasedOnAuditRecordError(t *testing.T) {
702716
require.NoError(t, err)
703717

704718
storeService := newTestStoreService(t, newFakeStore())
705-
svc := newTestService(storeService, nil)
719+
svc := newTestService(t, storeService, nil)
706720

707721
tx := &auditmock.Transaction{}
708722
tx.IDReturns("tx-audit-record-err")
@@ -729,7 +743,7 @@ func TestService_Audit_LocksReleasedOnAuditRecordError(t *testing.T) {
729743
// Release() frees them, and Release() is idempotent (safe to call multiple times).
730744
func TestService_Audit_LocksAcquiredOnSuccess(t *testing.T) {
731745
storeService := newTestStoreService(t, newFakeStore())
732-
svc := newTestService(storeService, nil)
746+
svc := newTestService(t, storeService, nil)
733747

734748
tx := &auditmock.Transaction{}
735749
tx.IDReturns("tx-audit-success")
@@ -759,7 +773,7 @@ func TestService_Audit_LocksAcquiredOnSuccess(t *testing.T) {
759773
// Release() is always safe regardless of Audit() outcome.
760774
func TestService_Audit_ContextCancellationBeforeLockAcquisition(t *testing.T) {
761775
storeService := newTestStoreService(t, newFakeStore())
762-
svc := newTestService(storeService, nil)
776+
svc := newTestService(t, storeService, nil)
763777

764778
tx := &auditmock.Transaction{}
765779
tx.IDReturns("tx-ctx-cancel")
@@ -790,7 +804,7 @@ func TestService_Audit_ContextCancellationBeforeLockAcquisition(t *testing.T) {
790804
// first Audit() acquires locks, Release() frees them, second Audit() succeeds.
791805
func TestService_Audit_MultipleAuditsSequential(t *testing.T) {
792806
storeService := newTestStoreService(t, newFakeStore())
793-
svc := newTestService(storeService, nil)
807+
svc := newTestService(t, storeService, nil)
794808

795809
ctx := context.Background()
796810

@@ -825,7 +839,7 @@ func TestService_Audit_MultipleAuditsSequential(t *testing.T) {
825839
// multiple times safely without panics (handles error paths, defer, retry logic).
826840
func TestService_Audit_ReleaseIdempotency(t *testing.T) {
827841
storeService := newTestStoreService(t, newFakeStore())
828-
svc := newTestService(storeService, nil)
842+
svc := newTestService(t, storeService, nil)
829843

830844
tx := &auditmock.Transaction{}
831845
tx.IDReturns("tx-release-idempotent")
@@ -857,7 +871,7 @@ func TestService_Audit_ReleaseIdempotency(t *testing.T) {
857871
// prior Audit() (handles defer in error paths where Audit() never ran or failed early).
858872
func TestService_Audit_ReleaseWithoutAudit(t *testing.T) {
859873
storeService := newTestStoreService(t, newFakeStore())
860-
svc := newTestService(storeService, nil)
874+
svc := newTestService(t, storeService, nil)
861875

862876
tx := &auditmock.Transaction{}
863877
tx.IDReturns("tx-no-audit")
@@ -875,7 +889,7 @@ func TestService_Audit_ReleaseWithoutAudit(t *testing.T) {
875889
// defer auditor.Release(ctx, tx) // MUST be after error check
876890
func TestService_Audit_PanicRecoveryReleasesLocks(t *testing.T) {
877891
storeService := newTestStoreService(t, newFakeStore())
878-
svc := newTestService(storeService, nil)
892+
svc := newTestService(t, storeService, nil)
879893

880894
tx := &auditmock.Transaction{}
881895
tx.IDReturns("tx-panic-recovery")
@@ -964,13 +978,16 @@ func newTestServiceWithMockLocker(t *testing.T, mockLocker *mockAuditLocker) *au
964978
storeService, err := auditdb.NewStoreService(fakeStore, auditdb.WithLocker(mockLocker))
965979
require.NoError(t, err)
966980

981+
tmsProv := &depmock.TokenManagementServiceProvider{}
982+
tmsProv.TokenManagementServiceReturns(tmsWithExtensions{newTestManagementService(t)}, nil)
983+
967984
// Create the auditor service with the store that uses our mock locker
968985
return auditor.NewService(
969986
token.TMSID{},
970987
nil, // networkProvider
971988
storeService,
972989
nil, // tokenDB
973-
nil, // tmsProvider
990+
tmsProv,
974991
nil, // finalityTracer
975992
nil, // metricsProvider
976993
nil, // checkService

0 commit comments

Comments
 (0)