diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c94282d9..6367fc53 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -168,13 +168,7 @@ jobs: run: | cd clients/tests/web sudo chmod -R 755 ./data_bitcoin_regtest - npx vitest --browser.name=chromium --browser.headless - - name: Run Client-Side Tests - run: | - cd clients/apps/nodejs - node test_basic_workflow2.js - node test_atomic_swap.js - mocha ./test/tb04-simple-lightning-latch.mjs --exit + npx vitest --browser.name=chromium --browser.headless - name: Tear Down run: | docker-compose -f docker-compose-test.yml down diff --git a/clients/apps/nodejs/test/ta02-duplicate-deposits.mjs b/clients/apps/nodejs/test/ta02-duplicate-deposits.mjs deleted file mode 100644 index 92d73111..00000000 --- a/clients/apps/nodejs/test/ta02-duplicate-deposits.mjs +++ /dev/null @@ -1,166 +0,0 @@ -import { expect } from 'chai'; -import client_config from '../client_config.js'; -import mercurynodejslib from 'mercurynodejslib'; -import { CoinStatus } from 'mercurynodejslib/coin_enum.js'; -import { createWallet, removeDatabase, getnewaddress, generateBlock, depositCoin } from './test-utils.mjs'; - -describe('TA02 - Duplicated Deposits', function() { - - context('Withdraw Flow', () => { - it('should complete successfully', async () => { - - const clientConfig = client_config.load(); - await removeDatabase(clientConfig); - - let wallet_1_name = "w1"; - let wallet_2_name = "w2"; - let wallet1 = await createWallet(clientConfig, wallet_1_name); - let wallet2 = await createWallet(clientConfig, wallet_2_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet1.name); - const tokenId = token.token_id; - - const amount1 = 10000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet1.name, amount1); - - await depositCoin(depositInfo.deposit_address, amount1); - - let amount2 = 20000; - await depositCoin(depositInfo.deposit_address, amount2); - - const coreWalletAddress = await getnewaddress(); - - await generateBlock(clientConfig.confirmationTarget, coreWalletAddress); - - const listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet1.name); - - expect(listCoins.length).to.equal(2); - - const newCoin = listCoins.find(coin => - coin.aggregated_address == depositInfo.deposit_address && - coin.status == CoinStatus.CONFIRMED - ); - - const duplicatedCoin = listCoins.find(coin => - coin.aggregated_address == depositInfo.deposit_address && - coin.status == CoinStatus.DUPLICATED - ); - - expect(newCoin).to.not.be.null; - expect(duplicatedCoin).to.not.be.null; - - expect(newCoin.duplicate_index).to.equal(0); - expect(duplicatedCoin.duplicate_index).to.equal(1); - - const transferAddress = await mercurynodejslib.newTransferAddress(clientConfig, wallet2.name, null); - - try { - await mercurynodejslib.transferSend(clientConfig, wallet1.name, newCoin.statechain_id, transferAddress.transfer_receive, false, null); - } catch (error) { - expect(error.message).to.include("Coin is duplicated. If you want to proceed, use the command '--force, -f' option. " + - "You will no longer be able to move other duplicate coins with the same statechain_id and this will cause PERMANENT LOSS of these duplicate coin funds."); - } - - const withdrawAddress = "bcrt1qn5948np2j8t68xgpceneg3ua4wcwhwrsqj8scv"; - - let txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet1.name, duplicatedCoin.statechain_id, withdrawAddress, null, 1); - - expect(txid).to.be.string; - - try { - await mercurynodejslib.transferSend(clientConfig, wallet1.name, newCoin.statechain_id, transferAddress.transfer_receive, false, null); - } catch (error) { - expect(error.message).to.include("There have been withdrawals of other coins with this same statechain_id (possibly duplicates). " + - "This transfer cannot be performed because the recipient would reject it due to the difference in signature count. This coin can be withdrawn, however."); - } - - txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet1.name, duplicatedCoin.statechain_id, withdrawAddress, null, 0); - - expect(txid).to.be.string; - }) - }), - - context('Transfer Flow', () => { - it('should complete successfully', async () => { - - const clientConfig = client_config.load(); - await removeDatabase(clientConfig); - - let wallet_1_name = "w1"; - let wallet_2_name = "w2"; - let wallet1 = await createWallet(clientConfig, wallet_1_name); - let wallet2 = await createWallet(clientConfig, wallet_2_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet1.name); - const tokenId = token.token_id; - - const amount1 = 10000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet1.name, amount1); - - await depositCoin(depositInfo.deposit_address, amount1); - - let amount2 = 20000; - await depositCoin(depositInfo.deposit_address, amount2); - - const coreWalletAddress = await getnewaddress(); - - await generateBlock(clientConfig.confirmationTarget, coreWalletAddress); - - let listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet1.name); - - expect(listCoins.length).to.equal(2); - - const newCoin = listCoins.find(coin => - coin.aggregated_address == depositInfo.deposit_address && - coin.status == CoinStatus.CONFIRMED - ); - - let duplicatedCoin = listCoins.find(coin => - coin.aggregated_address == depositInfo.deposit_address && - coin.status == CoinStatus.DUPLICATED - ); - - expect(newCoin).to.not.be.null; - expect(duplicatedCoin).to.not.be.null; - - expect(newCoin.duplicate_index).to.equal(0); - expect(duplicatedCoin.duplicate_index).to.equal(1); - - const transferAddress = await mercurynodejslib.newTransferAddress(clientConfig, wallet2.name, null); - - let result = await mercurynodejslib.transferSend(clientConfig, wallet1.name, newCoin.statechain_id, transferAddress.transfer_receive, true, null); - expect(result).to.have.property('statechain_id'); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet2.name); - - expect(transferReceiveResult.receivedStatechainIds).contains(newCoin.statechain_id); - expect(transferReceiveResult.receivedStatechainIds.length).to.equal(1); - - listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet1.name); - - const transferredCoin = listCoins.find(coin => - coin.aggregated_address === depositInfo.deposit_address && - coin.status === CoinStatus.TRANSFERRED - ); - - duplicatedCoin = listCoins.find(coin => - coin.aggregated_address === depositInfo.deposit_address && - coin.status === CoinStatus.DUPLICATED - ); - - expect(transferredCoin).to.not.be.null; - expect(transferredCoin.duplicate_index).to.equal(0); - - expect(duplicatedCoin).to.not.be.null; - expect(duplicatedCoin.duplicate_index).to.equal(1); - - try { - const withdrawAddress = "bcrt1qn5948np2j8t68xgpceneg3ua4wcwhwrsqj8scv"; - await mercurynodejslib.withdrawCoin(clientConfig, wallet1.name, duplicatedCoin.statechain_id, withdrawAddress, null, 1); - } catch (error) { - expect(error.response.status).to.equal(401); - expect(error.response.data.message).to.equal("Signature does not match authentication key."); - } - }) - }) -}) diff --git a/clients/apps/nodejs/test/tb04-simple-lightning-latch.mjs b/clients/apps/nodejs/test/tb04-simple-lightning-latch.mjs deleted file mode 100644 index 0ed4e187..00000000 --- a/clients/apps/nodejs/test/tb04-simple-lightning-latch.mjs +++ /dev/null @@ -1,594 +0,0 @@ -import { expect } from 'chai'; -import client_config from '../client_config.js'; -import mercurynodejslib from 'mercurynodejslib'; -import { CoinStatus } from 'mercurynodejslib/coin_enum.js'; -import crypto from 'crypto'; -import { sleep, createWallet, depositCoin, generateInvoice, payHoldInvoice, payInvoice, settleInvoice } from '../test_utils.js'; - -describe('TB04 - Lightning Latch', function() { - this.timeout(30000); - - context('Simple Transfer', () => { - it('should complete successfully', async () => { - - // await removeDatabase(); - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_1"; - let wallet_2_name = "w_ln_2"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - const tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - const usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - const listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin = listCoins[0]; - - expect(coin.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin.statechain_id); - - const transferAddress = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transferAddress.transfer_receive, false, paymentHash.batchId); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.true; - expect(transferReceiveResult.receivedStatechainIds).empty; - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).not.empty; - - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin.statechain_id, paymentHash.batchId); - - const hash = crypto.createHash('sha256') - .update(Buffer.from(preimage, 'hex')) - .digest('hex') - - expect(hash).to.equal(paymentHash.hash); - }) - }) - - context('The sender tries to get the pre-image before the batch is unlocked should fail', () => { - it('should complete successfully', async () => { - - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_3"; - let wallet_2_name = "w_ln_4"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let depositInfo = undefined; - let tokenList = undefined; - let usedToken = undefined; - let listCoins = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin1 = listCoins[0]; - - expect(coin1.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash1 = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin1.statechain_id); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_2_name, amount, depositInfo); - - listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - expect(listCoins.length).to.equal(1); - - const coin2 = listCoins[0]; - - expect(coin2.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash2 = await mercurynodejslib.paymentHash(clientConfig, wallet_2_name, coin2.statechain_id); - - const transferAddress1 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_1_name, null); - const transferAddress2 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transferAddress1.transfer_receive, false, paymentHash1.batchId); - await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transferAddress2.transfer_receive, false, paymentHash2.batchId); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_1_name); - - expect(transferReceiveResult.isThereBatchLocked).is.true; - expect(transferReceiveResult.receivedStatechainIds).empty; - - try { - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin1.statechain_id, paymentHash1.batchId); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'Request failed with status code 404'; - expect(error.message).to.equal(expectedMessage); - } - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin1.statechain_id); - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_2_name, coin2.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).not.empty; - - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin1.statechain_id, paymentHash1.batchId); - - const hash = crypto.createHash('sha256') - .update(Buffer.from(preimage, 'hex')) - .digest('hex') - - expect(hash).to.equal(paymentHash1.hash); - }) - }) - - context('Statecoin sender can recover (resend their coin) after batch timeout without completion', () => { - it('should complete successfully', async () => { - - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_5"; - let wallet_2_name = "w_ln_6"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let depositInfo = undefined; - let tokenList = undefined; - let usedToken = undefined; - let listCoins = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin1 = listCoins[0]; - - expect(coin1.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash1 = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin1.statechain_id); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_2_name, amount, depositInfo); - - listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - expect(listCoins.length).to.equal(1); - - const coin2 = listCoins[0]; - - expect(coin2.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash2 = await mercurynodejslib.paymentHash(clientConfig, wallet_2_name, coin2.statechain_id); - - const transferAddress1 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_1_name, null); - const transferAddress2 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transferAddress1.transfer_receive, false, paymentHash1.batchId); - await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transferAddress2.transfer_receive, false, paymentHash1.batchId); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_1_name); - - expect(transferReceiveResult.isThereBatchLocked).is.true; - expect(transferReceiveResult.receivedStatechainIds).empty; - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin1.statechain_id); - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_2_name, coin2.statechain_id); - - await sleep(20000); - - let errorMessage; - console.error = (msg) => { - errorMessage = msg; - }; - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - // Assert the captured error message - const expectedMessage = 'Failed to update transfer message'; - expect(errorMessage).contains(expectedMessage); - - const transferAddress3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_1_name, null); - const transferAddress4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transferAddress3.transfer_receive, false, paymentHash2.batchId); - await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transferAddress4.transfer_receive, false, paymentHash2.batchId); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_1_name); - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin1.statechain_id); - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_2_name, coin2.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).not.empty; - - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin1.statechain_id, paymentHash1.batchId); - - const hash = crypto.createHash('sha256') - .update(Buffer.from(preimage, 'hex')) - .digest('hex') - - expect(hash).to.equal(paymentHash1.hash); - }) - }) - - context('Statecoin trade with invoice creation, payment and settlement', () => { - it('should complete successfully', async () => { - - // await removeDatabase(); - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_7"; - let wallet_2_name = "w_ln_8"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - const tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - const usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - const listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin = listCoins[0]; - - expect(coin.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin.statechain_id); - - const invoice = await generateInvoice(paymentHash.hash, amount); - - payInvoice(invoice.payment_request); - - const transferAddress = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transferAddress.transfer_receive, false, paymentHash.batchId); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.true; - expect(transferReceiveResult.receivedStatechainIds).empty; - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).not.empty; - - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin.statechain_id, paymentHash.batchId); - - const hash = crypto.createHash('sha256') - .update(Buffer.from(preimage, 'hex')) - .digest('hex') - - expect(hash).to.equal(paymentHash.hash); - - await settleInvoice(preimage); - }) - }) - - context('Receiver tries to transfer invoice amount to another invoice before preimage retrieval should fail', () => { - it('should complete successfully', async () => { - - // await removeDatabase(); - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_9"; - let wallet_2_name = "w_ln_10"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 60000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - const tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - const usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - const listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin = listCoins[0]; - - expect(coin.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin.statechain_id); - - const invoice = await generateInvoice(paymentHash.hash, amount); - - payHoldInvoice(invoice.payment_request); - - const transferAddress = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transferAddress.transfer_receive, false, paymentHash.batchId); - - const hashFromServer = await mercurynodejslib.getPaymentHash(clientConfig, paymentHash.batchId); - - expect(hashFromServer).to.equal(paymentHash.hash); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.true; - expect(transferReceiveResult.receivedStatechainIds).empty; - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).not.empty; - - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin.statechain_id, paymentHash.batchId); - - const hash = crypto.createHash('sha256') - .update(Buffer.from(preimage, 'hex')) - .digest('hex') - - expect(hash).to.equal(paymentHash.hash); - - const paymentHashSecond = "b1f55a2f2eabb08ed9d6e15a053a6ac84d04d1c017de5a42caaec98b8d2ff738" - const invoiceSecond = await generateInvoice(paymentHashSecond, amount); - - try { - await payInvoice(invoiceSecond.payment_request); - } catch (error) { - console.error('Error:', error); - expect(error.message).to.include('failed'); - } - }) - }) - - context('Statecoin sender sends coin without batch_id (receiver should still be able to receive, but no pre-image revealed)', () => { - it('should complete successfully', async () => { - - // await removeDatabase(); - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_11"; - let wallet_2_name = "w_ln_12"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - const tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - const usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - const listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin = listCoins[0]; - - expect(coin.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin.statechain_id); - - const transferAddress = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transferAddress.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).not.empty; - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).is.empty; - - let hash; - try { - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin.statechain_id, paymentHash.batchId); - hash = crypto.createHash('sha256') - .update(Buffer.from(preimage, 'hex')) - .digest('hex') - expect(hash).to.equal(paymentHash.hash); - } catch (error) { - console.error('Error:', error); - expect(error.message).to.include('failed'); - } - }) - }) - - context('Sender sends coin without batch_id, and then resends to a different address (to attempt to steal), and then attempts to retrieve the pre-image, should fail (and LN payment cannot be claimed)', () => { - it('should complete successfully', async () => { - - // await removeDatabase(); - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_13"; - let wallet_2_name = "w_ln_14"; - let wallet_3_name = "w_ln_15"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - await createWallet(clientConfig, wallet_3_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - const tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - const usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - const listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin = listCoins[0]; - - expect(coin.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin.statechain_id); - - const transferAddress = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transferAddress.transfer_receive, false, null); - - const transferAddressSecond = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, null); - - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transferAddressSecond.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).not.empty; - - await mercurynodejslib.confirmPendingInvoice(clientConfig, wallet_1_name, coin.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - - expect(transferReceiveResult.isThereBatchLocked).is.false; - expect(transferReceiveResult.receivedStatechainIds).is.empty; - - let hash; - try { - const { preimage } = await mercurynodejslib.retrievePreImage(clientConfig, wallet_1_name, coin.statechain_id, paymentHash.batchId); - hash = crypto.createHash('sha256') - .update(Buffer.from(preimage, 'hex')) - .digest('hex') - expect(hash).to.equal(paymentHash.hash); - } catch (error) { - console.error('Error:', error); - expect(error.message).to.include('failed'); - } - }) - }) - - context('Coin receiver creates a non hold invoice, and sends to sender (i.e. an invoice with the a different payment hash). Sender should be able to determine this.', () => { - it('should complete successfully', async () => { - - // await removeDatabase(); - const clientConfig = client_config.load(); - let wallet_1_name = "w_ln_16"; - await createWallet(clientConfig, wallet_1_name); - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const depositInfo = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - const tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - const usedToken = tokenList.find(token => token.token_id === tokenId); - - expect(usedToken.spent).is.true; - - await depositCoin(clientConfig, wallet_1_name, amount, depositInfo); - - const listCoins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - expect(listCoins.length).to.equal(1); - - const coin = listCoins[0]; - - expect(coin.status).to.equal(CoinStatus.CONFIRMED); - - const paymentHash = await mercurynodejslib.paymentHash(clientConfig, wallet_1_name, coin.statechain_id); - - const paymentHashSecond = "a3b5f72d4e8cb07cd9a6e17c054a7ac84d05e1c018fe5b43cbbef98a9d3ff839" - const invoiceSecond = await generateInvoice(paymentHashSecond, amount); - - const isInvoiceValid = await mercurynodejslib.verifyInvoice(clientConfig, paymentHash.batchId, invoiceSecond.payment_request); - expect(isInvoiceValid).is.false; - }) - }) -}) diff --git a/clients/apps/nodejs/test/test-utils.mjs b/clients/apps/nodejs/test/test-utils.mjs deleted file mode 100644 index 1365bc11..00000000 --- a/clients/apps/nodejs/test/test-utils.mjs +++ /dev/null @@ -1,50 +0,0 @@ -import { promisify } from 'node:util'; -import { exec as execCallback } from 'node:child_process'; -import mercurynodejslib from 'mercurynodejslib'; -import { promises as fs } from 'fs'; - -const exec = promisify(execCallback); - -async function createWallet(clientConfig, walletName) { - - let wallet = await mercurynodejslib.createWallet(clientConfig, walletName); - return wallet; - - // TODO: add more assertions - } - - -async function removeDatabase(clientConfig) { - try { - await fs.unlink(clientConfig.databaseFile); - } catch (error) { - if (error.code !== 'ENOENT') { - console.error('Error occurred:', error); - } - // ENOENT means "No such file or directory", so we ignore this error - } -} - -async function getnewaddress() { - const generateBlockCommand = `docker exec $(docker ps -qf "name=lnd_docker-bitcoind-1") bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass getnewaddress`; - const { stdout, stderr } = await exec(generateBlockCommand); - if (stderr) { - console.error('Error:', stderr); - return null; - } - return stdout.trim(); -} - -async function generateBlock(numBlocks, address) { - const generateBlockCommand = `docker exec $(docker ps -qf "name=lnd_docker-bitcoind-1") bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass generatetoaddress ${numBlocks} ${address}`; - await exec(generateBlockCommand); -} - -async function depositCoin(deposit_address, amountInSats) { - const amountInBtc = amountInSats / 100000000; - - const sendBitcoinCommand = `docker exec $(docker ps -qf "name=lnd_docker-bitcoind-1") bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass sendtoaddress ${deposit_address} ${amountInBtc}`; - await exec(sendBitcoinCommand); -} - -export { createWallet, removeDatabase, getnewaddress, generateBlock, depositCoin }; \ No newline at end of file diff --git a/clients/apps/nodejs/test_atomic_swap.js b/clients/apps/nodejs/test_atomic_swap.js deleted file mode 100644 index 6156a89f..00000000 --- a/clients/apps/nodejs/test_atomic_swap.js +++ /dev/null @@ -1,856 +0,0 @@ -const assert = require('node:assert/strict'); -const mercurynodejslib = require('mercurynodejslib'); -const { CoinStatus } = require('mercurynodejslib/coin_enum'); -const { sleep, createWallet, generateBlock, depositCoin } = require('./test_utils'); -const client_config = require('./client_config'); - -async function atomicSwapSuccess(clientConfig, wallet_1_name, wallet_2_name, wallet_3_name, wallet_4_name) { - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let deposit_info = undefined; - let tokenList = undefined; - let usedToken = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin1 = undefined; - - console.log("coin: ", coin1); - - while (!coin1) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin1 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin1); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_2_name, amount, deposit_info); - - let coin2 = undefined; - - console.log("coin: ", coin2); - - while (!coin2) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin2 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin2); - - const generateBatchId = true; - - let transfer_address_w3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - let transfer_address_w4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, null); - - let coin3 = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin3); - - let coin4 = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin4); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - console.log("transferReceiveResult: ", transferReceiveResult); - assert(transferReceiveResult.isThereBatchLocked === true); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_4_name); - let received_statechain_ids_w4 = transferReceiveResult.receivedStatechainIds; - - console.log("received_statechain_ids: ", received_statechain_ids_w4); - - assert(received_statechain_ids_w4.length > 0); - assert(received_statechain_ids_w4[0] == coin4.statechain_id); - - // transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - // received_statechain_ids_w3 = transferReceiveResult.receivedStatechainIds; - - // console.log("received_statechain_ids: ", received_statechain_ids_w3); - - // assert(received_statechain_ids_w3.length > 0); - // assert(received_statechain_ids_w3[0] == coin3.statechain_id); -} - -async function atomicSwapWithSecondBatchIdMissing(clientConfig, wallet_1_name, wallet_2_name, wallet_3_name, wallet_4_name) { - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let deposit_info = undefined; - let tokenList = undefined; - let usedToken = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin1 = undefined; - - console.log("coin: ", coin1); - - while (!coin1) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin1 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin1); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_2_name, amount, deposit_info); - - let coin2 = undefined; - - console.log("coin: ", coin2); - - while (!coin2) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin2 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin2); - - const generateBatchId = true; - - let transfer_address_w3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - let transfer_address_w4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, null); - - let coin3 = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin3); - - transfer_address_w3.batchId = ""; - - let coin4 = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin4); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - let received_statechain_ids_w3 = transferReceiveResult.receivedStatechainIds; - - console.log("received_statechain_ids: ", received_statechain_ids_w3); - - assert(received_statechain_ids_w3.length > 0); - assert(received_statechain_ids_w3[0] == coin3.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_4_name); - let received_statechain_ids_w4 = transferReceiveResult.receivedStatechainIds; - - console.log("received_statechain_ids: ", received_statechain_ids_w4); - - assert(received_statechain_ids_w4.length > 0); - assert(received_statechain_ids_w4[0] == coin4.statechain_id); -} - -async function atomicSwapWithoutFirstBatchId(clientConfig, wallet_1_name, wallet_2_name, wallet_3_name, wallet_4_name) { - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let deposit_info = undefined; - let tokenList = undefined; - let usedToken = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin1 = undefined; - - console.log("coin: ", coin1); - - while (!coin1) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin1 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin1); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_2_name, amount, deposit_info); - - let coin2 = undefined; - - console.log("coin: ", coin2); - - while (!coin2) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin2 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin2); - - const generateBatchId = true; - - let transfer_address_w3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - let transfer_address_w4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, null); - - let coin3 = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3.transfer_receive, false, null); - console.log("coin transferSend: ", coin3); - - let coin4 = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin4); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - let received_statechain_ids_w3 = transferReceiveResult.receivedStatechainIds; - - console.log("received_statechain_ids: ", received_statechain_ids_w3); - - assert(received_statechain_ids_w3.length > 0); - assert(received_statechain_ids_w3[0] == coin3.statechain_id); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_4_name); - let received_statechain_ids_w4 = transferReceiveResult.receivedStatechainIds; - - console.log("received_statechain_ids: ", received_statechain_ids_w4); - - assert(received_statechain_ids_w4.length > 0); - assert(received_statechain_ids_w4[0] == coin4.statechain_id); -} - -async function atomicSwapWithTimeout(clientConfig, wallet_1_name, wallet_2_name, wallet_3_name, wallet_4_name) { - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let deposit_info = undefined; - let tokenList = undefined; - let usedToken = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin1 = undefined; - - console.log("coin: ", coin1); - - while (!coin1) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin1 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin1); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_2_name, amount, deposit_info); - - let coin2 = undefined; - - console.log("coin: ", coin2); - - while (!coin2) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin2 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin2); - - const generateBatchId = true; - - let transfer_address_w3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - let transfer_address_w4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, null); - - let coin3 = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin3); - - let coin4 = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin4); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - let received_statechain_ids_w3 = transferReceiveResult.receivedStatechainIds; - await sleep(20000); - - assert(transferReceiveResult.isThereBatchLocked === true); - - let errorMessage; - console.error = (msg) => { - errorMessage = msg; - }; - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_4_name); - let received_statechain_ids_w4 = transferReceiveResult.receivedStatechainIds; - - // Assert the captured error message - const expectedMessage = 'Failed to update transfer message'; - assert.ok(errorMessage.includes(expectedMessage)); - - transfer_address_w3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - transfer_address_w4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, null); - - coin3 = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin3); - - coin4 = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin4); - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - received_statechain_ids_w3 = transferReceiveResult.receivedStatechainIds; - - transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_4_name); - received_statechain_ids_w4 = transferReceiveResult.receivedStatechainIds; - - console.log("received_statechain_ids: ", received_statechain_ids_w4); - - assert(received_statechain_ids_w4.length > 0); - assert(received_statechain_ids_w4[0] == coin4.statechain_id); - - // transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - // received_statechain_ids_w3 = transferReceiveResult.receivedStatechainIds; - - // console.log("received_statechain_ids: ", received_statechain_ids_w3); - - // assert(received_statechain_ids_w3.length > 0); - // assert(received_statechain_ids_w3[0] == coin3.statechain_id); -} - -async function atomicSwapWithFirstPartySteal(clientConfig, wallet_1_name, wallet_2_name, wallet_3_name, wallet_4_name) { - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let deposit_info = undefined; - let tokenList = undefined; - let usedToken = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin1 = undefined; - - console.log("coin: ", coin1); - - while (!coin1) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin1 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin1); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_2_name, amount, deposit_info); - - let coin2 = undefined; - - console.log("coin: ", coin2); - - while (!coin2) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin2 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin2); - - const generateBatchId = true; - - let transfer_address_w3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - let transfer_address_w4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, null); - - let coin3 = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin3); - - let coin4 = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin4); - - let transfer_address_w3_for_steal = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - console.log("transfer address for steal", transfer_address_w3_for_steal); - - let coin_to_steal = undefined; - try { - coin_to_steal = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3_for_steal.transfer_receive, false, transfer_address_w3.batchId); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'expected a string argument, found undefined'; - assert.ok(error.message.includes(expectedMessage)); - } - - console.log("coin to steal transferSend: ", coin_to_steal); - - let received_statechain_ids_w3 = undefined; - try { - received_statechain_ids_w3 = mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'num_sigs is not correct'; - assert.ok(error.message.includes(expectedMessage)); - } - await sleep(3000); - - let received_statechain_ids_w4 = undefined; - try { - received_statechain_ids_w4 = await mercurynodejslib.transferReceive(clientConfig, wallet_4_name); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'num_sigs is not correct'; - assert.ok(error.message.includes(expectedMessage)); - } - - try { - received_statechain_ids_w3 = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'num_sigs is not correct'; - assert.ok(error.message.includes(expectedMessage)); - } -} - -async function atomicSwapWithSecondPartySteal(clientConfig, wallet_1_name, wallet_2_name, wallet_3_name, wallet_4_name) { - - const amount = 10000; - let token = undefined; - let tokenId = undefined; - let deposit_info = undefined; - let tokenList = undefined; - let usedToken = undefined; - - token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin1 = undefined; - - console.log("coin: ", coin1); - - while (!coin1) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin1 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin1); - - token = await mercurynodejslib.newToken(clientConfig, wallet_2_name); - tokenId = token.token_id; - - deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_2_name, amount); - - tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_2_name); - - usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_2_name, amount, deposit_info); - - let coin2 = undefined; - - console.log("coin: ", coin2); - - while (!coin2) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_2_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - generateBlock(1); - continue; - } - - coin2 = coinsWithStatechainId[0]; - break; - } - - console.log("coin: ", coin2); - - const generateBatchId = true; - - let transfer_address_w3 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_3_name, generateBatchId); - let transfer_address_w4 = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, null); - - let coin3 = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin1.statechain_id, transfer_address_w3.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin3); - - let coin4 = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4.transfer_receive, false, transfer_address_w3.batchId); - console.log("coin transferSend: ", coin4); - - let transfer_address_w4_for_steal = await mercurynodejslib.newTransferAddress(clientConfig, wallet_4_name, generateBatchId); - console.log("transfer address for steal", transfer_address_w4_for_steal); - - let coin_to_steal = undefined; - try { - coin_to_steal = await mercurynodejslib.transferSend(clientConfig, wallet_2_name, coin2.statechain_id, transfer_address_w4_for_steal.transfer_receive, false, transfer_address_w4.batchId); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'expected a string argument, found undefined'; - assert.ok(error.message.includes(expectedMessage)); - } - - console.log("coin to steal transferSend: ", coin_to_steal); - - let received_statechain_ids_w3 = undefined; - try { - received_statechain_ids_w3 = mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'num_sigs is not correct'; - assert.ok(error.message.includes(expectedMessage)); - } - - let received_statechain_ids_w4 = undefined; - try { - received_statechain_ids_w4 = await mercurynodejslib.transferReceive(clientConfig, wallet_4_name); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'num_sigs is not correct'; - assert.ok(error.message.includes(expectedMessage)); - } - - try { - received_statechain_ids_w3 = await mercurynodejslib.transferReceive(clientConfig, wallet_3_name); - } catch (error) { - // Assert the captured error message - const expectedMessage = 'num_sigs is not correct'; - assert.ok(error.message.includes(expectedMessage)); - } -} - -(async () => { - - try { - const clientConfig = client_config.load(); - - // Successful test - all transfers complete within batch_time complete. - let wallet_1_name = "w_atomic_1"; - let wallet_2_name = "w_atomic_2"; - let wallet_3_name = "w_atomic_3"; - let wallet_4_name = "w_atomic_4"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - await createWallet(clientConfig, wallet_3_name); - await createWallet(clientConfig, wallet_4_name); - await atomicSwapSuccess(clientConfig, wallet_1_name, wallet_2_name, wallet_3_name, wallet_4_name); - console.log("Completed test for Successful test - all transfers complete within batch_time complete."); - - // Second party performs transfer-sender with incorrect or missing batch-id. First party should still receive OK. - let wallet_5_name = "w_atomic_5"; - let wallet_6_name = "w_atomic_6"; - let wallet_7_name = "w_atomic_7"; - let wallet_8_name = "w_atomic_8"; - await createWallet(clientConfig, wallet_5_name); - await createWallet(clientConfig, wallet_6_name); - await createWallet(clientConfig, wallet_7_name); - await createWallet(clientConfig, wallet_8_name); - await atomicSwapWithSecondBatchIdMissing(clientConfig, wallet_5_name, wallet_6_name, wallet_7_name, wallet_8_name); - console.log("Completed test for Second party performs transfer-sender with incorrect or missing batch-id. First party should still receive OK."); - - // First party performs transfer-sender without batch_id. - let wallet_9_name = "w_atomic_9"; - let wallet_10_name = "w_atomic_10"; - let wallet_11_name = "w_atomic_11"; - let wallet_12_name = "w_atomic_12"; - await createWallet(clientConfig, wallet_9_name); - await createWallet(clientConfig, wallet_10_name); - await createWallet(clientConfig, wallet_11_name); - await createWallet(clientConfig, wallet_12_name); - await atomicSwapWithoutFirstBatchId(clientConfig, wallet_9_name, wallet_10_name, wallet_11_name, wallet_12_name); - console.log("Completed test for First party performs transfer-sender without batch_id."); - - // One party doesn't complete transfer-receiver before the timeout. - // Both wallets should be able to repeat transfer-sender and transfer-receiver back to new addresses without error, - // after the timeout. - let wallet_13_name = "w_atomic_13"; - let wallet_14_name = "w_atomic_14"; - let wallet_15_name = "w_atomic_15"; - let wallet_16_name = "w_atomic_16"; - await createWallet(clientConfig, wallet_13_name); - await createWallet(clientConfig, wallet_14_name); - await createWallet(clientConfig, wallet_15_name); - await createWallet(clientConfig, wallet_16_name); - await atomicSwapWithTimeout(clientConfig, wallet_13_name, wallet_14_name, wallet_15_name, wallet_16_name); - console.log("Completed test for One party doesn't complete transfer-receiver before the timeout."); - - // First party tries to steal within timeout - // they perform transfer-sender a second time sending back to one of their own addresses - should fail. - let wallet_17_name = "w_atomic_17"; - let wallet_18_name = "w_atomic_18"; - let wallet_19_name = "w_atomic_19"; - let wallet_20_name = "w_atomic_20"; - await createWallet(clientConfig, wallet_17_name); - await createWallet(clientConfig, wallet_18_name); - await createWallet(clientConfig, wallet_19_name); - await createWallet(clientConfig, wallet_20_name); - await atomicSwapWithFirstPartySteal(clientConfig, wallet_17_name, wallet_18_name, wallet_19_name, wallet_20_name); - console.log("Completed test for First party tries to steal within timeout"); - - // Second party tries to steal within timeout - // they perform transfer-sender a second time sending back to one of their own addresses - should fail. - let wallet_21_name = "w_atomic_21"; - let wallet_22_name = "w_atomic_22"; - let wallet_23_name = "w_atomic_23"; - let wallet_24_name = "w_atomic_24"; - await createWallet(clientConfig, wallet_21_name); - await createWallet(clientConfig, wallet_22_name); - await createWallet(clientConfig, wallet_23_name); - await createWallet(clientConfig, wallet_24_name); - await atomicSwapWithSecondPartySteal(clientConfig, wallet_21_name, wallet_22_name, wallet_23_name, wallet_24_name); - console.log("Completed test for Second party tries to steal within timeout"); - - process.exit(0); // Exit successfully - } catch (error) { - console.error("Test encountered an error:", error); - process.exit(1); // Exit with failure - } -})(); \ No newline at end of file diff --git a/clients/apps/nodejs/test_basic_workflow.js b/clients/apps/nodejs/test_basic_workflow.js deleted file mode 100644 index 35f7303c..00000000 --- a/clients/apps/nodejs/test_basic_workflow.js +++ /dev/null @@ -1,232 +0,0 @@ -const util = require('node:util'); -const exec = util.promisify(require('node:child_process').exec); -const assert = require('node:assert/strict'); -const { CoinStatus } = require('mercurynodejslib/coin_enum'); - -async function removeDatabase() { - try { - const { stdout, stderr } = await exec('rm wallet.db'); - // console.log('stdout:', stdout); - // console.error('stderr:', stderr); - } catch (e) { - // console.error(e); - } -} - -async function createWallet(wallet_name) { - const { stdout, stderr } = await exec(`node index.js create-wallet ${wallet_name}`); - let wallet = JSON.parse(stdout); - assert.equal(wallet.name, wallet_name); - // console.log('wallet:', wallet); -} - -async function newToken(wallet_name) { - const { stdout, stderr } = await exec(`node index.js new-token ${wallet_name}`); - let json = JSON.parse(stdout); - return json; -} - -async function listTokens(wallet_name) { - const { stdout, stderr } = await exec(`node index.js list-tokens ${wallet_name}`); - let json = JSON.parse(stdout); - return json; -} - -async function getDepositBitcoinAddress(wallet_name, token_id, amount) { - const { stdout, stderr } = await exec(`node index.js new-deposit-address ${wallet_name} ${token_id} ${amount}`); - let json = JSON.parse(stdout); - return json; -} - -async function listStatecoins(wallet_name) { - try { - const { stdout, stderr } = await exec(`node index.js list-statecoins ${wallet_name}`); - let json = JSON.parse(stdout); - return json; - } catch (e) { - console.log('e:', e); - return undefined; - } -} - -async function newTransferAddress(wallet_name) { - const { stdout, stderr } = await exec(`node index.js new-transfer-address ${wallet_name}`); - let json = JSON.parse(stdout); - return json.transfer_receive; -} - -async function transferSend(wallet_name, statechain_id, to_address) { - const { stdout, stderr } = await exec(`node index.js transfer-send ${wallet_name} ${statechain_id} ${to_address}`); - let json = JSON.parse(stdout); - return json; -} - -async function transferReceive(wallet_name) { - const { stdout, stderr } = await exec(`node index.js transfer-receive ${wallet_name}`); - let json = JSON.parse(stdout); - return json; -} - -async function withdraw(wallet_name, statechain_id, to_address) { - const { stdout, stderr } = await exec(`node index.js withdraw ${wallet_name} ${statechain_id} ${to_address}`); - let json = JSON.parse(stdout); - return json.txid; -} - -async function broadcastBackupTransaction(wallet_name, statechain_id, to_address) { - const { stdout, stderr } = await exec(`node index.js broadcast-backup-transaction ${wallet_name} ${statechain_id} ${to_address}`); - let json = JSON.parse(stdout); - return json; -} - -const sleep = (ms) => { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -async function walletTransfersToItselfAndWithdraw(wallet_name) { - - const amount = 10000; - - const token = await newToken(wallet_name); - const tokenId = token.token_id; - - const deposit_info = await getDepositBitcoinAddress(wallet_name, amount); - - let tokenList = await listTokens(wallet_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - deposit_info["amount"] = amount; - console.log("deposit_coin: ", deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await listStatecoins(wallet_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - continue; - } - - coin = coinsWithStatechainId[0]; - - break; - } - - console.log("coin: ", coin); - - for (let i = 0; i < 10; i++) { - - let transfer_address = await newTransferAddress(wallet_name); - - console.log("transfer_address: ", transfer_address); - - coin = await transferSend(wallet_name, coin.statechain_id, transfer_address); - - console.log("coin transferSend: ", coin); - - let received_statechain_ids = await transferReceive(wallet_name); - - console.log("received_statechain_ids: ", received_statechain_ids); - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - } - - let withdraw_address = "tb1qwrujs6f4gyexsextpf9p50smjtht7p7ypknteu"; - - let txid = await withdraw(wallet_name, coin.statechain_id, withdraw_address); - - console.log("txid: ", txid); - -}; - -async function walletTransfersMultipleTimesToItselfAndBroadcastsBackup(wallet_name) { - for (let i = 0; i < 3; i++) { - await walletTransfersToItselfAndWithdraw(wallet_name); - } -} - -async function walletTransfersToAnotherAndBroadcastsBackupTx(wallet_1_name, wallet_2_name) { - - const amount = 10000; - - const token = await newToken(wallet_1_name); - const tokenId = token.token_id; - - const deposit_info = await getDepositBitcoinAddress(wallet_1_name, amount); - - deposit_info["amount"] = amount; - console.log("deposit_info w1: ", deposit_info); - - let tokenList = await listTokens(wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - console.log("usedToken: ", usedToken); - - assert(usedToken.spent); - - let coin = undefined; - - while (!coin) { - const list_coins = await listStatecoins(wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(5000); - continue; - } - - coin = coinsWithStatechainId[0]; - - break; - } - - let transfer_address = await newTransferAddress(wallet_2_name); - - coin = await transferSend(wallet_1_name, coin.statechain_id, transfer_address); - - let received_statechain_ids = await transferReceive(wallet_2_name); - - console.log("received_statechain_ids: ", received_statechain_ids); - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - - let withdraw_address = "tb1qwrujs6f4gyexsextpf9p50smjtht7p7ypknteu"; - - let txids = await broadcastBackupTransaction(wallet_2_name, received_statechain_ids[0], withdraw_address); - - console.log("txids: ", txids); - -} - -(async () => { - let wallet_1_name = "w1"; - let wallet_2_name = "w2"; - - await removeDatabase(); - await createWallet(wallet_1_name); - await createWallet(wallet_2_name); - - await walletTransfersMultipleTimesToItselfAndBroadcastsBackup(wallet_1_name); - - await walletTransfersToAnotherAndBroadcastsBackupTx(wallet_1_name, wallet_2_name) - - await removeDatabase(); -})(); diff --git a/clients/apps/nodejs/test_basic_workflow2.js b/clients/apps/nodejs/test_basic_workflow2.js deleted file mode 100644 index faf64184..00000000 --- a/clients/apps/nodejs/test_basic_workflow2.js +++ /dev/null @@ -1,1072 +0,0 @@ -const assert = require('node:assert/strict'); -const mercurynodejslib = require('mercurynodejslib'); -const { CoinStatus } = require('mercurynodejslib/coin_enum'); -const client_config = require('./client_config'); -const sqlite_manager = require('../../libs/nodejs/sqlite_manager'); -const mercury_wasm = require('mercury-wasm'); -const transaction = require('../../libs/nodejs/transaction'); -const utils = require('../../libs/nodejs/utils'); -const { getDatabase, sleep, createWallet, getElectrumClient, generateBlock, depositCoin, connectElectr, disconnectElectr, disconnectMercuryServer, connectMercuryServer } = require('./test_utils'); - -async function walletTransfersToItselfAndWithdraw(clientConfig, wallet_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_name); - // console.log(list_coins); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - // console.log("Waiting for coin to be confirmed..."); - // console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - - break; - } - - for (let i = 0; i < 10; i++) { - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - } - - let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; - - let txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet_name, coin.statechain_id, withdraw_address, null, null); - - // TODO: confirm withdrawal status -} - -async function walletTransfersToItselfTillLocktimeReachesBlockHeightAndWithdraw(clientConfig, wallet_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - /* console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); */ - await sleep(5000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - - break; - } - - const electrumClient = await getElectrumClient(clientConfig); - - let block_header = await electrumClient.request('blockchain.headers.subscribe'); - let currentBlockHeight = block_header.height; - - while (coin.locktime <= currentBlockHeight) { - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - - // Fetch the coin again to get the updated locktime - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_name); - coin = list_coins.find(c => c.statechain_id === coin.statechain_id); - } - - let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; - - let txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet_name, coin.statechain_id, withdraw_address, null, null); - - // TODO: confirm withdrawal status -} - -async function walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, wallet_1_name, wallet_2_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - /* console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); */ - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - - let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; - - let txid = await mercurynodejslib.broadcastBackupTransaction(clientConfig, wallet_2_name, coin.statechain_id, withdraw_address, null); - - // TODO: confirm withdrawal status -} - -async function depositAndRepeatSend(clientConfig, wallet_1_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - // console.log("Waiting for coin to be confirmed..."); - // console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - for (let i = 0; i < 10; i++) { - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_1_name, null); - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_1_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_1_name, null); - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_1_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); -} - -async function transferSenderAfterTransferReceiver(clientConfig, wallet_1_name, wallet_2_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - // console.log("Waiting for coin to be confirmed..."); - // console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - - try { - transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - assert.fail("Expected error when transferring from wallet one again, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("Coin status must be CONFIRMED or IN_TRANSFER to transfer it. The current status is TRANSFERRED"), - `Unexpected error message: ${error.message}`); - } -} - -async function depositAndTransfer(clientConfig, wallet_name) { - - for (let i = 0; i < 10; i++) { - const token = await mercurynodejslib.newToken(clientConfig, wallet_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - // console.log("Waiting for coin to be confirmed..."); - // console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - - break; - } - } - - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_name); - - for (let coin of list_coins) { - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - } -} - -async function interruptBeforeSignFirst(clientConfig, wallet_1_name, wallet_2_name) { - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - // console.log("Waiting for coin to be confirmed..."); - // console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - console.log("Disconnect mercurylayer-mercury-1 from network"); - await disconnectMercuryServer(); - - try { - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - assert.fail("Expected error when transferring from wallet one, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("connect ECONNREFUSED 0.0.0.0:8000"), - `Unexpected error message: ${error.message}`); - } - console.log("Connect mercurylayer-mercury-1 from network"); - await connectMercuryServer(); -} - -const new_transaction = async(clientConfig, electrumClient, coin, toAddress, isWithdrawal, qtBackupTx, block_height, network) => { - let coin_nonce = mercury_wasm.createAndCommitNonces(coin); - - let server_pubnonce = await transaction.signFirst(clientConfig, coin_nonce.sign_first_request_payload); - - coin.secret_nonce = coin_nonce.secret_nonce; - coin.public_nonce = coin_nonce.public_nonce; - coin.server_public_nonce = server_pubnonce; - coin.blinding_factor = coin_nonce.blinding_factor; - - const serverInfo = await utils.infoConfig(clientConfig, electrumClient); - - let new_block_height = 0; - if (block_height == null) { - const block_header = await electrumClient.request('blockchain.headers.subscribe'); // request(promise) - new_block_height = block_header.height; - } else { - new_block_height = block_height; - } - - const initlock = serverInfo.initlock; - const interval = serverInfo.interval; - const feeRateSatsPerByte = serverInfo.fee_rate_sats_per_byte; - - let partialSigRequest = mercury_wasm.getPartialSigRequest( - coin, - new_block_height, - initlock, - interval, - feeRateSatsPerByte, - qtBackupTx, - toAddress, - network, - isWithdrawal); - - const serverPartialSigRequest = partialSigRequest.partial_signature_request_payload; - - console.log("Disconnect mercurylayer-mercury-1 from network"); - await disconnectMercuryServer(); - - let serverPartialSig; - - try { - serverPartialSig = await transaction.signSecond(clientConfig, serverPartialSigRequest); - assert.fail("Expected error when signing second transaction, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("Server partial signature is not available."), - `Unexpected error message: ${error.message}`); - } - - console.log("Connect mercurylayer-mercury-1 from network"); - await connectMercuryServer(); -} - -async function interruptBeforeSignSecond(clientConfig, wallet_1_name, wallet_2_name) { - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coinDeposited = undefined; - - while (!coinDeposited) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - // console.log("Waiting for coin to be confirmed..."); - // console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(1000); - generateBlock(1); - continue; - } - - coinDeposited = coinsWithStatechainId[0]; - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - const db = await getDatabase(clientConfig); - - const electrumClient = await getElectrumClient(clientConfig); - - let options = transfer_address; - - let batchId = (options && options.batch_id) || null; - - let wallet = await sqlite_manager.getWallet(db, wallet_1_name); - - const backupTxs = await sqlite_manager.getBackupTxs(db, coinDeposited.statechain_id); - - if (backupTxs.length === 0) { - throw new Error(`There is no backup transaction for the statechain id ${coinDeposited.statechain_id}`); - } - - const new_tx_n = backupTxs.length + 1; - - let coinsWithStatechainId = wallet.coins.filter(c => { - return c.statechain_id === coinDeposited.statechain_id - }); - - if (!coinsWithStatechainId || coinsWithStatechainId.length === 0) { - throw new Error(`There is no coin for the statechain id ${coinDeposited.statechain_id}`); - } - - // If the user sends to himself, he will have two coins with same statechain_id - // In this case, we need to find the one with the lowest locktime - // Sort the coins by locktime in ascending order and pick the first one - let coin = coinsWithStatechainId.sort((a, b) => a.locktime - b.locktime)[0]; - - if (coin.status != CoinStatus.CONFIRMED && coin.status != CoinStatus.IN_TRANSFER) { - throw new Error(`Coin status must be CONFIRMED or IN_TRANSFER to transfer it. The current status is ${coin.status}`); - } - - if (coin.locktime == null) { - throw new Error("Coin.locktime is null"); - } - - const blockHeader = await electrumClient.request('blockchain.headers.subscribe'); // request(promise) - const currentBlockheight = blockHeader.height; - - if (currentBlockheight > coin.locktime) { - throw new Error(`The coin is expired. Coin locktime is ${coin.locktime} and current blockheight is ${currentBlockheight}`); - } - - const statechain_id = coin.statechain_id; - const signed_statechain_id = coin.signed_statechain_id; - - const isWithdrawal = false; - const qtBackupTx = backupTxs.length; - - backupTxs.sort((a, b) => a.tx_n - b.tx_n); - - const bkp_tx1 = backupTxs[0]; - - const block_height = mercury_wasm.getBlockheight(bkp_tx1); - - const decodedTransferAddress = mercury_wasm.decodeTransferAddress(transfer_address.transfer_receive); - const new_auth_pubkey = decodedTransferAddress.auth_pubkey; - - // const new_x1 = await get_new_x1(clientConfig, statechain_id, signed_statechain_id, new_auth_pubkey, batchId); - - const signed_tx = await new_transaction(clientConfig, electrumClient, coin, transfer_address.transfer_receive, isWithdrawal, qtBackupTx, block_height, wallet.network); - - transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - console.log("coin ", coin); - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - console.log("received_statechain_ids: ", received_statechain_ids); - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); - - // Coin withdrawal - let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; - - let txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet_2_name, coin.statechain_id, withdraw_address, null, null); - - console.log("txid: ", txid); -} - -async function interruptSignWithElectrumUnavailability(clientConfig, wallet_1_name, wallet_2_name) { - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - // console.log("Waiting for coin to be confirmed..."); - // console.log(`Check the address ${deposit_info.deposit_address} ...\n`); - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - await sleep(5000); // wait for Electrum to disconnect - - console.log("Disconnect mercurylayer-electrs-1 from network"); - await disconnectElectr(); - - try { - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - assert.fail("Expected error when transferring from wallet one, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("connect ECONNREFUSED 0.0.0.0:50001"), - `Unexpected error message: ${error.message}`); - } - console.log("Connect mercurylayer-electrs-1 from network"); - await connectElectr(); - - await sleep(5000); // wait for Electrum to connect -} - -async function interruptTransferReceiveWithElectrumUnavailability(clientConfig, wallet_1_name, wallet_2_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - /* console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); */ - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - await sleep(5000); // wait for Electrum to disconnect - - console.log("Disconnect mercurylayer-electrs-1 from network"); - await disconnectElectr(); - - try { - await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - assert.fail("Expected error when receiving into wallet two, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("connect ECONNREFUSED 0.0.0.0:50001"), - `Unexpected error message: ${error.message}`); - } - console.log("Connect mercurylayer-electrs-1 from network"); - await connectElectr(); - - await sleep(5000); // wait for Electrum to connect -} - -async function interruptTransferReceiveWithMercuryServerUnavailability(clientConfig, wallet_1_name, wallet_2_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - /* console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); */ - await sleep(5000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - console.log("Disconnect mercurylayer-mercury-1 from network"); - await disconnectMercuryServer(); - - try { - let received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - assert.fail("Expected error when receiving into wallet two, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("connect ECONNREFUSED 0.0.0.0:8000"), - `Unexpected error message: ${error.message}`); - } - console.log("Connect mercurylayer-mercury-1 from network"); - await connectMercuryServer(); -} - -async function transferSendAtCoinExpiry(clientConfig, wallet_1_name, wallet_2_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - /* console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); */ - await sleep(1000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - const electrumClient = await getElectrumClient(clientConfig); - - const blockHeader = await electrumClient.request('blockchain.headers.subscribe'); // request(promise) - const currentBlockheight = blockHeader.height; - - const blocksToBeGenerated = coin.locktime - currentBlockheight; - await generateBlock(blocksToBeGenerated); - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - try { - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - assert.fail("Expected error when transferring expired coin, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("The coin is expired."), - `Unexpected error message: ${error.message}`); - } -} - -async function transferReceiveAtCoinExpiry(clientConfig, wallet_1_name, wallet_2_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - /* console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); */ - await sleep(5000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - - const electrumClient = await getElectrumClient(clientConfig); - - const blockHeader = await electrumClient.request('blockchain.headers.subscribe'); // request(promise) - const currentBlockheight = blockHeader.height; - - const blocksToBeGenerated = coin.locktime - currentBlockheight; - await generateBlock(blocksToBeGenerated); - - let errorMessage; - console.error = (msg) => { - errorMessage = msg; - }; - - let transferReceiveResult = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name); - let received_statechain_ids = transferReceiveResult.receivedStatechainIds; - - // Assert the captured error message - const expectedMessage = 'The coin is expired.'; - assert.ok(errorMessage.includes(expectedMessage)); - - assert(received_statechain_ids.length > 0); - assert(received_statechain_ids[0] == coin.statechain_id); -} - -async function transferSendCoinExpiryBySending(clientConfig, wallet_1_name, wallet_2_name) { - - const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); - const tokenId = token.token_id; - - const amount = 10000; - const deposit_info = await mercurynodejslib.getDepositBitcoinAddress(clientConfig, wallet_1_name, amount); - - let tokenList = await mercurynodejslib.getWalletTokens(clientConfig, wallet_1_name); - - let usedToken = tokenList.find(token => token.token_id === tokenId); - - assert(usedToken.spent); - - await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - - let coin = undefined; - - while (!coin) { - const list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_1_name); - - let coinsWithStatechainId = list_coins.filter(c => { - return c.statechain_id === deposit_info.statechain_id && c.status === CoinStatus.CONFIRMED; - }); - - if (coinsWithStatechainId.length === 0) { - /* console.log("Waiting for coin to be confirmed..."); - console.log(`Check the address ${deposit_info.deposit_address} ...\n`); */ - await sleep(5000); - generateBlock(1); - continue; - } - - coin = coinsWithStatechainId[0]; - break; - } - - const electrumClient = await getElectrumClient(clientConfig); - - const blockHeader = await electrumClient.request('blockchain.headers.subscribe'); // request(promise) - const currentBlockheight = blockHeader.height; - - const serverInfo = await utils.infoConfig(clientConfig, electrumClient); - - const blocksToBeGenerated = coin.locktime - currentBlockheight - serverInfo.interval; - await generateBlock(blocksToBeGenerated); - - let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); - - try { - coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive, false, null); - assert.fail("Expected error when transferring expired coin, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("The coin is expired."), - `Unexpected error message: ${error.message}`); - } -} - -(async () => { - - try { - const clientConfig = client_config.load(); - - let wallet_1_name = "w1"; - let wallet_2_name = "w2"; - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); - await walletTransfersToItselfAndWithdraw(clientConfig, wallet_1_name); - // await walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, wallet_1_name, wallet_2_name); - - - // Deposit, repeat send - let wallet_3_name = "w3"; - let wallet_4_name = "w4"; - await createWallet(clientConfig, wallet_3_name); - await createWallet(clientConfig, wallet_4_name); - await depositAndRepeatSend(clientConfig, wallet_3_name); - console.log("Completed test for Deposit, repeat send"); - - // Transfer-sender after transfer-receiver - let wallet_5_name = "w5"; - let wallet_6_name = "w6"; - await createWallet(clientConfig, wallet_5_name); - await createWallet(clientConfig, wallet_6_name); - await transferSenderAfterTransferReceiver(clientConfig, wallet_5_name, wallet_6_name); - console.log("Completed test for Transfer-sender after transfer-receiver"); - - // Deposit of 10 coins in same wallet, and transfer each one 10 times - let wallet_7_name = "w7"; - await createWallet(clientConfig, wallet_7_name); - await depositAndTransfer(clientConfig, wallet_7_name); - console.log("Completed test for Deposit of 100 coins in same wallet, and transfer each one 100 times"); - - // Test for interruption of transferSend before sign first - let wallet_8_name = "w8"; - let wallet_9_name = "w9"; - await createWallet(clientConfig, wallet_8_name); - await createWallet(clientConfig, wallet_9_name); - await interruptBeforeSignFirst(clientConfig, wallet_8_name, wallet_9_name); - console.log("Completed test for interruption of transferSend before sign first"); - - // Test for interruption of transferSend before sign second - let wallet_10_name = "w10"; - let wallet_11_name = "w11"; - await createWallet(clientConfig, wallet_10_name); - await createWallet(clientConfig, wallet_11_name); - await interruptBeforeSignSecond(clientConfig, wallet_10_name, wallet_11_name); - console.log("Completed test for interruption of transferSend before sign second"); - - // Test for interruption of sign with Electrum unavailability - let wallet_12_name = "w12"; - let wallet_13_name = "w13"; - await createWallet(clientConfig, wallet_12_name); - await createWallet(clientConfig, wallet_13_name); - await interruptSignWithElectrumUnavailability(clientConfig, wallet_12_name, wallet_13_name); - console.log("Completed test for interruption of sign with Electrum unavailability"); - - // Test for interruption of transfer receive with Electrum unavailability - let wallet_14_name = "w14"; - let wallet_15_name = "w15"; - await createWallet(clientConfig, wallet_14_name); - await createWallet(clientConfig, wallet_15_name); - await interruptTransferReceiveWithElectrumUnavailability(clientConfig, wallet_14_name, wallet_15_name); - console.log("Completed test for interruption of transfer receive with Electrum unavailability"); - - // Test for interruption of transfer receive with mercury server unavailability - let wallet_16_name = "w16"; - let wallet_17_name = "w17"; - await createWallet(clientConfig, wallet_16_name); - await createWallet(clientConfig, wallet_17_name); - await interruptTransferReceiveWithMercuryServerUnavailability(clientConfig, wallet_16_name, wallet_17_name); - console.log("Completed test for interruption of transfer receive with mercury server unavailability"); - - // Deposit, iterative self transfer - let wallet_18_name = "w18"; - await createWallet(clientConfig, wallet_18_name); - await walletTransfersToItselfTillLocktimeReachesBlockHeightAndWithdraw(clientConfig, wallet_18_name); - console.log("Completed test for Deposit, iterative self transfer"); - - // Send backup tx before expiry - let wallet_19_name = "w19"; - let wallet_20_name = "w20"; - await createWallet(clientConfig, wallet_19_name); - await createWallet(clientConfig, wallet_20_name); - try { - await walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, wallet_19_name, wallet_20_name) - assert.fail("Expected error when sending backup tx before expiry, but no error was thrown"); - } catch (error) { - console.log("Expected error received: ", error.message); - assert(error.message.includes("The coin is not expired yet."), - `Unexpected error message: ${error.message}`); - } - console.log("Completed test for send backup tx before expiry"); - - // Transfer-sender of coin at expiry - let wallet_21_name = "w21"; - let wallet_22_name = "w22"; - await createWallet(clientConfig, wallet_21_name); - await createWallet(clientConfig, wallet_22_name); - await transferSendAtCoinExpiry(clientConfig, wallet_21_name, wallet_22_name); - console.log("Completed test for Transfer-sender of coin at expiry"); - - // Transfer-receive of coin at expiry - let wallet_23_name = "w23"; - let wallet_24_name = "w24"; - await createWallet(clientConfig, wallet_23_name); - await createWallet(clientConfig, wallet_24_name); - await transferReceiveAtCoinExpiry(clientConfig, wallet_23_name, wallet_24_name); - console.log("Completed test for Transfer-receive of coin at expiry"); - - // Transfer-sender of coin that will make it expired by sending - let wallet_25_name = "w25"; - let wallet_26_name = "w26"; - await createWallet(clientConfig, wallet_25_name); - await createWallet(clientConfig, wallet_26_name); - await transferSendCoinExpiryBySending(clientConfig, wallet_25_name, wallet_26_name); - console.log("Completed test for Transfer-sender of coin that will make it expired by sending"); - - process.exit(0); // Exit successfully - } catch (error) { - console.error("Test encountered an error:", error); - process.exit(1); // Exit with failure - } -})(); diff --git a/docker-compose-test.yml b/docker-compose-test.yml index c629574c..f7b8e847 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -70,6 +70,22 @@ services: depends_on: - postgres + token-server: + build: + context: . + dockerfile: ./token-server/Dockerfile + environment: + PROCESSOR_URL: https://api.swiss-bitcoin-pay.ch + API_KEY: aaaa + FEE: 0.0001 + UNIT: BTC + DELAY: 3600 + CONNECTION_STRING: postgres://postgres:pgpassword@postgres:5432/postgres + ports: + - "8001:8001" + depends_on: + - postgres + alice: image: lightninglabs/lndinit:v0.1.21-beta-lnd-v0.18.0-beta container_name: mercurylayer-alice-1 diff --git a/server/migrations/0001_key_data_table.sql b/server/migrations/0001_key_data_table.sql index 68b39bab..082df6cd 100644 --- a/server/migrations/0001_key_data_table.sql +++ b/server/migrations/0001_key_data_table.sql @@ -55,5 +55,6 @@ CREATE TABLE public.tokens ( onchain_address varchar NULL, processor_id varchar NULL, confirmed boolean DEFAULT false, - spent boolean DEFAULT false + spent boolean DEFAULT false, + accepted boolean DEFAULT false ); \ No newline at end of file diff --git a/token-server/src/endpoints/token.rs b/token-server/src/endpoints/token.rs index ce4795f4..7544b0b4 100644 --- a/token-server/src/endpoints/token.rs +++ b/token-server/src/endpoints/token.rs @@ -99,12 +99,41 @@ pub struct PODStatus { } -#[get("/token/token_init")] -pub async fn token_init(token_server: &State) -> status::Custom> { +#[get("/token/token_init/")] +pub async fn token_init(token_server: &State, token_id: String) -> status::Custom> { + + set_tnc_accepted(&token_server.pool, &token_id).await; + + let row = sqlx::query( + "SELECT token_id, invoice, onchain_address, processor_id \ + FROM public.tokens \ + WHERE token_id = $1") + .bind(&token_id) + .fetch_one(&token_server.pool) + .await; + + let row = row.unwrap(); + + let pod_info = PODInfo { + token_id: row.get(0), + fee: token_server.config.fee.clone(), + lightning_invoice: row.get(1), + btc_payment_address: row.get(2), + processor_id: row.get(3), + }; + + let response_body = json!(pod_info); + + return status::Custom(Status::Ok, Json(response_body)); +} + +#[get("/token/token_gen")] +pub async fn token_gen(token_server: &State) -> status::Custom> { let token_id = uuid::Uuid::new_v4().to_string(); let invoice: Invoice = get_lightning_invoice(token_server, token_id.clone()).await; + let pod_info = PODInfo { token_id: token_id.clone(), fee: token_server.config.fee.clone(), @@ -115,7 +144,12 @@ pub async fn token_init(token_server: &State) -> status::Custom for ServerConfig { fee: config.get::("fee").unwrap_or_else(|_| String::new()), delay: config.get::("delay").unwrap_or(0), connection_string: config.get::("connection_string").unwrap_or_else(|_| String::new()), + tnc: fs::read_to_string("tnc.html").unwrap_or_else(|_| String::from("")), } } } @@ -73,6 +77,7 @@ impl ServerConfig { fee: get_env_or_config("fee", "FEE"), delay: get_env_or_config("delay", "DELAY").parse::().unwrap(), connection_string: get_env_or_config("connection_string", "CONNECTION_STRING"), + tnc: fs::read_to_string("tnc.html").unwrap_or_else(|_| String::from("")), } } } diff --git a/token-server/tnc.html b/token-server/tnc.html new file mode 100644 index 00000000..d96e0a0c --- /dev/null +++ b/token-server/tnc.html @@ -0,0 +1,85 @@ +Users of the api.mercurylayer.com blind key service should carefully read the terms of use detailed below. By completing token_init with the supplied token_id or accessing our services a User agrees to be bound by these Terms of Use. These Terms of use are not assignable by the user to anyone else. + +Mercury layer is an implementation of a system the uses a blind co-signing and key-update service that enables statechains. +The Mercury blind key service generates and updates key shares on request in addition to partial blinded signatures. The blind key-update server never has control or custody, and is never aware of the identity of any shared key. + +Mercury layer service: + +The mercury layer service generates a private key share s1 on initialisation of a session. In order to initialise a session, a client must provide a valid token_id which controls access to the service. + +The client then initialises a session with an auth_pubkey which is used to authenticate all subsequent messages with the server. The server responds with the public key corresponding to the session private key share s1. + +Once initialised, the server can then perform two operations using the key share: + +Partial signature generation - the server uses the key share to compute a partial signature from a blinded challenge value provided by the client. +Key update - an encrypted value is sent to the server and used to update the key share along with a new auth_pubkey for the updated key. The previous key share is then deleted securely. +The server does not ever receive any other information regarding the client state. + +Token ID Generation: + +Valid Token IDs (token_id) can be generated by first initialising a token_id (GET api.mercurylayer.com/token/token_gen) and then agreeing to the terms of use (GET api.mercurylayer.com/token/token_init/). +This returns a JSON object that specifies an amount (in EUR) required to be paid in BTC via a Lightning invoice or on-chain. The JSON object contains the invoice and swiss-bitcoin-pay.ch invoice ID (the invoice can be viewed at api.swiss-bitcoin-pay.ch/checkout/). +Once the invoice has been paid (via the Lightning Network or on-chain) the token_id is verified (GET api.mercurylayer.com/token/token_verify/). +The token_id is now valid and can be used to generate a blind key share 'session' (see docs.mercurylayer.com for API details). + +The 'session' will maintain the key share privately for a period of 12 months, or until the session is closed by the user. +The session will enable the generation of up to 10,000 blind partial signatures and up to 10,000 key update operations. + +Security and User responsibility: + +All services provided by Mercury Layer are on a non-custodial basis. The Mercury service software does not have access to, or the ability to decrypt or access a user’s wallet. +Mercury Layer service does not keep or store User’s keys. The safekeeping of User’s keys is the sole responsibility of the User. +Mercury Layer service will not ask for recovery words, passwords or similar security-related information. Users should never provide any security-critical information to any person purporting to represent Commerceblock, its affiliates or partners. +The user bears the sole responsibility for performing the broadcast of backup transactions. Commerceblock will not be held responsible for failures to perform this function on time. +Users use this service in their own discretion and in compliance with all applicable laws and regulations. Users assume full responsibility for the consequences of their use of the service, including the safekeeping of their recovery words, passwords and stored keys, and user uses the services at their own risk. +Mercury Layer service is unable to monitor or regulate the User’s use of the services, due to the nature of the blinded cryptography employed. + +User warranties: +The User confirms and warrants that: + +- Where they are representing a legal entity or company, that they have the authorisation and power to bind the legal entity or company to the terms of service agreement and/or to use this service on behalf of that entity or company. +- That they have legal capacity to accept these terms and conditions and enter into this agreement. The service is not available to persons under the age of 18 years old. Users who do not qualify are not permitted to use the Mercury Layer service unless specifically permitted to contract under their applicable domestic law. +- They are using the services in compliance with their own local laws and jurisdiction. +- The user is aware that national taxation laws may apply to Bitcoin transactions. The User undertakes sole responsibility for determining, calculating and paying any taxes or duties applicable to their transactions under the laws or regulations applicable to their use of the service. Commerceblock Limited will not calculate, determine, pay, collect any revenue on behalf of any national government or user. +- Users assume use of the service at their own risk. Commerceblock is not liable for the actions, decisions or other behaviour of the user taken in reliance on information provided by third party services. + +Prohibited Activities: + +Notwithstanding the generality of the foregoing, the User agrees to use Mercury Layer service in a lawful manner and to refrain from any activity which hinders or otherwise adversely affects the performance of or the provision by Commerceblock of its services to the User or any other users. +The User undertakes not to "Reverse Engineer", change, modify, hack, translate, copy, distribute, pledge, assign, transfer, make derivative works, exploit, encumber or crack any portion of the service. +The User specifically agrees to defend, indemnify and hold harmless Commerceblock against any and all claims, costs, losses, damages, liability, judgments and expenses (including without limitation costs and reasonable attorneys’ fees) in connection with or arising from or in any way related to the use of the Mercury Layer service by the User in breach of these Terms of Use or use of the Mercury Layer service. +The User undertakes to inform Commerceblock immediately, or as early as possible, of any use of the Mercury Software by it, whether advertent or inadvertent which may negatively affect the rights of or cause any loss or damage to, any other person or entity. + +Fees for services: + +The fee charged for the use of the Mercury Layer service is taken in advance via swiss-bitcoin-pay.ch. +The User will be liable for transactions fees charged by the Bitcoin network, a third party separate of Commerceblock. + +Inapplicability of financial regulation law: + +Commerceblock limited is not a financial institution and does not provide banking services to the User. +Commerceblock limited does not perform any acts as a bank, including but not limited to receiving money deposits, extending credit facilities, investment of own or User funds, buying and selling money market instruments, providing money transmission services, buying and selling foreign currency, issuing and administering means of payment, money brokering, safekeeping and administration of valuables, providing portfolio management or advice or in any other way acting as a custodian, possessor or guardian, exchange or financial intermediary of or for the User’s Bitcoins. +The Mercury Layer Service does not have control or custody over the funds and does not possess, store or transmit value belonging to others. +The User accepts full knowledge that Commerceblock Limited is not performing the functions of a financial institution or bank and that the transactions undertaken using the Mercury Layer service are exempt from the provisions of law applying to financial institutions. + +Assumption of liability: + +The User assumes the full risk and conditions of liability attaching to the use of the Mercury Layer service. +TO THE MAXIMUM EXTENT PERMITTED BY LAW, UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, DELICT/TORT, CONTRACT OR OTHERWISE, SHALL COMMERCEBLOCK LIMITED, OR ANY OF ITS UNDERLYING SERVICE PROVIDERS, BUSINESS PARTNERS, INFORMATION PROVIDERS, ACCOUNT PROVIDERS, LICENSORS, OFFICERS, DIRECTORS, EMPLOYEES, DISTRIBUTORS OR AGENTS; BE LIABLE TO THE USER OR ANY OTHER PERSON FOR ANY MONEY DAMAGES, WHETHER DIRECT, INDIRECT, SPECIAL, INCIDENTAL, COVER, RELIANCE OR CONSEQUENTIAL DAMAGES, EVEN IF COMMERCEBLOCK LIMITED SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. +IN THE EVENT THAT, NOTWITHSTANDING THE FOREGOING, COMMERCEBLOCK LIMITED IS FOUND LIABLE TO THE USER FOR DAMAGES FROM ANY CAUSE WHATSOEVER, AND REGARDLESS OF THE FORM OF THE ACTION [WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE), DELICT, SOFTWARE LIABILITY OR OTHERWISE], THE MAXIMUM AGGREGATE LIABILITY OF COMMERCEBLOCK LIMITED TO THE USER ARISING IN CONNECTION WITH THIS AGREEMENT SHALL BE LIMITED TO THE AMOUNT THE USER PAID FOR THE SERVICE IN THE TWELVE (12) MONTHS PRIOR TO THE ACCRUAL OF THE APPLICABLE CLAIM, LESS ANY DAMAGES PREVIOUSLY PAID BY COMMERCEBLOCK LIMITED TO THE USER IN THAT TWELVE (12) MONTH PERIOD. + +These Terms of Use constitute the entire agreement (complete and exclusive) between Commerceblock Limited and the User and supersede or replace any previous or contemporaneous terms or agreements, representations, warranties or understandings, whether written or oral. +If any provision of the Agreement is found to be unenforceable or invalid, that provision will be limited or eliminated to the minimum extent necessary so that this Agreement will otherwise remain in full force, in effect and enforceable. + +Assignment: + +The rights and obligations arising under these Terms and Conditions may be assigned to and become binding on the parties’ parent companies, subsidiaries, or legal affiliates and will survive a merger, consolidation, or sale of Commerceblock Limited’s assets. + +No waiver: + +Delayed, late or failed performance by Commerceblock Limited to exercise or enforce any right or remedy (or any part thereof) accruing to it under these Terms of Use or by law does not constated a waiver of that or any other right or remedy. Commerceblock Limited will not be precluded from further exercising that or any other lawful right or remedy. + +Governing law and choice of jurisdiction: + +Any dispute or claim arising out of or in connection with the use of Commerceblock Limited’s services, including disputes related to or arising from these Terms of Use, shall be governed exclusively by UK law and the courts of UK shall have exclusive jurisdiction to hear and determine any such disputes or claims. +Commerceblock Limited will not be held liable for actions of its users in breach of foreign laws. End users are solely responsible for ensuring that they comply with the laws of the country in which they are resident, domiciled or otherwise present.