Skip to content

feat(interledger): add sub-accounts with credit and debt #51

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

Merged
merged 29 commits into from
Aug 11, 2021

Conversation

wilsonianb
Copy link
Contributor

@wilsonianb wilsonianb commented Jul 19, 2021

Changes proposed in this pull request

  • Add sub-accounts
  • Add credit and debt to sub-accounts

Context

Resolves #32

Checklist

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

Persist new balances even if initial transfer fails.
@wilsonianb
Copy link
Contributor Author

tigerbeetle/tigerbeetle#29 has more context for 950d6b0 👆
Also, here's what the commit looks like without husky/yarn format blowing up the diff with indentation changes: wilsonianb@0f1a9bb

Copy link
Collaborator

@matdehaast matdehaast left a comment

Choose a reason for hiding this comment

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

Just some comments for now. Want to dive deeper into the accounts and accounts.test to get a better gripe on this

@@ -282,68 +257,82 @@ export class AccountsService implements AccountsServiceInterface {
}

public async getAccount(accountId: string): Promise<IlpAccount | undefined> {
const accountRow = await IlpAccountModel.query().findById(accountId)
const accountRow = await IlpAccountModel.query()
.withGraphJoined('subAccounts')
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 fetching subAccounts will be pretty expensive -- we should avoid doing it unnecessarily (on every getAccount call). Maybe have a different getSubAccounts method?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@matdehaast do you know what type of call would be most useful for the backend?

  1. get account optionally with its sub-accounts
  2. get account optionally with its sub-accounts' ids
  3. get an account's sub-accounts
  4. get an account's sub-accounts' ids

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah agreed with @sentientwaffle. An account could potentially have 1000s of subaccounts. We should be very deliberate about getting them. I can't be sure of access patterns yet. Lets just leave the basic for now and add as needed

Define AdjustTrustlineMode enum as strings.
Improve mode test readability.
@wilsonianb wilsonianb requested a review from matdehaast July 21, 2021 21:56
Use withGraphFetched instead of withGraphJoined to avoid DBError:
FOR UPDATE cannot be applied to the nullable side of an outer join
Do not query sub-accounts in getAccount
@sentientwaffle
Copy link
Contributor

What is the 'right' way to create a new trustline without transferring any money to it? extendTrustline with autoApply:false and a caller-generated uuid, or createAccount with a super account?

@wilsonianb
Copy link
Contributor Author

As it is now, it'd be:

  1. createAccount with specified superAccountId
  2. extendTrustline with autoApply:false and the id of the account created in step 1

createAccount currently accepts a caller-generated accountId.
As is, trustlines don't have an id. Trustline methods just take accountId of the sub-account to which the trustline is extended.

If we wanted, we could add a way to create sub-account + extend trustline (steps 1+2) in a single operation.

@sentientwaffle
Copy link
Contributor

  • What is the best way to refund all money in a trustline/subaccount back to the parent account (e.g. to refund leftovers on payment completion, or on payment failure/cancellation)? Get balance then immediately revokeTrustline?
  • If extendTrustline is called multiple times in a row (with the same parameters), will it move money every time? Or just the first time?

@wilsonianb
Copy link
Contributor Author

What is the best way to refund all money in a trustline/subaccount back to the parent account (e.g. to refund leftovers on payment completion, or on payment failure/cancellation)? Get balance then immediately revokeTrustline?

Yeah, you'd need to call revokeTrustline with the current balance.
Maybe revokeTrustline's amount could be optional and default to the current balance? The same could be done for utilizeTrustline and settleTrustline.
I'm also wondering if any trustline operations should happen if a sub-account is disabled.

If extendTrustline is called multiple times in a row (with the same parameters), will it move money every time? Or just the first time?

Every time

@wilsonianb wilsonianb mentioned this pull request Aug 2, 2021
4 tasks
@github-actions github-actions bot added pkg: interledger Changes in the interledger package. type: source Changes business logic type: tests Testing related labels Aug 3, 2021
@wilsonianb wilsonianb changed the title feat(accounts): add sub-accounts and trustlines feat(interledger): add sub-accounts with credit and debt Aug 4, 2021
Super-accounts that are not top-level may still extend credit to their
own sub-accounts.
Comment on lines +893 to +894
if (error) {
switch (error.code) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: The if { switch { blocks (here and elsewhere in this file) could be simplified to switch (error?.code) { if you add a noop null/undefined case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

switch (error?.code) doesn't seem to work with createTransfers returning void vs undefined or null

error TS2339: Property 'code' does not exist on type 'void | CreateTransfersError'.
  Property 'code' does not exist on type 'void'.

893     switch (error?.code) {

microsoft/TypeScript#35236

Copy link
Contributor

Choose a reason for hiding this comment

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

Huh, that's annoying. Would error && error.code work instead?

Copy link
Contributor Author

@wilsonianb wilsonianb Aug 11, 2021

Choose a reason for hiding this comment

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

It complains about the subsequent error.indexs and error.code

error TS2339: Property 'index' does not exist on type 'void | CreateTransfersError'.
  Property 'index' does not exist on type 'void'.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, never mind then.

AccountsService.createTransfers doesn't return the error
Copy link
Contributor

@sentientwaffle sentientwaffle left a comment

Choose a reason for hiding this comment

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

LGTM. I apologize for the slow review; I thought I had marked it last week.

@wilsonianb wilsonianb merged commit 99d4f84 into main Aug 11, 2021
@wilsonianb wilsonianb deleted the bw-subaccounts branch August 11, 2021 19:06
wilsonianb referenced this pull request in wilsonianb/rafiki Aug 11, 2021
* feat(accounts): add superAccountId and subAccountIds to IlpAccount

* feat(accounts): add extendTrustline

Add availableCredit and creditExtended account balances

* feat(accounts): add utilizeTrustline

Add totalBorrowed and totalLent account balances

* feat(accounts): add auto-utilize support to extendTrustline

* feat(accounts): add revokeTrustline

* feat(accounts): add settleTrustline

* fix(accounts): order of fetchByIds result not guaranteed

* chore(accounts): clean up adjustTrustline

* chore(accounts): consolidate tests

* chore(accounts): do not orphan account balances

Persist new balances even if initial transfer fails.

* chore(accounts): document balance fields

* chore(accounts): don't export bigIntToUuid

Limit use of Postgres type cast to formatDatabaseJson

* chore(accounts): remove obsolete settlement account code

* chore(accounts): clean up model data conversion

* chore(accounts): document trustline methods

Define AdjustTrustlineMode enum as strings.
Improve mode test readability.

* fix(accounts): lock trustline account rows with forUpdate

Use withGraphFetched instead of withGraphJoined to avoid DBError:
FOR UPDATE cannot be applied to the nullable side of an outer join

* chore(accounts): add getSubAccounts

Do not query sub-accounts in getAccount

* chore(accounts): create balances on sub-account creation

* chore(accounts): refactor trustline methods

* chore(interledger): refactor trustline methods

use AccountService.createTransfers for all transfers

* chore(interledger): replace trustline with credit and debt

* feat(interledger): specify creditor super-account

Super-accounts that are not top-level may still extend credit to their
own sub-accounts.

* chore(interledger): move static methods out of AccountsService

* chore(interledger): don't require asset to create sub-account

* chore(interledger): remove check for linked_event_failed

AccountsService.createTransfers doesn't return the error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: interledger Changes in the interledger package. type: source Changes business logic type: tests Testing related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement Interledger sub-accounts
5 participants