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

feat(backend): unique keys per wallet address #2863

Open
wants to merge 36 commits into
base: main
Choose a base branch
from

Conversation

sabineschaller
Copy link
Member

@sabineschaller sabineschaller commented Aug 15, 2024

Changes proposed in this pull request

This prevents uploading the same key per wallet address multiple times.

Context

Checklist

  • Related issues linked using fixes #number
  • Tests added/updated
  • Documentation added
  • Make sure that all checks pass
  • Bruno collection updated

sabineschaller and others added 18 commits August 1, 2024 09:21
…posit input (#2817)

* fix(frontend): asset scale consistency in liquidity dialogs.

* Ensure asset scale consistency when displaying and withdrawing liquidity by adding asset info to the liquidity dialog component and updating the input handling in Rafiki Admin UI.
---------

Co-authored-by: Blair Currey <[email protected]>
* fix: getting the localenv docs and readme in sync

* chore: updated MASE screenshots

* chore: updating the code block language identifier to have consistent approach through the docs
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* fixed(frontend) asset page now retains page scroll position.

* feat(frontend) added autofocus to liquidity dialog input

* feat(fronted) made eslint happy
* feat(auth): build with alpine3.19

* feat(backend): build with alpine3.19

* feat(frontend): build with alpine3.19

* bump(localenv): docker image to alpine 3.19
* fix(auth): interact redirect

* fix(auth): session cookie not expiring in browser

* fix(auth): session expiration time unit

---------

Co-authored-by: Blair Currey <[email protected]>
* feat(auth): return granId for the grant lookup via interaction id

* test(auth): check grantId is returned for grant lookup via interaction id

* docs(openapi): auth return grantId for grant lookup via interaction id
* feat(backend): support for returning grantId when querying outgoing payment

When querying outgoing payment, either single one, or list of them via pagination, etc., it will be
possible to also get a grantId under which the outgoing

* test(outgoing-payment): check if grantId is returned

* docs(bruno): include grantId when fetching outgoing payment
* feat(localenv): add span metric generation

- adds configuration that generates span metrics from tempo traces
- can see new `traces_spanmetrics_bucket` etc. in local grafana dashboard

* feat(localenv): add gql resolver metric

* chore(localenv): give panel title
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* feat(2737): add fees as metric for outgoing payment.

* feat(2737): rename to payment_fees.

* feat(2737): test case updates.

* feat(2737): formatting.

* feat(2737): re-order test cases. Move fee collector.

* feat(2737): dashboard and doc updates.

* feat(2737): merged with main.

* feat(2737): review feedback applied from @JoblersTune.

* feat(2737): review feedback applied from @mkurapov.

* feat(2737): additional tests for covert of assets and rates.

* feat(2737): additional tests ensuring the increment counter was called.

* feat(2737): additional tests ensuring the increment counter was called.

* feat(2737): readme.
Our builds are failing due to Trivy scanner. Trivy scanner actually found that our Axios version
v1.6.8 has a vulnerability - CVE-2024-39338. This was fixed in version 1.7.4, hence, the upgrade.

fix #2860
Copy link

netlify bot commented Aug 15, 2024

Deploy Preview for brilliant-pasca-3e80ec canceled.

Name Link
🔨 Latest commit 424cce6
🔍 Latest deploy log https://app.netlify.com/sites/brilliant-pasca-3e80ec/deploys/67614daf1dfaf10009792715

@github-actions github-actions bot added type: tests Testing related pkg: backend Changes in the backend package. pkg: frontend Changes in the frontend package. type: source Changes business logic pkg: auth Changes in the GNAP auth package. type: localenv Local playground pkg: mock-ase pkg: documentation Changes in the documentation package. type: documentation (archived) Improvements or additions to documentation pkg: mock-account-service-lib labels Aug 15, 2024
@mkurapov
Copy link
Contributor

@sabineschaller to double check, are we good to review this?

@oana-lolea
Copy link
Contributor

Changes:

  • Added unrevoke wallet address key query resolver
  • Updated migration script to create a unique index on walletAddressId, kid and x, when the key is not revoked, because we can have the same key revoked multiple times (useful for keeping the key's history)
  • Added tests for the service
  • Added bruno file for unrevoking

@mkurapov mkurapov linked an issue Nov 25, 2024 that may be closed by this pull request
2 tasks
@oana-lolea
Copy link
Contributor

Updates based on #3130.

  1. Updated migration to:
  • delete all the duplicate active keys of a wallet address, keeping the most recent one.
  • unique constraint is now on (walletAddressId, kid), to avoid having duplicate keys.
  1. Removed unrevoke resolver, because create does exactly what unrevoke is supposed to do: create a new row with a key that is not revoked.

  2. Tested that UniqueConstraint error is returned when trying to add the same key that is not revoked.

})
})

