From 589686840ac397488ed04006cebd6a58d353fbd6 Mon Sep 17 00:00:00 2001 From: Oleg Lomaka Date: Fri, 17 May 2024 04:34:23 -0400 Subject: [PATCH] Cache issuer state for 5 minutes. Cache credential revocation proofs. --- badger_cache_engine_test.go | 4 +- cache_test.go | 14 +- caching_rev_status_resolver.go | 310 ++++++++++++++++++++++++++++ caching_rev_status_resolver_test.go | 299 +++++++++++++++++++++++++++ go.sum | 2 - inputs_sig.go | 15 +- inputs_sig_test.go | 29 +-- mt_storage_test.go | 2 +- onchain_adapter_test.go | 2 +- 9 files changed, 631 insertions(+), 46 deletions(-) create mode 100644 caching_rev_status_resolver.go create mode 100644 caching_rev_status_resolver_test.go diff --git a/badger_cache_engine_test.go b/badger_cache_engine_test.go index 1606d71..8a7fe86 100644 --- a/badger_cache_engine_test.go +++ b/badger_cache_engine_test.go @@ -10,8 +10,8 @@ import ( ) func TestGetPubRemoteDocument(t *testing.T) { - defer mockBadgerLog(t)() - flushCacheDB() + mockBadgerLog(t) + flushCacheDB(t) cacheEng, err := newBadgerCacheEngine() require.NoError(t, err) diff --git a/cache_test.go b/cache_test.go index 65bda30..96ef8ec 100644 --- a/cache_test.go +++ b/cache_test.go @@ -67,12 +67,12 @@ func (t *testBadgerLogger) Debugf(s string, i ...interface{}) { t.t.Logf("[BADGER DEBUG]"+s, i...) } -func mockBadgerLog(t testing.TB) func() { +func mockBadgerLog(t testing.TB) { orig := badgerLogger badgerLogger = &testBadgerLogger{t, true} - return func() { + t.Cleanup(func() { badgerLogger = orig - } + }) } func BenchmarkBadgerWithOpening(b *testing.B) { @@ -99,7 +99,7 @@ func BenchmarkBadgerWithoutOpening(b *testing.B) { } func TestBadger(t *testing.T) { - defer mockBadgerLog(t)() + mockBadgerLog(t) dbPath, err := getBadgerPath() require.NoError(t, err) @@ -114,7 +114,7 @@ func TestBadger(t *testing.T) { } func TestGetCacheDB(t *testing.T) { - defer mockBadgerLog(t)() + mockBadgerLog(t) db1, close1, err := getCacheDB() require.NoError(t, err) db2, close2, err := getCacheDB() @@ -179,7 +179,7 @@ func set(db *badger.DB, key string, value string) { } func TestCleanCache(t *testing.T) { - defer mockBadgerLog(t)() + mockBadgerLog(t) db1, close1, err := getCacheDB() require.NoError(t, err) @@ -214,7 +214,7 @@ func TestCleanCache(t *testing.T) { } func TestMultipleCleanup(t *testing.T) { - defer mockBadgerLog(t)() + mockBadgerLog(t) _, close1, err := getCacheDB() require.NoError(t, err) diff --git a/caching_rev_status_resolver.go b/caching_rev_status_resolver.go new file mode 100644 index 0000000..62d9558 --- /dev/null +++ b/caching_rev_status_resolver.go @@ -0,0 +1,310 @@ +package c_polygonid + +import ( + "context" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "slices" + "sync" + "time" + + "github.com/dgraph-io/badger/v4" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/go-merkletree-sql/v2" + "github.com/iden3/go-schema-processor/v2/verifiable" +) + +var revStatusCacheMutex sync.RWMutex + +const issuerStateTTL = 5 * time.Minute + +func toHash(in *string) (merkletree.Hash, error) { + if in == nil || *in == "" { + return merkletree.HashZero, nil + } + h, err := merkletree.NewHashFromHex(*in) + if err != nil { + return merkletree.Hash{}, err + } + return *h, nil +} + +func cacheKeyIssuerState(issuerDID *w3c.DID) string { + return fmt.Sprintf("issuer-state-%v", issuerDID.String()) +} + +func cacheKeyRevStatus(revTreeRoot merkletree.Hash, + credStatus verifiable.CredentialStatus) string { + + return fmt.Sprintf("rev-status-%s-%s-%s-%d", hex.EncodeToString(revTreeRoot[:]), + credStatus.ID, credStatus.Type, credStatus.RevocationNonce) +} + +func cacheKeyRevTreeEntries(revTreeRoot merkletree.Hash) string { + return fmt.Sprintf("rev-tree-entries-%s", + hex.EncodeToString(revTreeRoot[:])) +} + +type treeStateEntry struct { + TreeState verifiable.TreeState `json:"treeState"` + CreatedAt time.Time `json:"createdAt"` +} + +func putIssuerStateToCache(db *badger.DB, issuerDID *w3c.DID, + issuerState verifiable.TreeState) error { + + key := []byte(cacheKeyIssuerState(issuerDID)) + value, err := json.Marshal(treeStateEntry{ + TreeState: issuerState, + CreatedAt: time.Now().UTC(), + }) + if err != nil { + return err + } + return db.Update(func(txn *badger.Txn) error { + return txn.Set(key, value) + }) +} + +// may return badger.ErrKeyNotFound if state is not found in cache +func getIssuerStateFromCache(db *badger.DB, + issuerDID *w3c.DID) (verifiable.TreeState, time.Time, error) { + + var stateEntry treeStateEntry + err := db.View(func(txn *badger.Txn) error { + itm, err2 := txn.Get([]byte(cacheKeyIssuerState(issuerDID))) + if err2 != nil { + return err2 + } + return itm.Value(func(val []byte) error { + return json.Unmarshal(val, &stateEntry) + }) + }) + if err != nil { + return verifiable.TreeState{}, time.Time{}, err + } + + return stateEntry.TreeState, stateEntry.CreatedAt, nil +} + +type registryBuilder func(ctx context.Context, cfg PerChainConfig) ( + *verifiable.CredentialStatusResolverRegistry, func(), error) + +func resolveRevStatus(ctx context.Context, chainCfg PerChainConfig, + issuerDID *w3c.DID, credStatus verifiable.CredentialStatus, + regBuilder registryBuilder) (verifiable.RevocationStatus, error) { + + if regBuilder == nil { + return verifiable.RevocationStatus{}, + errors.New("registry builder is null") + } + resolversRegistry, registryCleanupFn, err := regBuilder(ctx, + chainCfg) + if err != nil { + return verifiable.RevocationStatus{}, err + } + defer registryCleanupFn() + + resolver, err := resolversRegistry.Get(credStatus.Type) + if err != nil { + return verifiable.RevocationStatus{}, err + } + + ctx = verifiable.WithIssuerDID(ctx, issuerDID) + return resolver.Resolve(ctx, credStatus) +} + +func resolveRevStatusAndCache(ctx context.Context, db *badger.DB, + chainCfg PerChainConfig, issuerDID *w3c.DID, + credStatus verifiable.CredentialStatus, + regBuilder registryBuilder) (verifiable.RevocationStatus, error) { + + revStatus, err := resolveRevStatus(ctx, chainCfg, issuerDID, credStatus, + regBuilder) + if err != nil { + return verifiable.RevocationStatus{}, err + } + + revStatusCacheMutex.Lock() + defer revStatusCacheMutex.Unlock() + + var oldState verifiable.TreeState + var hasOldState bool + var oldRevTreeRoot merkletree.Hash + oldState, _, err = getIssuerStateFromCache(db, issuerDID) + if errors.Is(err, badger.ErrKeyNotFound) { + hasOldState = false + } else if err != nil { + return verifiable.RevocationStatus{}, err + } else { + hasOldState = true + oldRevTreeRoot, err = toHash(oldState.RevocationTreeRoot) + if err != nil { + return verifiable.RevocationStatus{}, err + } + } + + var newRevTreeRoot merkletree.Hash + newRevTreeRoot, err = toHash(revStatus.Issuer.RevocationTreeRoot) + + if hasOldState && newRevTreeRoot != oldRevTreeRoot { + err = removeExpiredRevStatusFromCache(db, oldRevTreeRoot) + if err != nil { + return verifiable.RevocationStatus{}, err + } + } + + err = putIssuerStateToCache(db, issuerDID, revStatus.Issuer) + if err != nil { + return verifiable.RevocationStatus{}, err + } + + err = putRevProofToCache(db, newRevTreeRoot, credStatus, revStatus.MTP) + if err != nil { + return verifiable.RevocationStatus{}, err + } + + return revStatus, nil +} + +func removeExpiredRevStatusFromCache(db *badger.DB, + revTreeRoot merkletree.Hash) error { + + treeEntriesKey := cacheKeyRevTreeEntries(revTreeRoot) + return db.Update(func(txn *badger.Txn) error { + ent, err := txn.Get([]byte(treeEntriesKey)) + if errors.Is(err, badger.ErrKeyNotFound) { + return nil + } else if err != nil { + return err + } + + var treeProofs []string + err = ent.Value(func(val []byte) error { + return json.Unmarshal(val, &treeProofs) + }) + if err != nil { + return err + } + for _, k := range treeProofs { + err = txn.Delete([]byte(k)) + if err != nil { + return err + } + } + return txn.Delete([]byte(treeEntriesKey)) + }) +} + +type treeState struct { + state merkletree.Hash + rootOfRoots merkletree.Hash + claimsTreeRoot merkletree.Hash + revocationTreeRoot merkletree.Hash +} + +func putRevProofToCache(db *badger.DB, revTreeRoot merkletree.Hash, + credStatus verifiable.CredentialStatus, revProof merkletree.Proof) error { + + treeEntriesKey := cacheKeyRevTreeEntries(revTreeRoot) + revStatusKey := cacheKeyRevStatus(revTreeRoot, credStatus) + + return db.Update(func(txn *badger.Txn) error { + var treeProofs []string + ent, err := txn.Get([]byte(treeEntriesKey)) + if errors.Is(err, badger.ErrKeyNotFound) { + } else if err != nil { + return err + } else { + err = ent.Value(func(val []byte) error { + return json.Unmarshal(val, &treeProofs) + }) + if err != nil { + return err + } + } + + var val []byte + if !slices.Contains(treeProofs, revStatusKey) { + treeProofs = append(treeProofs, revStatusKey) + val, err = json.Marshal(treeProofs) + if err != nil { + return err + } + err = txn.Set([]byte(treeEntriesKey), val) + if err != nil { + return err + } + } + + val, err = json.Marshal(revProof) + if err != nil { + return err + } + return txn.Set([]byte(revStatusKey), val) + }) +} + +func getRevProofFromCache(db *badger.DB, revTreeRoot merkletree.Hash, + credStatus verifiable.CredentialStatus) (merkletree.Proof, error) { + + var revProof merkletree.Proof + err := db.View(func(txn *badger.Txn) error { + itm, err2 := txn.Get([]byte(cacheKeyRevStatus(revTreeRoot, credStatus))) + if err2 != nil { + return err2 + } + return itm.Value(func(val []byte) error { + return json.Unmarshal(val, &revProof) + }) + }) + if err != nil { + return merkletree.Proof{}, err + } + return revProof, nil +} + +func cachedResolve(ctx context.Context, chainCfg PerChainConfig, + issuerDID *w3c.DID, credStatus verifiable.CredentialStatus, + regBuilder registryBuilder) (verifiable.RevocationStatus, error) { + + cache, cacheCleanup, err := getCacheDB() + if err != nil { + // Cache engine is not available, so resolve without cache + return resolveRevStatus(ctx, chainCfg, issuerDID, credStatus, + regBuilder) + } + defer cacheCleanup() + + if issuerDID == nil { + return verifiable.RevocationStatus{}, errors.New("issuer DID is null") + } + + var revStatus verifiable.RevocationStatus + var createdAt time.Time + revStatus.Issuer, createdAt, err = getIssuerStateFromCache(cache, issuerDID) + if errors.Is(err, badger.ErrKeyNotFound) || (err == nil && createdAt.Before(time.Now().Add(-issuerStateTTL))) { + return resolveRevStatusAndCache(ctx, cache, chainCfg, issuerDID, + credStatus, regBuilder) + } else if err != nil { + return verifiable.RevocationStatus{}, err + } + + var revTreeRoot merkletree.Hash + revTreeRoot, err = toHash(revStatus.Issuer.RevocationTreeRoot) + if err != nil { + return verifiable.RevocationStatus{}, err + } + + revStatus.MTP, err = getRevProofFromCache(cache, revTreeRoot, credStatus) + if errors.Is(err, badger.ErrKeyNotFound) { + return resolveRevStatusAndCache(ctx, cache, chainCfg, issuerDID, + credStatus, regBuilder) + } else if err != nil { + return verifiable.RevocationStatus{}, err + } + + return revStatus, nil +} diff --git a/caching_rev_status_resolver_test.go b/caching_rev_status_resolver_test.go new file mode 100644 index 0000000..bcfad69 --- /dev/null +++ b/caching_rev_status_resolver_test.go @@ -0,0 +1,299 @@ +package c_polygonid + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math/big" + "strconv" + "testing" + "time" + + "github.com/dgraph-io/badger/v4" + core "github.com/iden3/go-iden3-core/v2" + "github.com/iden3/go-merkletree-sql/v2" + "github.com/iden3/go-merkletree-sql/v2/db/memory" + "github.com/iden3/go-schema-processor/v2/verifiable" + "github.com/stretchr/testify/require" +) + +const mockCredStatusType = verifiable.CredentialStatusType("mockCredStatus") + +type mockStatusResolver struct { + states map[string]verifiable.TreeState + trees map[merkletree.Hash]*merkletree.MerkleTree +} + +func (m *mockStatusResolver) Resolve(ctx context.Context, + credStatus verifiable.CredentialStatus) (verifiable.RevocationStatus, error) { + + var revStatus verifiable.RevocationStatus + + issuerDID := verifiable.GetIssuerDID(ctx) + if issuerDID == nil { + return revStatus, errors.New("issuer DID not found in context") + } + + var ok bool + revStatus.Issuer, ok = m.states[issuerDID.String()] + if !ok { + return revStatus, errors.New("issuer state not found") + } + + h, err := toHash(revStatus.Issuer.RevocationTreeRoot) + if err != nil { + return verifiable.RevocationStatus{}, err + } + + revTree, ok := m.trees[h] + if !ok { + return verifiable.RevocationStatus{}, + errors.New("revocation tree not found") + } + + var proof *merkletree.Proof + proof, _, err = revTree.GenerateProof(ctx, new(big.Int).SetUint64(credStatus.RevocationNonce), nil) + if err != nil { + return revStatus, err + } + revStatus.MTP = *proof + + return revStatus, nil +} + +func regBuilder(states map[string]verifiable.TreeState, + trees map[merkletree.Hash]*merkletree.MerkleTree) registryBuilder { + + return func(ctx context.Context, + cfg PerChainConfig) (*verifiable.CredentialStatusResolverRegistry, func(), error) { + + var registry = &verifiable.CredentialStatusResolverRegistry{} + + registry.Register(mockCredStatusType, &mockStatusResolver{states, trees}) + cleanupFn := func() { + registry.Delete(mockCredStatusType) + } + + return registry, cleanupFn, nil + } +} + +func TestCachedResolve(t *testing.T) { + mockBadgerLog(t) + flushCacheDB(t) + + ctx := context.Background() + _, err := cachedResolve(ctx, PerChainConfig{}, nil, + verifiable.CredentialStatus{}, nil) + require.EqualError(t, err, "issuer DID is null") + + typ, err := core.BuildDIDType(core.DIDMethodPolygonID, core.ZkEVM, + core.Main) + require.NoError(t, err) + + issuerDID, err := core.NewDIDFromIdenState(typ, big.NewInt(1)) + require.NoError(t, err) + + _, err = cachedResolve(ctx, PerChainConfig{}, issuerDID, + verifiable.CredentialStatus{}, nil) + require.EqualError(t, err, "registry builder is null") + + credStatus := verifiable.CredentialStatus{ + ID: "id1", + Type: mockCredStatusType, + RevocationNonce: 100500, + StatusIssuer: nil, + } + + states := map[string]verifiable.TreeState{} + trees := map[merkletree.Hash]*merkletree.MerkleTree{} + + revStatus, err := cachedResolve(ctx, PerChainConfig{}, issuerDID, + credStatus, regBuilder(states, trees)) + require.EqualError(t, err, "issuer state not found") + + revTree, err := merkletree.NewMerkleTree(ctx, memory.NewMemoryStorage(), 40) + require.NoError(t, err) + state0, err := merkletree.HashElems(merkletree.HashZero.BigInt(), + revTree.Root().BigInt(), merkletree.HashZero.BigInt()) + require.NoError(t, err) + issuerState := verifiable.TreeState{ + State: &[]string{state0.Hex()}[0], + RootOfRoots: nil, + ClaimsTreeRoot: nil, + RevocationTreeRoot: &[]string{revTree.Root().Hex()}[0], + } + states[issuerDID.String()] = issuerState + trees[*revTree.Root()] = revTree + revStatus, err = cachedResolve(ctx, PerChainConfig{}, issuerDID, + credStatus, regBuilder(states, trees)) + require.NoError(t, err) + wantRevStatus0 := `{ +"issuer":{ + "state":"aa99a51bb36dee7caec596ecec4e86e28ff07a0aafb6cf1ddceacc7dd288c10b", + "revocationTreeRoot":"0000000000000000000000000000000000000000000000000000000000000000" +}, +"mtp":{"existence":false,"siblings":[]}}` + require.JSONEq(t, wantRevStatus0, toJson(revStatus)) + + treeCacheKey0 := `rev-tree-entries-` + merkletree.HashZero.Hex() + proofCacheKey0 := `rev-status-0000000000000000000000000000000000000000000000000000000000000000-id1-mockCredStatus-100500` + + // check state in cache + t.Run("check state in cache", func(t *testing.T) { + cacheDB := getTestCacheDB(t) + + wantTreeEntries := toJson([]string{proofCacheKey0}) + assertCacheEqual(t, cacheDB, treeCacheKey0, wantTreeEntries) + assertCacheEqual(t, cacheDB, proofCacheKey0, `{"existence":false,"siblings":[]}`) + + cachedState, _, err2 := getIssuerStateFromCache(cacheDB, issuerDID) + require.NoError(t, err2) + require.Equal(t, issuerState, cachedState) + }) + + // put nonce into revTree + err = revTree.Add(ctx, new(big.Int).SetUint64(credStatus.RevocationNonce), + big.NewInt(0)) + require.NoError(t, err) + trees[*revTree.Root()] = revTree + treeCacheKey1 := `rev-tree-entries-` + revTree.Root().Hex() + + // calculate new issuer state + issuerState.RevocationTreeRoot = &[]string{revTree.Root().Hex()}[0] + state1, err := merkletree.HashElems(merkletree.HashZero.BigInt(), + revTree.Root().BigInt(), merkletree.HashZero.BigInt()) + require.NoError(t, err) + issuerState.State = &[]string{state1.Hex()}[0] + states[issuerDID.String()] = issuerState + + // check state in cache + t.Run("got old state from cache", func(t *testing.T) { + revStatus, err = cachedResolve(ctx, PerChainConfig{}, issuerDID, + credStatus, regBuilder(states, trees)) + require.NoError(t, err) + require.JSONEq(t, wantRevStatus0, toJson(revStatus)) + }) + + cacheKeyIssuer := cacheKeyIssuerState(issuerDID) + // expire old state + t.Run("expire old state", func(t *testing.T) { + cacheDB := getTestCacheDB(t) + expireTreeStateCache(t, cacheDB, cacheKeyIssuer) + }) + + wantRevStatus1 := fmt.Sprintf(`{ + "issuer":{ + "state":"%v", + "revocationTreeRoot":"%v" + }, + "mtp":{"existence":true,"siblings":[]}}`, + *issuerState.State, *issuerState.RevocationTreeRoot) + + t.Run("got new rev status", func(t *testing.T) { + revStatus, err = cachedResolve(ctx, PerChainConfig{}, issuerDID, + credStatus, regBuilder(states, trees)) + require.NoError(t, err) + require.JSONEq(t, wantRevStatus1, toJson(revStatus)) + }) + + t.Run("old state cleaned up", func(t *testing.T) { + cacheDB := getTestCacheDB(t) + assertCacheDoesNotExists(t, cacheDB, treeCacheKey0) + assertCacheDoesNotExists(t, cacheDB, proofCacheKey0) + }) + + wantRevStatus2 := fmt.Sprintf(`{ + "issuer":{ + "state":"%v", + "revocationTreeRoot":"%v" + }, + "mtp":{ + "existence":false, + "siblings":[], + "node_aux":{"key":"100500","value":"0"} + } +}`, + *issuerState.State, *issuerState.RevocationTreeRoot) + + t.Run("unknown rev nonce", func(t *testing.T) { + credStatus2 := credStatus + credStatus2.RevocationNonce += 1 + revStatus, err = cachedResolve(ctx, PerChainConfig{}, issuerDID, + credStatus2, regBuilder(states, trees)) + require.NoError(t, err) + require.JSONEq(t, wantRevStatus2, toJson(revStatus)) + + cacheDB := getTestCacheDB(t) + + revTreeRoot := revTree.Root().Hex() + proof1Key := `rev-status-` + revTreeRoot + `-id1-mockCredStatus-` + strconv.Itoa(int(credStatus.RevocationNonce)) + proof2Key := `rev-status-` + revTreeRoot + `-id1-mockCredStatus-` + strconv.Itoa(int(credStatus2.RevocationNonce)) + wantTreeEntries := toJson([]string{proof1Key, proof2Key}) + assertCacheEqual(t, cacheDB, treeCacheKey1, wantTreeEntries) + + assertCacheEqual(t, cacheDB, proof1Key, `{"existence":true,"siblings":[]}`) + assertCacheEqual(t, cacheDB, proof2Key, `{"existence":false,"siblings":[],"node_aux":{"key":"100500","value":"0"}}`) + }) +} + +func expireTreeStateCache(t testing.TB, cacheDB *badger.DB, key string) { + err := cacheDB.Update(func(txn *badger.Txn) error { + entry, err := txn.Get([]byte(key)) + require.NoError(t, err) + entryVal, err := entry.ValueCopy(nil) + require.NoError(t, err) + var stateEntry treeStateEntry + require.NoError(t, json.Unmarshal(entryVal, &stateEntry)) + stateEntry.CreatedAt = time.Now().Add(-issuerStateTTL - time.Second) + entryVal, err = json.Marshal(stateEntry) + require.NoError(t, txn.Set([]byte(key), entryVal)) + return nil + }) + require.NoError(t, err) +} + +func assertCacheDoesNotExists(t testing.TB, cacheDB *badger.DB, key string) { + err := cacheDB.Update(func(txn *badger.Txn) error { + _, err := txn.Get([]byte(key)) + require.ErrorIs(t, err, badger.ErrKeyNotFound) + return nil + }) + require.NoError(t, err) +} + +func assertCacheEqual(t testing.TB, cacheDB *badger.DB, key string, want string) { + err := cacheDB.View(func(txn *badger.Txn) error { + entry, err := txn.Get([]byte(key)) + require.NoError(t, err) + + valueBytes, err := entry.ValueCopy(nil) + require.NoError(t, err) + + require.JSONEq(t, want, string(valueBytes), string(valueBytes)) + + return nil + }) + require.NoError(t, err) +} + +func toJson(obj any) string { + bs, err := json.Marshal(obj) + if err != nil { + panic(err) + } + return string(bs) +} + +func getTestCacheDB(t testing.TB) *badger.DB { + db, cleanup, err := getCacheDB() + require.NoError(t, err) + t.Cleanup(cleanup) + return db +} + +func TestCacheKeyRevTreeEntries(t *testing.T) { + key := cacheKeyRevTreeEntries(merkletree.HashZero) + require.Equal(t, "rev-tree-entries-0000000000000000000000000000000000000000000000000000000000000000", key) +} diff --git a/go.sum b/go.sum index b967c2e..8283757 100644 --- a/go.sum +++ b/go.sum @@ -168,8 +168,6 @@ github.com/iden3/go-iden3-crypto v0.0.16 h1:zN867xiz6HgErXVIV/6WyteGcOukE9gybYTo github.com/iden3/go-iden3-crypto v0.0.16/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= github.com/iden3/go-merkletree-sql/v2 v2.0.6 h1:vsVDImnvnHf7Ggr45ptFOXJyWNA/8IwVQO1jzRLUlY8= github.com/iden3/go-merkletree-sql/v2 v2.0.6/go.mod h1:kRhHKYpui5DUsry5RpveP6IC4XMe6iApdV9VChRYuEk= -github.com/iden3/go-onchain-credential-adapter v0.0.0-20240223120548-5d8d1d28c6d1 h1:xpKhrLYi0s6ocUCrFwkjG0YQjndDGMW7/cgU9KjLyig= -github.com/iden3/go-onchain-credential-adapter v0.0.0-20240223120548-5d8d1d28c6d1/go.mod h1:1RVClY0JdWmzgx3ZBvy0Z2bNyPg2iz+0R9Ryl2Z3s64= github.com/iden3/go-onchain-credential-adapter v0.0.0-20240425110009-4004ac8da50c h1:F0r62i4c30JwQFw3YY1ZkqrJiCWThfq14ju3u85uuGA= github.com/iden3/go-onchain-credential-adapter v0.0.0-20240425110009-4004ac8da50c/go.mod h1:MmdE5bm8lwZxPTDb7iQb9bjtarN1+jPHmNnl4fFxqj4= github.com/iden3/go-schema-processor/v2 v2.3.4 h1:zWE9NrY7dm9Il8c0u3XcJfenptmxbvIaNDOqkq5uQ0A= diff --git a/inputs_sig.go b/inputs_sig.go index 6df06b6..b86a269 100644 --- a/inputs_sig.go +++ b/inputs_sig.go @@ -314,24 +314,13 @@ func buildAndValidateCredentialStatus(ctx context.Context, cfg EnvConfig, credStatus jsonObj, issuerDID *w3c.DID, skipClaimRevocationCheck bool) (circuits.MTProof, error) { - resolversRegistry, registryCleanupFn, err := getResolversRegistry(ctx, cfg.ChainConfigs) - if err != nil { - return circuits.MTProof{}, err - } - defer registryCleanupFn() - credStatus2, err := credStatusFromJsonObj(credStatus) if err != nil { return circuits.MTProof{}, err } - resolver, err := resolversRegistry.Get(credStatus2.Type) - if err != nil { - return circuits.MTProof{}, err - } - - ctx = verifiable.WithIssuerDID(ctx, issuerDID) - revStatus, err := resolver.Resolve(ctx, credStatus2) + revStatus, err := cachedResolve(ctx, cfg.ChainConfigs, issuerDID, + credStatus2, getResolversRegistry) if err != nil { return circuits.MTProof{}, fmt.Errorf("error resolving revocation status: %w", err) diff --git a/inputs_sig_test.go b/inputs_sig_test.go index a7ab740..8eb3098 100644 --- a/inputs_sig_test.go +++ b/inputs_sig_test.go @@ -81,7 +81,7 @@ func removeIdFromEthBody(body []byte) []byte { } func TestPrepareInputs(t *testing.T) { - defer mockBadgerLog(t)() + mockBadgerLog(t) type PrepareInputsFn func( ctx context.Context, cfg EnvConfig, in []byte) ( @@ -1047,16 +1047,8 @@ func stringFromJsonObj(obj map[string]any, key string) string { return "" } -func flushCacheDB() { - db, cleanup, err := getCacheDB() - if err != nil { - panic(err) - } - defer cleanup() - err = db.DropAll() - if err != nil { - panic(err) - } +func flushCacheDB(t testing.TB) { + require.NoError(t, getTestCacheDB(t).DropAll()) } type countingDocumentLoader struct { @@ -1086,8 +1078,8 @@ func (c *countingDocumentLoader) reset() { } func TestMerklizeCred(t *testing.T) { - defer mockBadgerLog(t)() - flushCacheDB() + mockBadgerLog(t) + flushCacheDB(t) defer httpmock.MockHTTPClient(t, map[string]string{ "https://schema.iden3.io/core/jsonld/iden3proofs.jsonld": "testdata/httpresp_iden3proofs.jsonld", @@ -1118,7 +1110,7 @@ func TestMerklizeCred(t *testing.T) { require.Equal(t, wantRoot, mz.Root().BigInt().String()) require.Equal(t, 0, documentLoader.counter()) - flushCacheDB() + flushCacheDB(t) } func vcCredChecksum(in []byte) []byte { @@ -1147,9 +1139,8 @@ func vcCredChecksum(in []byte) []byte { } func TestPreCacheVC(t *testing.T) { - defer mockBadgerLog(t)() - - flushCacheDB() + mockBadgerLog(t) + flushCacheDB(t) defer httpmock.MockHTTPClient(t, map[string]string{ "https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/iden3credential-v2.json-ld": "testdata/httpresp_iden3credential_v2.json", @@ -1161,9 +1152,7 @@ func TestPreCacheVC(t *testing.T) { err := PreCacheVC(context.Background(), cfg, in) require.NoError(t, err) - db, closeCache, err := getCacheDB() - require.NoError(t, err) - t.Cleanup(closeCache) + db := getTestCacheDB(t) cacheKey := vcCredChecksum(in) mz, _, err := diff --git a/mt_storage_test.go b/mt_storage_test.go index 07db176..a3f118b 100644 --- a/mt_storage_test.go +++ b/mt_storage_test.go @@ -25,7 +25,7 @@ func makeW3CCred(jsonDoc string) verifiable.W3CCredential { var w3cCredDoc = `{"id":"https://dd25-62-87-103-47.ngrok-free.app/api/v1/identities/did:polygonid:polygon:mumbai:2qKc2ns18nV6uDSfaR1RVd7zF1Nm9vfeNZuvuEXQ3X/claims/f4263c2a-53ae-11ee-a53c-3ec1cb517438","@context":["https://www.w3.org/2018/credentials/v1","https://schema.iden3.io/core/jsonld/iden3proofs.jsonld","https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v3.json-ld"],"type":["VerifiableCredential","KYCAgeCredential"],"expirationDate":"2361-03-21T21:14:48+02:00","issuanceDate":"2023-09-15T13:02:18.062527+03:00","credentialSubject":{"birthday":19960424,"documentType":2,"id":"did:polygonid:polygon:mumbai:2qFgtCjKV3cDjWJapSLMZGfzw7CrodjCcQzLQABByv","type":"KYCAgeCredential"},"credentialStatus":{"id":"did:polygonid:polygon:mumbai:2qKc2ns18nV6uDSfaR1RVd7zF1Nm9vfeNZuvuEXQ3X/credentialStatus?revocationNonce=651783518\u0026contractAddress=80001:0x49b84b9Dd137de488924b18299De8bf46fD11469","revocationNonce":651783518,"type":"Iden3OnchainSparseMerkleTreeProof2023"},"issuer":"did:polygonid:polygon:mumbai:2qKc2ns18nV6uDSfaR1RVd7zF1Nm9vfeNZuvuEXQ3X","credentialSchema":{"id":"https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json","type":"JsonSchema2023"},"proof":[{"type":"Iden3SparseMerkleTreeProof","issuerData":{"id":"did:polygonid:polygon:mumbai:2qKc2ns18nV6uDSfaR1RVd7zF1Nm9vfeNZuvuEXQ3X","state":{"txId":"0x824d08f68e6c5f8ed2fd1e1a7719c3027e5474c27524f33656796056b360237b","blockTimestamp":1694772169,"blockNumber":40137103,"rootOfRoots":"f3b209de35e59081d119334c9aba3b87474f28971b61dca7aeadff5a14e64524","claimsTreeRoot":"5bf08eb140df36ca00d1ca24eb2dd0f1b4b117e92904279020c8bf51e590f920","revocationTreeRoot":"0000000000000000000000000000000000000000000000000000000000000000","value":"5a166f339f23fb53ae7433909eaf7eee27431f4e3ac038c329416e38a48a5818"}},"coreClaim":"c9b2370371b7fa8b3dab2a5ba81b68382a000000000000000000000000000000021246f30c37a622bfbba26de552d3c9d71d189a58d8c7520c24feaf0b8b0d00b914ea81a5d3a7c2655cc5cc79787cf9b2c10a24cd236fa627252b06b2e7ca1b00000000000000000000000000000000000000000000000000000000000000005e6dd92600000000281cdcdf0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","mtp":{"existence":true,"siblings":["10474921236147713942078552079582531417664737388924733946611980668334294816825"]}}]}` func TestInMemoryStorage_MarshalBinary(t *testing.T) { - defer mockBadgerLog(t)() + mockBadgerLog(t) defer httpmock.MockHTTPClient(t, map[string]string{ "https://schema.iden3.io/core/jsonld/iden3proofs.jsonld": "testdata/httpresp_iden3proofs.jsonld", diff --git a/onchain_adapter_test.go b/onchain_adapter_test.go index e999b72..92b240e 100644 --- a/onchain_adapter_test.go +++ b/onchain_adapter_test.go @@ -12,7 +12,7 @@ import ( ) func TestW3CCredentialFromOnchainHex(t *testing.T) { - defer mockBadgerLog(t)() + mockBadgerLog(t) doTest := func(t testing.TB, inFile, wantOutFile string, cfg EnvConfig) { ctx := context.Background()