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

chore(core): add (externalId, accountId) invoices index #4377

Merged
merged 3 commits into from
May 9, 2024
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
61 changes: 54 additions & 7 deletions bats/core/api/invoices.bats
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ setup_file() {
clear_cache

create_user 'alice'
create_user 'bob'
seed_invoices
}

Expand Down Expand Up @@ -69,29 +70,75 @@ seed_invoices() {
[[ "$num_errors" == "0" ]] || exit 1
}

@test "invoices: adding multiple invoices with same external id fails" {
token_name='alice'
btc_wallet_name="$token_name.btc_wallet_id"
btc_amount="1000"
@test "invoices: adding multiple invoices with same external id fails for same account" {
external_id="external-id-$RANDOM"

alice_btc_wallet_name="alice.btc_wallet_id"
alice_usd_wallet_name="alice.usd_wallet_id"
bob_btc_wallet_name="bob.btc_wallet_id"
bob_usd_wallet_name="bob.usd_wallet_id"

btc_amount="1000"
usd_amount="20"

variables=$(
jq -n \
--arg wallet_id "$(read_value $btc_wallet_name)" \
--arg wallet_id "$(read_value $alice_btc_wallet_name)" \
--arg amount "$btc_amount" \
--arg external_id "$external_id" \
'{input: {walletId: $wallet_id, amount: $amount, externalId: $external_id}}'
)

exec_graphql "$token_name" 'ln-invoice-create' "$variables"
exec_graphql 'alice' 'ln-invoice-create' "$variables"
num_errors="$(graphql_output '.data.lnInvoiceCreate.errors | length')"
[[ "$num_errors" == "0" ]] || exit 1

exec_graphql "$token_name" 'ln-invoice-create' "$variables"
# Check 'alice' can't re-use externalId for same wallet
exec_graphql 'alice' 'ln-invoice-create' "$variables"
invoice="$(graphql_output '.data.lnInvoiceCreate.invoice')"
[[ "$invoice" == "null" ]] || exit 1
error_msg="$(graphql_output '.data.lnInvoiceCreate.errors[0].message')"
[[ "${error_msg}" =~ "already exists" ]] || exit 1

# Check 'alice' can't re-use externalId for different wallet
variables=$(
jq -n \
--arg wallet_id "$(read_value $alice_usd_wallet_name)" \
--arg amount "$usd_amount" \
--arg external_id "$external_id" \
'{input: {walletId: $wallet_id, amount: $amount, externalId: $external_id}}'
)
exec_graphql 'alice' 'ln-usd-invoice-create' "$variables"
invoice="$(graphql_output '.data.lnUsdInvoiceCreate.invoice')"
[[ "$invoice" == "null" ]] || exit 1
error_msg="$(graphql_output '.data.lnUsdInvoiceCreate.errors[0].message')"
[[ "${error_msg}" =~ "already exists" ]] || exit 1

# Check 'bob' can re-use externalId once
variables=$(
jq -n \
--arg wallet_id "$(read_value $bob_btc_wallet_name)" \
--arg amount "$btc_amount" \
--arg external_id "$external_id" \
'{input: {walletId: $wallet_id, amount: $amount, externalId: $external_id}}'
)
exec_graphql 'bob' 'ln-invoice-create' "$variables"
num_errors="$(graphql_output '.data.lnInvoiceCreate.errors | length')"
[[ "$num_errors" == "0" ]] || exit 1

# Check 'bob' cant re-use externalId again
variables=$(
jq -n \
--arg wallet_id "$(read_value $bob_usd_wallet_name)" \
--arg amount "$usd_amount" \
--arg external_id "$external_id" \
'{input: {walletId: $wallet_id, amount: $amount, externalId: $external_id}}'
)
exec_graphql 'bob' 'ln-usd-invoice-create' "$variables"
invoice="$(graphql_output '.data.lnUsdInvoiceCreate.invoice')"
[[ "$invoice" == "null" ]] || exit 1
error_msg="$(graphql_output '.data.lnUsdInvoiceCreate.errors[0].message')"
[[ "${error_msg}" =~ "already exists" ]] || exit 1
}

@test "invoices: get invoices for account" {
Expand Down
5 changes: 1 addition & 4 deletions core/api/src/domain/shared/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,9 @@ type BalanceAmount<T extends WalletCurrency> = Amount<T> & {
readonly brand?: unique symbol
}

type PartialWalletDescriptor<T extends WalletCurrency> = {
type WalletDescriptor<T extends WalletCurrency> = {
id: WalletId
currency: T
accountId?: AccountId // TODO: unify when we migrate accountId to invoices collection
}
type WalletDescriptor<T extends WalletCurrency> = PartialWalletDescriptor<T> & {
accountId: AccountId
}

Expand Down
2 changes: 1 addition & 1 deletion core/api/src/domain/wallet-invoices/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ type WalletInvoiceWithOptionalLnInvoice = {
selfGenerated: boolean
pubkey: Pubkey
usdAmount?: UsdPaymentAmount
recipientWalletDescriptor: PartialWalletDescriptor<WalletCurrency>
recipientWalletDescriptor: WalletDescriptor<WalletCurrency>
paid: boolean
createdAt: Date
processingCompleted: boolean
Expand Down
4 changes: 3 additions & 1 deletion core/api/src/services/mongoose/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const walletInvoiceSchema = new Schema<WalletInvoiceRecord>({
},

accountId: {
required: true,
type: String,
validate: {
validator: function (v: string) {
Expand Down Expand Up @@ -98,14 +99,15 @@ const walletInvoiceSchema = new Schema<WalletInvoiceRecord>({
},

externalId: {
required: true,
type: String,
unique: true,
validator: (v: string) => !(checkedToLedgerExternalId(v) instanceof Error),
},
})

walletInvoiceSchema.index({ walletId: 1, paid: 1 })
walletInvoiceSchema.index({ paid: 1, processingCompleted: 1 })
walletInvoiceSchema.index({ accountId: 1, externalId: 1 }, { unique: true })

export const WalletInvoice = mongoose.model<WalletInvoiceRecord>(
"InvoiceUser",
Expand Down
7 changes: 3 additions & 4 deletions core/api/test/helpers/wallet-invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { randomLedgerExternalId } from "./random"
import { decodeInvoice, getSecretAndPaymentHash } from "@/domain/bitcoin/lightning"
import { WalletCurrency } from "@/domain/shared"

export const createMockWalletInvoice = (recipientWalletDescriptor: {
currency: WalletCurrency
id: WalletId
}): WalletInvoice => {
export const createMockWalletInvoice = (
recipientWalletDescriptor: WalletDescriptor<WalletCurrency>,
): WalletInvoice => {
const externalId = randomLedgerExternalId()
if (externalId instanceof Error) throw externalId

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe("getInvoicePreImageByHash", () => {
it("returns a valid preimage when invoice has been paid", async () => {
const invoice = createMockWalletInvoice({
id: crypto.randomUUID() as WalletId,
accountId: crypto.randomUUID() as AccountId,
currency: WalletCurrency.Btc,
})
await WalletInvoicesRepository().persistNew({ ...invoice, paid: true })
Expand All @@ -27,6 +28,7 @@ describe("getInvoicePreImageByHash", () => {
it("returns error if invoice has not been paid", async () => {
const invoice = createMockWalletInvoice({
id: crypto.randomUUID() as WalletId,
accountId: crypto.randomUUID() as AccountId,
currency: WalletCurrency.Btc,
})
await WalletInvoicesRepository().persistNew({ ...invoice, paid: false })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ describe("update pending invoices", () => {
recipientWalletDescriptor: {
id: randomUUID() as WalletId,
currency: WalletCurrency.Usd,
accountId: randomUUID() as AccountId,
},
paid: false,
lnInvoice: mockLnInvoice,
Expand Down Expand Up @@ -130,6 +131,7 @@ describe("update pending invoices", () => {
recipientWalletDescriptor: {
id: randomUUID() as WalletId,
currency: WalletCurrency.Btc,
accountId: randomUUID() as AccountId,
},
paid: false,
lnInvoice: mockLnInvoice,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const walletInvoices = WalletInvoicesRepository()
const recipientWalletDescriptor = {
currency: WalletCurrency.Btc,
id: crypto.randomUUID() as WalletId,
accountId: crypto.randomUUID() as AccountId,
}

let createdInvoices: WalletInvoice[] = []
Expand All @@ -32,6 +33,7 @@ beforeAll(async () => {
createMockWalletInvoice({
currency: WalletCurrency.Btc,
id: crypto.randomUUID() as WalletId,
accountId: crypto.randomUUID() as AccountId,
}),
)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ const goodWalletInvoice: WalletInvoiceWithOptionalLnInvoice = {
secret: "secretPreImage" as SecretPreImage,
selfGenerated: true,
pubkey: "pubkey" as Pubkey,
recipientWalletDescriptor: { id: "walletId" as WalletId, currency: WalletCurrency.Usd },
recipientWalletDescriptor: {
id: "walletId" as WalletId,
accountId: crypto.randomUUID() as AccountId,
currency: WalletCurrency.Usd,
},
paid: false,
createdAt: new Date(Date.now()),
processingCompleted: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,15 @@ describe("WalletInvoiceReceiver", () => {

const recipientAccountId = "recipientAccountId" as AccountId

const partialRecipientBtcWalletDescriptor = {
const recipientBtcWalletDescriptor = {
id: "recipientBtcWalletId" as WalletId,
currency: WalletCurrency.Btc,
}
const recipientBtcWalletDescriptor = {
...partialRecipientBtcWalletDescriptor,
accountId: recipientAccountId,
}

const partialRecipientUsdWalletDescriptor = {
const recipientUsdWalletDescriptor = {
id: "recipientUsdWalletId" as WalletId,
currency: WalletCurrency.Usd,
}
const recipientUsdWalletDescriptor = {
...partialRecipientUsdWalletDescriptor,
accountId: recipientAccountId,
}

Expand Down Expand Up @@ -77,7 +71,7 @@ describe("WalletInvoiceReceiver", () => {
pubkey: "pubkey" as Pubkey,
usdAmount: undefined,
paid: false,
recipientWalletDescriptor: partialRecipientBtcWalletDescriptor,
recipientWalletDescriptor: recipientBtcWalletDescriptor,
createdAt: new Date(),
lnInvoice: mockLnInvoice,
processingCompleted: false,
Expand Down Expand Up @@ -116,7 +110,7 @@ describe("WalletInvoiceReceiver", () => {
const amountUsdInvoice: WalletInvoice = {
paymentHash: "paymentHash" as PaymentHash,
secret: "secret" as SecretPreImage,
recipientWalletDescriptor: partialRecipientUsdWalletDescriptor,
recipientWalletDescriptor: recipientUsdWalletDescriptor,
selfGenerated: false,
pubkey: "pubkey" as Pubkey,
usdAmount: UsdPaymentAmount(BigInt(100)),
Expand Down Expand Up @@ -153,7 +147,7 @@ describe("WalletInvoiceReceiver", () => {
const noAmountUsdInvoice: WalletInvoice = {
paymentHash: "paymentHash" as PaymentHash,
secret: "secret" as SecretPreImage,
recipientWalletDescriptor: partialRecipientUsdWalletDescriptor,
recipientWalletDescriptor: recipientUsdWalletDescriptor,
selfGenerated: false,
pubkey: "pubkey" as Pubkey,
paid: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ const baseInvoice: WalletInvoice = {
secret: "secretPreImage" as SecretPreImage,
selfGenerated: true,
pubkey: "pubkey" as Pubkey,
recipientWalletDescriptor: { id: "walletId" as WalletId, currency: WalletCurrency.Usd },
recipientWalletDescriptor: {
id: "walletId" as WalletId,
currency: WalletCurrency.Usd,
accountId: "accountId" as AccountId,
},
paid: false,
createdAt: new Date(Date.now()),
processingCompleted: false,
Expand Down
Loading