test('Returns DuplicateKey when the key already exists and it is unrevoked', async (): Promise<void> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you are already handling this in the "'cannot add duplicate key to a wallet address'" test.


const revokedKey = await walletAddressKeyService.revoke(key.id)
await expect(walletAddressKeyService.revoke(key.id)).resolves.toEqual(
revokedKey
)
})
})

describe('Unrevoke Wallet Address Keys', (): void => {
test('Creates a new key unrevoked', async (): Promise<void> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just add this to the "create" describe block

Comment on lines 9 to 17
`DELETE FROM "walletAddressKeys" w
WHERE revoked = false
AND "createdAt" <> (
SELECT MAX("createdAt")
FROM "walletAddressKeys"
WHERE revoked = false
AND "walletAddressId" = w."walletAddressId"
GROUP BY "walletAddressId"
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a tricky one. One thing we need to add is the kid parameter (so we don't delete all non-revoked keys for a wallet address), and also maybe we can come up with a way to not delete by createdAt (although very unlikely, it could end up deleting valid, non-duplicated keys which happen to have the same createdAt).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that grouping by kid is the right way to do it.

Suggested change
`DELETE FROM "walletAddressKeys" w
WHERE revoked = false
AND "createdAt" <> (
SELECT MAX("createdAt")
FROM "walletAddressKeys"
WHERE revoked = false
AND "walletAddressId" = w."walletAddressId"
GROUP BY "walletAddressId"
);
`DELETE FROM "walletAddressKeys" w
WHERE revoked = false
AND "createdAt" <> (
SELECT MAX("createdAt")
FROM "walletAddressKeys"
WHERE revoked = false
AND kid = w.kid
GROUP BY kid
);

Could we use TOP to delete just one key if there are multiple valid keys with the same createdAt? I don't see another way to determine which key to choose.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a way to do it, but still based on createdAt. If there are multiple keys that have the most recent createdAt, we can keep the first one we find and delete the rest. What do you think, @mkurapov ?

Suggested change
`DELETE FROM "walletAddressKeys" w
WHERE revoked = false
AND "createdAt" <> (
SELECT MAX("createdAt")
FROM "walletAddressKeys"
WHERE revoked = false
AND "walletAddressId" = w."walletAddressId"
GROUP BY "walletAddressId"
);
`DELETE FROM "walletAddressKeys" w
WHERE revoked = false
AND w."createdAt" = (
SELECT "createdAt"
FROM "walletAddressKeys"
WHERE revoked = false
AND kid = w.kid
ORDER BY "createdAt" DESC
LIMIT 1
)
AND w.id <> (
SELECT id
FROM "walletAddressKeys"
WHERE revoked = false
AND kid = w.kid
ORDER BY "createdAt" DESC
LIMIT 1 OFFSET 1
);
`

Copy link
Contributor

@mkurapov mkurapov Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oana-lolea I was looking through the PR history, and I think we can use physical location row ctid value:

DELETE FROM walletAddressKeys
WHERE ctid NOT IN (
SELECT MIN(ctid)
FROM walletAddressKeys
GROUP BY walletAddressId, kid, x
);

I agree with you on keeping the most recent key so we'd have to adjust the migration a bit, but using ctid should work.

Comment on lines 12 to 15
SELECT MIN(ctid)
FROM "walletAddressKeys"
WHERE revoked = false
AND kid = w.kid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to group by walletAddressId and kid here still, otherwise we may delete keys that have the same kid but belong to a different wallet address.

Additionally, we should also use MAX(ctid) so we can grab the most recent key

Comment on lines 9 to 17
`DELETE FROM "walletAddressKeys" w
WHERE revoked = false
AND ctid NOT IN (
SELECT MAX(ctid)
FROM "walletAddressKeys"
WHERE revoked = false
AND kid = w.kid
GROUP BY kid, "walletAddressId"
)`
Copy link
Contributor

@mkurapov mkurapov Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`DELETE FROM "walletAddressKeys" w
WHERE revoked = false
AND ctid NOT IN (
SELECT MAX(ctid)
FROM "walletAddressKeys"
WHERE revoked = false
AND kid = w.kid
GROUP BY kid, "walletAddressId"
)`
`DELETE FROM "walletAddressKeys" w
AND ctid NOT IN (
SELECT MAX(ctid)
FROM "walletAddressKeys"
WHERE revoked = false
GROUP BY kid, "walletAddressId"
)`

can you double check if this suggestion has the same behaviour? I think it should. Otherwise, looks good 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: backend Changes in the backend package. type: source Changes business logic type: tests Testing related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Managing unique wallet address keys
9 participants