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 24 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 ready!

Name Link
🔨 Latest commit 83664de
🔍 Latest deploy log https://app.netlify.com/sites/brilliant-pasca-3e80ec/deploys/66bf1b479a946800085bcfa7
😎 Deploy Preview https://deploy-preview-2863--brilliant-pasca-3e80ec.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@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
@github-actions github-actions bot removed pkg: frontend Changes in the frontend package. 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
@sabineschaller sabineschaller marked this pull request as ready for review August 15, 2024 09:30
FROM walletAddressKeys
GROUP BY walletAddressId, kid, x
);
`)
Copy link
Contributor

@BlairCurrey BlairCurrey Aug 15, 2024

Choose a reason for hiding this comment

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

played around with it and it seems to be working. Had to lookup ctid but I guess it probably makes for an easier query than using the uuid.

What's the user experience going to be for the people using the keys being deleted here? I recall the discussion and read the issue but I guess I'm still not quite connecting the dots. For example, is some normal user going to see invalid signature or similar from the grant request process and have no idea they need to upload some new key? I guess probably not because they should still have a key right (just not duplicates of it)? I suppose maybe it just fixes the bug in that issue and otherwise people wont be impacted from these duplicates being deleted.

I see the error for duplicate writes in gql - that makes sense.

Copy link
Member Author

Choose a reason for hiding this comment

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

The users should not have any issues since they still have another copy of the key. But this makes me think I need to check what happens when a user re-adds a key that they have previously revoked. That should be un-revoked and not throw the duplicate error. And I'll also double check that we actually delete the older versions of the key in the migration and keep the most recent.

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah thats a good point... since we're not deleting these and instead setting revoked to true I imagine re-adding the previously revoked key will trigger the unique constraint error. I dont think we can just add the revoked to the unique constraint because you couldnt revoke it twice.

  1. add key, OK
  2. revoke key, OK
  3. re-add same key, OK
  4. revoke same key, NOT OK. already have a revoked key with these unique fields.

Copy link
Contributor

@BlairCurrey BlairCurrey Aug 16, 2024

Choose a reason for hiding this comment

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

Not tested, but I think a partial index like this would work:

CREATE UNIQUE INDEX wallet_address_keys_revoked_false_idx
ON "walletAddressKeys" (walletAddressId, kid, x)
WHERE revoked = false;

Copy link
Collaborator

Choose a reason for hiding this comment

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

do we really want to unrevoke the key? if we do this, we lose the information that sometime in the past, this key was revoked. I think that we need to have new entry in the database if revoked key was again added

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed with @golobitch re: unrevoking (we should just create a new row instead) & @BlairCurrey's partial index suggestion

Comment on lines 5 to 19
exports.up = function (knex) {
// delete any existing duplicates per wallet address
knex.raw(`
DELETE FROM walletAddressKeys
WHERE ctid NOT IN (
SELECT MIN(ctid)
FROM walletAddressKeys
GROUP BY walletAddressId, kid, x
);
`)

return knex.schema.alterTable('walletAddressKeys', (table) => {
table.unique(['walletAddressId', 'kid', 'x'])
})
}
Copy link
Contributor

@BlairCurrey BlairCurrey Aug 15, 2024

Choose a reason for hiding this comment

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

noticed this wasn't actually running the DELETE (and when it does postgres doesnt like the unquoted camelCase names). Need to do something like:

Suggested change
exports.up = function (knex) {
// delete any existing duplicates per wallet address
knex.raw(`
DELETE FROM walletAddressKeys
WHERE ctid NOT IN (
SELECT MIN(ctid)
FROM walletAddressKeys
GROUP BY walletAddressId, kid, x
);
`)
return knex.schema.alterTable('walletAddressKeys', (table) => {
table.unique(['walletAddressId', 'kid', 'x'])
})
}
exports.up = function (knex) {
// delete any existing duplicates per wallet address
return knex
.raw(
`
DELETE FROM "walletAddressKeys"
WHERE ctid NOT IN (
SELECT MIN(ctid)
FROM "walletAddressKeys"
GROUP BY "walletAddressId", kid, x
);
`
)
.then(() => {
return knex.schema.alterTable('walletAddressKeys', (table) => {
table.unique(['walletAddressId', 'kid', 'x'])
})
})
}

Tested locally and it works.

Its kind of annoying but when I want to test migrating data I:

  1. move the file out of the migration dir
  2. spin up the containers and add whatever data that needs to be migrated (adding a duplicate key in this case)
  3. stop the containers (dont remove the volume) and move migration file back in
  4. spin back up and check

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, in PostgreSQL you need to put camel case in quotes. Delete statement was not performed probably because of the missing await, because it is async operation.

Copy link
Contributor

@BlairCurrey BlairCurrey Aug 15, 2024

Choose a reason for hiding this comment

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

wish we would have used the snake case mapper when we setup the db initially: https://vincit.github.io/objection.js/recipes/snake-case-to-camel-case-conversion.html

Copy link
Member Author

@sabineschaller sabineschaller Aug 16, 2024

Choose a reason for hiding this comment

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

I'll add the quotes.

@golobitch all of these have to be async functions?
EDIT: I googled, looks like just the up needs to be async.

Copy link
Contributor

@BlairCurrey BlairCurrey Aug 16, 2024

Choose a reason for hiding this comment

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

With knex and objection, if you dont await the function the query is not executed. Instead it returns a query builder (so you can modify further).

That's what happened originally and asserting as much here really made me stop and think how that was even possible (aka, "am I wrong???"). Guess I simply was not familiar with then() (MDN).

Copy link
Contributor

@BlairCurrey BlairCurrey Aug 16, 2024

Choose a reason for hiding this comment

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

I've found there to be a lot of gotchas with promises in the knex migrations. Another important thing is returning from the migration (which we are doing). We couldn't, for example, await all the queries and not return (something I've done).

I've also seen/tried using Promise.All like this but I don't think it would guarantee resolution order because they run concurrently (ie, second might actually modify the db before the first):

return Promise.All([
  knex.raw(...),
  knex.raw(...)
])

Usually I actually use the dreaded promise chaining 😱 :

return knex.raw(...)
 .then(()=>{
   knex.raw(...)
 })

Ugly, I know. But clear.

The way it is now seems fine - just wanted to chime in with other general pitfalls I've run into.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@sabineschaller No, I meant that knex.raw needs to be awaited

@BlairCurrey
Copy link
Contributor

hmmm what's going on with the history? Just surprised to see a bunch of commits already in main.

@sabineschaller
Copy link
Member Author

hmmm what's going on with the history? Just surprised to see a bunch of commits already in main.

@BlairCurrey I messed up merging in main. I haven't touched code in a while so I'm out of practice 😬

@mkurapov
Copy link
Contributor

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

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.

8 participants