Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update identity state for mtp credentials #644

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading