@@ -444,3 +444,74 @@ func TestDeserializeActionsErrors(t *testing.T) {
444444 _ , _ , err = ad .DeserializeActions (tr )
445445 require .Error (t , err )
446446}
447+
448+ // TestTransferVerifierCache tests that when multiple inputs have the same owner,
449+ // the verifier is only deserialized once (cache hit)
450+ func TestTransferVerifierCache (t * testing.T ) {
451+ ctx := context .Background ()
452+
453+ // Create a transfer action with 3 inputs, where 2 have the same owner
454+ sameOwner := []byte ("same-owner-identity" )
455+ differentOwner := []byte ("different-owner-identity" )
456+
457+ transferAction := & transfer.Action {
458+ Inputs : []* transfer.ActionInput {
459+ {Token : & token.Token {Owner : sameOwner }}, // Input 0: same owner
460+ {Token : & token.Token {Owner : sameOwner }}, // Input 1: same owner (should hit cache)
461+ {Token : & token.Token {Owner : differentOwner }}, // Input 2: different owner
462+ },
463+ Outputs : []* token.Token {
464+ {Owner : []byte ("output-owner" )},
465+ },
466+ }
467+
468+ // Track deserializer calls
469+ deserializerCallCount := 0
470+ ownerCallsByIdentity := make (map [string ]int )
471+
472+ mockDes := & mock3.Deserializer {}
473+ mockDes .GetOwnerVerifierCalls (func (ctx context.Context , id driver.Identity ) (driver.Verifier , error ) {
474+ deserializerCallCount ++
475+ key := string (id )
476+ ownerCallsByIdentity [key ]++
477+ return & mock3.Verifier {}, nil
478+ })
479+
480+ mockSigProv := & mockSignatureProvider {
481+ HasBeenSignedByFunc : func (ctx context.Context , id driver.Identity , verifier driver.Verifier ) ([]byte , error ) {
482+ return []byte ("signature" ), nil
483+ },
484+ }
485+
486+ validatorCtx := & validator.Context {
487+ Logger : & logging.MockLogger {},
488+ TransferAction : transferAction ,
489+ Deserializer : mockDes ,
490+ SignatureProvider : mockSigProv ,
491+ PP : & v1.PublicParams {},
492+ }
493+
494+ // Execute validation
495+ err := validator .TransferSignatureValidate (ctx , validatorCtx )
496+ require .NoError (t , err )
497+
498+ // Verify results - WITH cache implementation
499+ t .Logf ("Total GetOwnerVerifier calls: %d" , deserializerCallCount )
500+ t .Logf ("Calls for same owner: %d" , ownerCallsByIdentity [string (sameOwner )])
501+ t .Logf ("Calls for different owner: %d" , ownerCallsByIdentity [string (differentOwner )])
502+
503+ // With verifier cache: deserializer is called only once per unique owner
504+ // Expected: 2 calls total (once for sameOwner, once for differentOwner)
505+ require .Equal (t , 2 , deserializerCallCount , "With cache: should call deserializer only for unique owners" )
506+ require .Equal (t , 1 , ownerCallsByIdentity [string (sameOwner )], "With cache: same owner called only once" )
507+ require .Equal (t , 1 , ownerCallsByIdentity [string (differentOwner )], "Different owner called once" )
508+
509+ // Calculate cache effectiveness
510+ uniqueOwners := 2
511+ totalInputs := 3
512+ cacheHits := totalInputs - uniqueOwners
513+ cacheHitRate := float64 (cacheHits ) / float64 (totalInputs ) * 100
514+
515+ t .Logf ("Cache hit rate: %.1f%% (%d cache hits out of %d inputs)" ,
516+ cacheHitRate , cacheHits , totalInputs )
517+ }
0 commit comments