@@ -14,12 +14,15 @@ import (
1414 "github.com/LFDT-Panurus/panurus/token/core/zkatdlog/nogh/v1/benchmark"
1515 "github.com/LFDT-Panurus/panurus/token/core/zkatdlog/nogh/v1/crypto/rp"
1616 testing2 "github.com/LFDT-Panurus/panurus/token/core/zkatdlog/nogh/v1/testutils"
17+ "github.com/LFDT-Panurus/panurus/token/core/zkatdlog/nogh/v1/validator"
1718 "github.com/LFDT-Panurus/panurus/token/driver"
19+ "github.com/LFDT-Panurus/panurus/token/driver/mock"
1820 benchmark2 "github.com/LFDT-Panurus/panurus/token/services/benchmark"
1921 "github.com/LFDT-Panurus/panurus/token/services/identity"
2022 "github.com/LFDT-Panurus/panurus/token/services/identity/idemix"
2123 "github.com/LFDT-Panurus/panurus/token/services/identity/idemixnym"
2224 "github.com/hyperledger-labs/fabric-smart-client/node/start/profile"
25+ "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
2326 "github.com/stretchr/testify/require"
2427)
2528
@@ -106,6 +109,83 @@ func TestValidator(t *testing.T) {
106109 }
107110}
108111
112+ // TestVerifierCache verifies that VerifierCache deserializes each distinct owner
113+ // at most once (issue #2074): repeated lookups of the same owner reuse the
114+ // cached verifier, distinct owners are deserialized independently, and a
115+ // deserialization error is propagated without being cached so a later lookup
116+ // can retry.
117+ func TestVerifierCache (t * testing.T ) {
118+ ctx := context .Background ()
119+ ownerA := driver .Identity ("owner-A" )
120+ ownerB := driver .Identity ("owner-B" )
121+
122+ t .Run ("same owner is deserialized only once" , func (t * testing.T ) {
123+ des := & mock.Deserializer {}
124+ verifier := & mock.Verifier {}
125+ des .GetOwnerVerifierReturns (verifier , nil )
126+
127+ cache := validator .NewVerifierCache (des )
128+
129+ first , err := cache .Get (ctx , ownerA )
130+ require .NoError (t , err )
131+ require .Same (t , verifier , first )
132+
133+ second , err := cache .Get (ctx , ownerA )
134+ require .NoError (t , err )
135+ require .Same (t , verifier , second )
136+
137+ require .Equal (t , 1 , des .GetOwnerVerifierCallCount (), "owner should be deserialized exactly once" )
138+ })
139+
140+ t .Run ("distinct owners are deserialized separately" , func (t * testing.T ) {
141+ des := & mock.Deserializer {}
142+ verifiers := map [string ]driver.Verifier {
143+ string (ownerA ): & mock.Verifier {},
144+ string (ownerB ): & mock.Verifier {},
145+ }
146+ des .GetOwnerVerifierStub = func (_ context.Context , id driver.Identity ) (driver.Verifier , error ) {
147+ return verifiers [string (id )], nil
148+ }
149+
150+ cache := validator .NewVerifierCache (des )
151+
152+ gotA , err := cache .Get (ctx , ownerA )
153+ require .NoError (t , err )
154+ require .Same (t , verifiers [string (ownerA )], gotA )
155+
156+ gotB , err := cache .Get (ctx , ownerB )
157+ require .NoError (t , err )
158+ require .Same (t , verifiers [string (ownerB )], gotB )
159+
160+ // re-fetch the first owner: it is served from the cache, not re-deserialized.
161+ gotAAgain , err := cache .Get (ctx , ownerA )
162+ require .NoError (t , err )
163+ require .Same (t , gotA , gotAAgain )
164+
165+ require .Equal (t , 2 , des .GetOwnerVerifierCallCount (), "each distinct owner should be deserialized once" )
166+ })
167+
168+ t .Run ("deserialization error propagates and is not cached" , func (t * testing.T ) {
169+ des := & mock.Deserializer {}
170+ boom := errors .New ("boom" )
171+ verifier := & mock.Verifier {}
172+ des .GetOwnerVerifierReturnsOnCall (0 , nil , boom )
173+ des .GetOwnerVerifierReturnsOnCall (1 , verifier , nil )
174+
175+ cache := validator .NewVerifierCache (des )
176+
177+ _ , err := cache .Get (ctx , ownerA )
178+ require .ErrorIs (t , err , boom )
179+
180+ // The failed lookup was not cached, so a subsequent call retries and succeeds.
181+ got , err := cache .Get (ctx , ownerA )
182+ require .NoError (t , err )
183+ require .Same (t , verifier , got )
184+
185+ require .Equal (t , 2 , des .GetOwnerVerifierCallCount (), "a failed lookup must not be cached" )
186+ })
187+ }
188+
109189func BenchmarkValidatorTransfer (b * testing.B ) {
110190 pp , err := profile .New (profile .WithAll (), profile .WithPath ("./profile" ))
111191 require .NoError (b , err )
0 commit comments