Skip to content

Commit

Permalink
Merge pull request #644 from 0xPolygonID/PID-2042-issuer-node-fix-upd…
Browse files Browse the repository at this point in the history
…ate-identity-state-for-mtp-credentials

fix: update identity state for mtp credentials
  • Loading branch information
martinsaporiti authored Apr 5, 2024
2 parents 930e304 + 1b155c1 commit e1e2edd
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 63 deletions.
33 changes: 27 additions & 6 deletions internal/core/services/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,19 +327,27 @@ func (i *identity) UpdateState(ctx context.Context, did w3c.DID) (*domain.Identi
return fmt.Errorf("error getting the identifier last state: %w", err)
}

lc, err := i.claimsRepository.GetAllByState(ctx, tx, &did, nil)
var state *merkletree.Hash = nil
if previousState != nil {
state = previousState.TreeState().State
}

lc, err := i.claimsRepository.GetAllByState(ctx, tx, &did, state)
if err != nil {
return fmt.Errorf("error getting the states: %w", err)
}

if len(lc) == 0 {
return ErrNoClaimsFoundToProcess
// Check if there are claims to process
if err := checkClaimsToAdd(lc); err != nil {
return err
}

for i := range lc {
err = iTrees.AddClaim(ctx, &lc[i])
if err != nil {
return err
if lc[i].IdentityState == nil {
err = iTrees.AddClaim(ctx, &lc[i])
if err != nil {
return err
}
}
}

Expand Down Expand Up @@ -395,6 +403,19 @@ func (i *identity) UpdateState(ctx context.Context, did w3c.DID) (*domain.Identi
return newState, err
}

// checkClaimsToAdd checks if there are claims to process
// if the len of the claims is 0 or the len is 1 and the claim is the authBJJCredential then return an error
func checkClaimsToAdd(lc []domain.Claim) error {
if len(lc) == 0 {
return ErrNoClaimsFoundToProcess
}

if len(lc) == 1 && lc[0].SchemaType == domain.AuthBJJCredentialTypeID {
return ErrNoClaimsFoundToProcess
}
return nil
}

func (i *identity) UpdateIdentityState(ctx context.Context, state *domain.IdentityState) error {
// save identity to store
err := i.storage.Pgx.BeginFunc(ctx, func(tx pgx.Tx) error {
Expand Down
159 changes: 103 additions & 56 deletions internal/core/services/tests/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,71 +45,118 @@ func Test_identity_UpdateState(t *testing.T) {

identity, err := identityService.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
require.NoError(t, err)

identity2, err := identityService.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
require.NoError(t, err)

schema := "https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json"
did, err := w3c.ParseDID(identity.Identifier)
assert.NoError(t, err)
did2, err := w3c.ParseDID(identity2.Identifier)
assert.NoError(t, err)
did3, err := w3c.ParseDID("did:polygonid:polygon:mumbai:2qD6cqGpLX2dibdFuKfrPxGiybi3wKa8RbR4onw49H")
assert.NoError(t, err)
credentialSubject := map[string]any{
"id": "did:polygonid:polygon:mumbai:2qE1BZ7gcmEoP2KppvFPCZqyzyb5tK9T6Gec5HFANQ",
"birthday": 19960424,
"documentType": 2,
}
typeC := "KYCAgeCredential"

merklizedRootPosition := "index"
_, err = claimsService.Save(context.Background(), ports.NewCreateClaimRequest(did, schema, credentialSubject, common.ToPointer(time.Now()), typeC, nil, nil, &merklizedRootPosition, common.ToPointer(true), common.ToPointer(true), nil, false, verifiable.SparseMerkleTreeProof, nil, nil, nil))
assert.NoError(t, err)

type testConfig struct {
name string
did *w3c.DID
shouldReturnErr bool
}

for _, tc := range []testConfig{
{
name: "should get a new state for identity with a claim",
did: did,
shouldReturnErr: false,
},
{
name: "should get a new state for identity without claim",
did: did2,
shouldReturnErr: true,
},
{
name: "should return an error",
did: did3,
shouldReturnErr: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
previousStateIdentity, _ := identityStateRepo.GetLatestStateByIdentifier(ctx, storage.Pgx, tc.did)
identityState, err := identityService.UpdateState(ctx, *tc.did)
if tc.shouldReturnErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.NoError(t, err)
assert.Equal(t, tc.did.String(), identityState.Identifier)
assert.NotNil(t, identityState.State)
assert.Equal(t, domain.StatusCreated, identityState.Status)
assert.NotNil(t, identityState.StateID)
assert.Equal(t, previousStateIdentity.State, identityState.PreviousState)
assert.NotNil(t, identityState.RootOfRoots)
assert.NotNil(t, identityState.ClaimsTreeRoot)
assert.NotNil(t, identityState.RevocationTreeRoot)
assert.Equal(t, domain.StatusCreated, identityState.Status)
}
})
}
t.Run("should update state", func(t *testing.T) {
ctx := context.Background()
merklizedRootPosition := "index"
_, err = claimsService.Save(ctx, ports.NewCreateClaimRequest(did, schema, credentialSubject,
common.ToPointer(time.Now()), typeC, nil, nil, &merklizedRootPosition,
common.ToPointer(true), common.ToPointer(true), nil, false,
verifiable.SparseMerkleTreeProof, nil, nil, nil))

assert.NoError(t, err)
previousStateIdentity, _ := identityStateRepo.GetLatestStateByIdentifier(ctx, storage.Pgx, did)
identityState, err := identityService.UpdateState(ctx, *did)
assert.NoError(t, err)
assert.Equal(t, did.String(), identityState.Identifier)
assert.NotNil(t, identityState.State)
assert.Equal(t, domain.StatusCreated, identityState.Status)
assert.NotNil(t, identityState.StateID)
assert.Equal(t, previousStateIdentity.State, identityState.PreviousState)
assert.NotNil(t, identityState.RootOfRoots)
assert.NotNil(t, identityState.ClaimsTreeRoot)
assert.NotNil(t, identityState.RevocationTreeRoot)
})

t.Run("should update state for a new credential with mtp", func(t *testing.T) {
ctx := context.Background()
merklizedRootPosition := "index"
_, err = claimsService.Save(ctx, ports.NewCreateClaimRequest(did, schema, credentialSubject,
common.ToPointer(time.Now()), typeC, nil, nil, &merklizedRootPosition,
common.ToPointer(false), common.ToPointer(true), nil, false,
verifiable.SparseMerkleTreeProof, nil, nil, nil))

assert.NoError(t, err)
previousStateIdentity, _ := identityStateRepo.GetLatestStateByIdentifier(ctx, storage.Pgx, did)
identityState, err := identityService.UpdateState(ctx, *did)
assert.NoError(t, err)
assert.Equal(t, did.String(), identityState.Identifier)
assert.NotNil(t, identityState.State)
assert.Equal(t, domain.StatusCreated, identityState.Status)
assert.NotNil(t, identityState.StateID)
assert.Equal(t, previousStateIdentity.State, identityState.PreviousState)
assert.NotNil(t, identityState.RootOfRoots)
assert.NotNil(t, identityState.ClaimsTreeRoot)
assert.NotNil(t, identityState.RevocationTreeRoot)
})

t.Run("should return an error after revoke a credential", func(t *testing.T) {
ctx := context.Background()
merklizedRootPosition := "index"
claim, err := claimsService.Save(ctx, ports.NewCreateClaimRequest(did, schema, credentialSubject,
common.ToPointer(time.Now()), typeC, nil, nil, &merklizedRootPosition,
common.ToPointer(false), common.ToPointer(true), nil, false,
verifiable.SparseMerkleTreeProof, nil, nil, nil))

assert.NoError(t, err)
previousStateIdentity, _ := identityStateRepo.GetLatestStateByIdentifier(ctx, storage.Pgx, did)
identityState, err := identityService.UpdateState(ctx, *did)
assert.NoError(t, err)
assert.Equal(t, did.String(), identityState.Identifier)
assert.NotNil(t, identityState.State)
assert.Equal(t, domain.StatusCreated, identityState.Status)
assert.NotNil(t, identityState.StateID)
assert.Equal(t, previousStateIdentity.State, identityState.PreviousState)
assert.NotNil(t, identityState.RootOfRoots)
assert.NotNil(t, identityState.ClaimsTreeRoot)
assert.NotNil(t, identityState.RevocationTreeRoot)

assert.NoError(t, claimsService.Revoke(ctx, *did, uint64(claim.RevNonce), ""))
_, err = identityService.UpdateState(ctx, *did)
assert.Error(t, err)
})

t.Run("should get an error creating credential with sig proof", func(t *testing.T) {
ctx := context.Background()
merklizedRootPosition := "index"
_, err = claimsService.Save(ctx, ports.NewCreateClaimRequest(did, schema, credentialSubject,
common.ToPointer(time.Now()), typeC, nil, nil, &merklizedRootPosition,
common.ToPointer(true), common.ToPointer(false), nil, false,
verifiable.SparseMerkleTreeProof, nil, nil, nil))

assert.NoError(t, err)
_, err = identityStateRepo.GetLatestStateByIdentifier(ctx, storage.Pgx, did)
assert.NoError(t, err)
_, err = identityService.UpdateState(ctx, *did)
assert.Error(t, err)
})

t.Run("should update state after revoke credential with sig proof", func(t *testing.T) {
ctx := context.Background()
merklizedRootPosition := "index"
claim, err := claimsService.Save(ctx, ports.NewCreateClaimRequest(did, schema, credentialSubject,
common.ToPointer(time.Now()), typeC, nil, nil, &merklizedRootPosition,
common.ToPointer(true), common.ToPointer(false), nil, false,
verifiable.SparseMerkleTreeProof, nil, nil, nil))

assert.NoError(t, err)
_, err = identityStateRepo.GetLatestStateByIdentifier(ctx, storage.Pgx, did)
assert.NoError(t, err)
_, err = identityService.UpdateState(ctx, *did)
assert.Error(t, err)

assert.NoError(t, claimsService.Revoke(ctx, *did, uint64(claim.RevNonce), ""))
_, err = identityService.UpdateState(ctx, *did)
assert.NoError(t, err)
})
}

func Test_identity_GetByDID(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion internal/repositories/claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ func (c *claims) GetAllByState(ctx context.Context, conn db.Querier, did *w3c.DI
core_claim
FROM claims
LEFT OUTER JOIN identity_states ON claims.identity_state = identity_states.state
WHERE issuer = $1 AND identity_state = $2 AND claims.identifier = issuer AND (mtp = true OR revoked = true)
WHERE issuer = $1 AND ((identity_state IS NULL AND (mtp = true OR revoked = true) OR (identity_state = $2 AND mtp = true)))
AND claims.identifier = issuer
`, did.String(), state.Hex())
}

Expand Down

0 comments on commit e1e2edd

Please sign in to comment.