Skip to content

Commit

Permalink
feat: add test for invoice for lightning latch
Browse files Browse the repository at this point in the history
  • Loading branch information
DhananjayPurohit committed Aug 21, 2024
1 parent ac18c2e commit 8da5488
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 5 deletions.
45 changes: 44 additions & 1 deletion clients/tests/web/test-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from 'axios';
const exec = util.promisify(require('node:child_process').exec);

const generateBlocks = async (blocks) => {
const body = {
Expand Down Expand Up @@ -41,4 +42,46 @@ const sleep = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
}

export { generateBlocks, depositCoin, sleep };
const generateInvoice = async (paymentHash, amountInSats) => {

const generateInvoiceCommand = `docker exec $(docker ps -qf "name=mercurylayer-alice-1") lncli -n regtest addholdinvoice ${paymentHash} --amt ${amountInSats}`;
const { stdout, stderr } = await exec(generateInvoiceCommand);
if (stderr) {
console.error('Error:', stderr);
return null;
}

try {
const response = JSON.parse(stdout.trim());
return response;
} catch (error) {
console.error('Error parsing JSON:', error);
return null;
}
}

const payInvoice = async (paymentRequest) => {

const payInvoiceCommand = `docker exec $(docker ps -qf "name=mercurylayer-bob-1") lncli -n regtest payinvoice --force ${paymentRequest}`;
const { stdout, stderr } = await exec(payInvoiceCommand);
if (stderr) {
console.error('Error:', stderr);
return null;
}
console.log('stdout:', stdout.trim());
return stdout.trim();
}

const payHoldInvoice = (paymentRequest) => {

const payInvoiceCommand = `docker exec $(docker ps -qf "name=mercurylayer-bob-1") lncli -n regtest payinvoice --force ${paymentRequest}`;
exec(payInvoiceCommand);
}

const settleInvoice = async (preimage) => {

const settleInvoiceCommand = `docker exec $(docker ps -qf "name=mercurylayer-alice-1") lncli -n regtest settleinvoice ${preimage}`;
await exec(settleInvoiceCommand);
}

export { generateBlocks, depositCoin, sleep, generateInvoice, payInvoice, payHoldInvoice, settleInvoice };
2 changes: 1 addition & 1 deletion clients/tests/web/test/tb03-simple-atomic-transfer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ describe('TB03 - Atomic swap with second party steal', () => {
const toAddress4_for_steal = await mercuryweblib.newTransferAddress(wallet4.name, true);

try {
await mercuryweblib.transferSend(clientConfig, wallet2.name, statechainId1, toAddress4_for_steal.transfer_receive, false, toAddress4.batch_id);
await mercuryweblib.transferSend(clientConfig, wallet2.name, statechainId2, toAddress4_for_steal.transfer_receive, false, toAddress4.batch_id);
} catch (error) {
// Assert the captured error message
const expectedMessage = 'Request failed';
Expand Down
176 changes: 173 additions & 3 deletions clients/tests/web/test/tb04-simple-lightning-latch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { describe, test, expect } from "vitest";
import CoinStatus from 'mercuryweblib/coin_enum.js';
import clientConfig from '../ClientConfig.js';
import mercuryweblib from 'mercuryweblib';
import { generateBlocks, depositCoin } from '../test-utils.js';
import { generateBlocks, depositCoin, sleep, generateInvoice, payInvoice, payHoldInvoice, settleInvoice } from '../test-utils.js';

async function sha256(preimage) {
let buffer;
Expand Down Expand Up @@ -198,7 +198,8 @@ describe('TB04 - The sender tries to get the pre-image before the batch is unloc

let toAddress = "bcrt1q805t9k884s5qckkxv7l698hqlz7t6alsfjsqym";

await mercuryweblib.withdrawCoin(clientConfig, wallet2.name, statechainId, toAddress, null, null);
await mercuryweblib.withdrawCoin(clientConfig, wallet2.name, statechainId1, toAddress, null, null);
await mercuryweblib.withdrawCoin(clientConfig, wallet1.name, statechainId2, toAddress, null, null);

const { preimage } = await mercuryweblib.retrievePreImage(clientConfig, wallet1.name, statechainId1, paymentHash1.batchId);

Expand Down Expand Up @@ -313,7 +314,8 @@ describe('TB04 - Statecoin sender can recover (resend their coin) after batch ti

let toAddress = "bcrt1q805t9k884s5qckkxv7l698hqlz7t6alsfjsqym";

await mercuryweblib.withdrawCoin(clientConfig, wallet2.name, statechainId, toAddress, null, null);
await mercuryweblib.withdrawCoin(clientConfig, wallet2.name, statechainId1, toAddress, null, null);
await mercuryweblib.withdrawCoin(clientConfig, wallet1.name, statechainId2, toAddress, null, null);

const { preimage } = await mercuryweblib.retrievePreImage(clientConfig, wallet1.name, statechainId1, paymentHash1.batchId);

Expand All @@ -322,3 +324,171 @@ describe('TB04 - Statecoin sender can recover (resend their coin) after batch ti
expect(hashPreImage).toEqual(paymentHash1.hash);
});
}, 50000);

describe('TB04 - Statecoin trade with invoice creation, payment and settlement', () => {
test("expected flow", async () => {

localStorage.removeItem("mercury-layer:wallet1_tb04");
localStorage.removeItem("mercury-layer:wallet2_tb04");

let wallet1 = await mercuryweblib.createWallet(clientConfig, "wallet1_tb04");
let wallet2 = await mercuryweblib.createWallet(clientConfig, "wallet2_tb04");

await mercuryweblib.newToken(clientConfig, wallet1.name);

const amount = 1000;

let result = await mercuryweblib.getDepositBitcoinAddress(clientConfig, wallet1.name, amount);

const statechainId = result.statechain_id;

let isDepositInMempool = false;
let isDepositConfirmed = false;
let areBlocksGenerated = false;

await depositCoin(result.deposit_address, amount);

while (!isDepositConfirmed) {

const coins = await mercuryweblib.listStatecoins(clientConfig, wallet1.name);

for (let coin of coins) {
if (coin.statechain_id === statechainId && coin.status === CoinStatus.IN_MEMPOOL && !isDepositInMempool) {
isDepositInMempool = true;
} else if (coin.statechain_id === statechainId && coin.status === CoinStatus.CONFIRMED) {
isDepositConfirmed = true;
break;
}
}

if (isDepositInMempool && !areBlocksGenerated) {
areBlocksGenerated = true;
await generateBlocks(clientConfig.confirmationTarget);
}

await new Promise(r => setTimeout(r, 1000));
}

const paymentHash = await mercuryweblib.paymentHash(clientConfig, wallet1.name, statechainId);

const invoice = await generateInvoice(paymentHash.hash, amount);

payInvoice(invoice.payment_request);

let transferAddress = await mercuryweblib.newTransferAddress(wallet2.name);

await mercuryweblib.transferSend(clientConfig, wallet1.name, statechainId, transferAddress.transfer_receive, false, paymentHash.batchId );

let transferReceive = await mercuryweblib.transferReceive(clientConfig, wallet2.name);

expect(transferReceive.isThereBatchLocked).toBe(true);

await mercuryweblib.confirmPendingInvoice(clientConfig, wallet1.name, statechainId);

transferReceive = await mercuryweblib.transferReceive(clientConfig, wallet2.name);

expect(transferReceive.isThereBatchLocked).toBe(false);

let toAddress = "bcrt1q805t9k884s5qckkxv7l698hqlz7t6alsfjsqym";

await mercuryweblib.withdrawCoin(clientConfig, wallet2.name, statechainId, toAddress, null, null);

const { preimage } = await mercuryweblib.retrievePreImage(clientConfig, wallet1.name, statechainId, paymentHash.batchId);

let hashPreImage = await sha256(preimage);

expect(hashPreImage).toEqual(paymentHash.hash);

await settleInvoice(preimage);
});
}, 50000);

describe('TB04 - Receiver tries to transfer invoice amount to another invoice before preimage retrieval should fail', () => {
test("expected flow", async () => {

localStorage.removeItem("mercury-layer:wallet1_tb04");
localStorage.removeItem("mercury-layer:wallet2_tb04");

let wallet1 = await mercuryweblib.createWallet(clientConfig, "wallet1_tb04");
let wallet2 = await mercuryweblib.createWallet(clientConfig, "wallet2_tb04");

await mercuryweblib.newToken(clientConfig, wallet1.name);

const amount = 1000;

let result = await mercuryweblib.getDepositBitcoinAddress(clientConfig, wallet1.name, amount);

const statechainId = result.statechain_id;

let isDepositInMempool = false;
let isDepositConfirmed = false;
let areBlocksGenerated = false;

await depositCoin(result.deposit_address, amount);

while (!isDepositConfirmed) {

const coins = await mercuryweblib.listStatecoins(clientConfig, wallet1.name);

for (let coin of coins) {
if (coin.statechain_id === statechainId && coin.status === CoinStatus.IN_MEMPOOL && !isDepositInMempool) {
isDepositInMempool = true;
} else if (coin.statechain_id === statechainId && coin.status === CoinStatus.CONFIRMED) {
isDepositConfirmed = true;
break;
}
}

if (isDepositInMempool && !areBlocksGenerated) {
areBlocksGenerated = true;
await generateBlocks(clientConfig.confirmationTarget);
}

await new Promise(r => setTimeout(r, 1000));
}

const paymentHash = await mercuryweblib.paymentHash(clientConfig, wallet1.name, statechainId);

const invoice = await generateInvoice(paymentHash.hash, amount);

payHoldInvoice(invoice.payment_request);

let transferAddress = await mercuryweblib.newTransferAddress(wallet2.name);

await mercuryweblib.transferSend(clientConfig, wallet1.name, statechainId, transferAddress.transfer_receive, false, paymentHash.batchId );

const hashFromServer = await mercurynodejslib.getPaymentHash(clientConfig, paymentHash.batchId);

expect(hashFromServer).to.equal(paymentHash.hash);

let transferReceive = await mercuryweblib.transferReceive(clientConfig, wallet2.name);

expect(transferReceive.isThereBatchLocked).toBe(true);

await mercuryweblib.confirmPendingInvoice(clientConfig, wallet1.name, statechainId);

transferReceive = await mercuryweblib.transferReceive(clientConfig, wallet2.name);

expect(transferReceive.isThereBatchLocked).toBe(false);

let toAddress = "bcrt1q805t9k884s5qckkxv7l698hqlz7t6alsfjsqym";

await mercuryweblib.withdrawCoin(clientConfig, wallet2.name, statechainId, toAddress, null, null);

const { preimage } = await mercuryweblib.retrievePreImage(clientConfig, wallet1.name, statechainId, paymentHash.batchId);

let hashPreImage = await sha256(preimage);

expect(hashPreImage).toEqual(paymentHash.hash);

const paymentHashSecond = "4f67f0a4bc4a8a6a8ecb944e9b748ed7c27655fbdb4c4d3f045d7f18c1e4de64"
const invoiceSecond = await generateInvoice(paymentHashSecond, amount);

try {
await payInvoice(invoiceSecond.payment_request);
} catch (error) {
console.error('Error:', error);
expect(error.message).to.include('failed');
}
});
}, 50000);

0 comments on commit 8da5488

Please sign in to comment.