diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 77db1907..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Build/release Electron app - -on: - pull_request: - branches: - - dev - push: - branches: - - main - tags: - - v*.*.* - -jobs: - release: - permissions: write-all - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - - steps: - - name: Check out Git repository - uses: actions/checkout@v3 - - - name: Install Node.js - uses: actions/setup-node@v3 - with: - node-version: 18 - - - name: Install Dependencies - working-directory: "./clients/react-app" - run: npm install - - - name: build-linux - if: matrix.os == 'ubuntu-latest' - working-directory: "./clients/react-app" - run: | - pwd - id - npm run build:linux - - - name: build-win - if: matrix.os == 'windows-latest' - working-directory: "./clients/react-app" - run: npm run build:win - - - name: release - uses: softprops/action-gh-release@v1 - with: - draft: true - files: | - ./clients/react-app/dist/*.exe - ./clients/react-app/dist/*.zip - ./clients/react-app/dist/*.dmg - ./clients/react-app/dist/*.AppImage - ./clients/react-app/dist/*.snap - ./clients/react-app/dist/*.deb - ./clients/react-app/dist/*.rpm - ./clients/react-app/dist/*.tar.gz - ./clients/react-app/dist/*.yml - ./clients/react-app/dist/*.blockmap diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..54a7577d --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,117 @@ +name: Integration Tests + +on: + push: + branches: + - dev + pull_request: + branches: + - dev + +jobs: + test: + runs-on: ubuntu-latest + + services: + docker: + image: docker:19.03.12 + options: --privileged + ports: + - 5432:5432 + - 18443:18443 + - 50002:50002 + - 50001:50001 + - 8000:8000 + - 18080:18080 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Docker Compose + run: | + docker-compose -f docker-compose-test.yml up --build -d + - name: Wait for services to be ready + run: | + sleep 80 # Adjust time as necessary for services to initialize + - name: Verify Bitcoin daemon Service with Curl + run: | + container_id=$(docker ps -qf "name=mercurylayer_bitcoind_1") + echo "Container ID: $container_id" + docker logs $container_id + wallet_name="new_wallet" + docker exec $container_id bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass createwallet $wallet_name + address=$(docker exec $container_id bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass getnewaddress $wallet_name) + echo "New Wallet Address: $address" + docker exec $container_id bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass generatetoaddress 101 "$address" + docker exec $container_id bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass sendtoaddress bcrt1pcngfxjdkf4r2h26k52dh5nunxg8m68uf4lkfhmfjvjj6agfkm5jqmftw4e 0.0001 + - name: Verify ElectrumX Service with Curl + run: | + container_id=$(docker ps -qf "name=mercurylayer_electrs_1") + echo "Container ID: $container_id" + docker logs $container_id + - name: Verify Enclave Service with Curl + run: | + container_id=$(docker ps -qf "name=mercurylayer_enclave-sgx_1") + echo "Container ID: $container_id" + docker logs $container_id + - name: Verify Mercury Service with Curl + run: | + container_id=$(docker ps -qf "name=mercurylayer_mercury_1") + echo "Container ID: $container_id" + docker logs $container_id + docker exec $container_id \ + curl -X POST http://0.0.0.0:8000/deposit/init/pod \ + -H "Content-Type: application/json" \ + -d '{ + "auth_key": "f461775606ffc86e3f6e3115ff425d371b0f68cc59ad8cf71375c0e08c2ee8e9", + "token_id": "616f505d-b94c-45cf-b251-833e4fa14fa1", + "signed_token_id": "7401ac1f792f56d5357997f9846b5045656758f4afece4c51b73472bd338e97da3d167733502cf62d77c1169bd89bfaa0c9c5fcc26d75190e7a3a3fd2f83ae0a" + }' + - name: Get Public Key + run: | + docker exec $(docker ps -qf "name=enclave") \ + curl -X POST http://0.0.0.0:18080/get_public_key \ + -H "Content-Type: application/json" \ + -d '{"statechain_id":"550e8400e29b41d4a716446655440000"}' + docker logs $(docker ps -qf "name=enclave") + - name: Check connectivity between containers + run: | + # Get container IDs + enclave_container=$(docker ps -qf "name=mercurylayer_enclave-sgx_1") + mercury_container=$(docker ps -qf "name=mercurylayer_mercury_1") + + # Check if mercurylayer_mercury_1 can reach mercurylayer_enclave-sgx_1 + docker exec $mercury_container curl -v http://mercurylayer_enclave-sgx_1:18080/get_public_key \ + -H "Content-Type: application/json" \ + -d '{"statechain_id":"550e8400e29b41d4a716446655440000"}' + + # Alternatively, using IP address if service name resolution fails + enclave_ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $enclave_container) + docker exec $mercury_container curl -v http://$enclave_ip:18080/get_public_key \ + -H "Content-Type: application/json" \ + -d '{"statechain_id":"550e8400e29b41d4a716446655440000"}' + + docker inspect mercurylayer_mercury_1 + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '20.12.2' + + - name: Install Node.js dependencies for client + run: | + cd clients/apps/nodejs + npm install + - name: Install Node.js dependencies for lib + run: | + cd clients/libs/nodejs + npm install + - name: Run Client-Side Tests + run: | + cd clients/apps/nodejs + node test_basic_workflow2.js + - name: Tear Down + run: | + docker-compose -f docker-compose-test.yml down diff --git a/clients/apps/nodejs/config/default.json b/clients/apps/nodejs/config/default.json index aeac5a9b..477042c8 100644 --- a/clients/apps/nodejs/config/default.json +++ b/clients/apps/nodejs/config/default.json @@ -1,12 +1,12 @@ { - "statechainEntity": "http://127.0.0.1:8000", + "statechainEntity": "http://0.0.0.0:8000", "__statechainEntity": "http://j23wevaeducxuy3zahd6bpn4x76cymwz2j3bdixv7ow4awjrg5p6jaid.onion", "_statechainEntity": "http://45.76.136.11:8500/", - "__electrumServer": "tcp://signet-electrumx.wakiyamap.dev:50001", - "electrumServer": "ssl://mempool.space:60602", + "__electrumServer": "tcp://0.0.0.0:50001", + "electrumServer": "tcp://0.0.0.0:50001", "electrumType": "electrs", - "_electrumServer": "tcp://localhost:50001", - "network": "signet", + "_electrumServer": "tcp://0.0.0.0:50001", + "network": "regtest", "feeRateTolerance": 5, "databaseFile": "wallet.db", "confirmationTarget": 2, diff --git a/clients/apps/nodejs/config/regtest.json b/clients/apps/nodejs/config/regtest.json new file mode 100644 index 00000000..477042c8 --- /dev/null +++ b/clients/apps/nodejs/config/regtest.json @@ -0,0 +1,16 @@ +{ + "statechainEntity": "http://0.0.0.0:8000", + "__statechainEntity": "http://j23wevaeducxuy3zahd6bpn4x76cymwz2j3bdixv7ow4awjrg5p6jaid.onion", + "_statechainEntity": "http://45.76.136.11:8500/", + "__electrumServer": "tcp://0.0.0.0:50001", + "electrumServer": "tcp://0.0.0.0:50001", + "electrumType": "electrs", + "_electrumServer": "tcp://0.0.0.0:50001", + "network": "regtest", + "feeRateTolerance": 5, + "databaseFile": "wallet.db", + "confirmationTarget": 2, + "_torProxy": "socks5h://localhost:9050", + "torProxy": null, + "maxFeeRate": 1 +} \ No newline at end of file diff --git a/clients/apps/nodejs/config/signet.json b/clients/apps/nodejs/config/signet.json new file mode 100644 index 00000000..aeac5a9b --- /dev/null +++ b/clients/apps/nodejs/config/signet.json @@ -0,0 +1,16 @@ +{ + "statechainEntity": "http://127.0.0.1:8000", + "__statechainEntity": "http://j23wevaeducxuy3zahd6bpn4x76cymwz2j3bdixv7ow4awjrg5p6jaid.onion", + "_statechainEntity": "http://45.76.136.11:8500/", + "__electrumServer": "tcp://signet-electrumx.wakiyamap.dev:50001", + "electrumServer": "ssl://mempool.space:60602", + "electrumType": "electrs", + "_electrumServer": "tcp://localhost:50001", + "network": "signet", + "feeRateTolerance": 5, + "databaseFile": "wallet.db", + "confirmationTarget": 2, + "_torProxy": "socks5h://localhost:9050", + "torProxy": null, + "maxFeeRate": 1 +} \ No newline at end of file diff --git a/clients/apps/nodejs/test_basic_workflow2.js b/clients/apps/nodejs/test_basic_workflow2.js index c239cd29..883926e2 100644 --- a/clients/apps/nodejs/test_basic_workflow2.js +++ b/clients/apps/nodejs/test_basic_workflow2.js @@ -4,17 +4,31 @@ const assert = require('node:assert/strict'); const mercurynodejslib = require('mercurynodejslib'); const { CoinStatus } = require('mercurynodejslib/coin_enum'); const client_config = require('./client_config'); +const ElectrumCli = require('@mempool/electrum-client'); +const sqlite3 = require('sqlite3').verbose(); +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'); async function removeDatabase() { - try { - const { stdout, stderr } = await exec('rm wallet.db'); - // console.log('stdout:', stdout); - // console.error('stderr:', stderr); + try { + const clientConfig = client_config.load(); + const { stdout, stderr } = await exec(`rm ./${clientConfig.databaseFile}`); + console.log('stdout:', stdout); + console.error('stderr:', stderr); } catch (e) { - // console.error(e); + console.error(e); } } +const getDatabase = async (clientConfig) => { + const databaseFile = clientConfig.databaseFile; + const db = new sqlite3.Database(databaseFile); + await sqlite_manager.createTables(db); + return db; +} + const sleep = (ms) => { return new Promise(resolve => setTimeout(resolve, ms)); } @@ -27,13 +41,58 @@ async function createWallet(clientConfig, walletName) { // TODO: add more assertions } -async function walletTransfersToItselfAndWithdraw(clientConfig, wallet_name) { +const getElectrumClient = async (clientConfig) => { - const amount = 10000; + const urlElectrum = clientConfig.electrumServer; + const urlElectrumObject = new URL(urlElectrum); + + const electrumPort = parseInt(urlElectrumObject.port, 10); + const electrumHostname = urlElectrumObject.hostname; + const electrumProtocol = urlElectrumObject.protocol.slice(0, -1); + + const electrumClient = new ElectrumCli(electrumPort, electrumHostname, electrumProtocol); + await electrumClient.connect(); + + return electrumClient; +} + +async function generateBlock(numBlocks) { + const generateBlockCommand = `docker exec $(docker ps -qf "name=mercurylayer_bitcoind_1") bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass generatetoaddress ${numBlocks} "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"`; + exec(generateBlockCommand); + console.log(`Generated ${numBlocks} blocks`); + + const clientConfig = client_config.load(); + const electrumClient = await getElectrumClient(clientConfig); + const block_header = await electrumClient.request('blockchain.headers.subscribe'); + const blockheight = block_header.height; + console.log("Current block height: ", blockheight); +} + +async function depositCoin(clientConfig, wallet_name, amount, deposit_info) { + + deposit_info["amount"] = amount; + console.log("deposit_coin: ", deposit_info); + + const amountInBtc = amount / 100000000; + + // Sending Bitcoin using bitcoin-cli + try { + const sendBitcoinCommand = `docker exec $(docker ps -qf "name=mercurylayer_bitcoind_1") bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass sendtoaddress ${deposit_info.deposit_address} ${amountInBtc}`; + exec(sendBitcoinCommand); + console.log(`Sent ${amountInBtc} BTC to ${deposit_info.deposit_address}`); + await generateBlock(3); + } catch (error) { + console.error('Error sending Bitcoin:', error.message); + return; + } +} + +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); @@ -42,13 +101,13 @@ async function walletTransfersToItselfAndWithdraw(clientConfig, wallet_name) { assert(usedToken.spent); - deposit_info["amount"] = amount; - console.log("deposit_coin: ", deposit_info); + 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; @@ -58,6 +117,7 @@ async function walletTransfersToItselfAndWithdraw(clientConfig, wallet_name) { console.log("Waiting for coin to be confirmed..."); console.log(`Check the address ${deposit_info.deposit_address} ...\n`); await sleep(5000); + generateBlock(1); continue; } @@ -85,7 +145,7 @@ async function walletTransfersToItselfAndWithdraw(clientConfig, wallet_name) { assert(received_statechain_ids[0] == coin.statechain_id); } - let withdraw_address = "tb1qwrujs6f4gyexsextpf9p50smjtht7p7ypknteu"; + let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; let txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet_name, coin.statechain_id, withdraw_address, null); @@ -93,13 +153,88 @@ async function walletTransfersToItselfAndWithdraw(clientConfig, wallet_name) { } -async function walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, wallet_1_name, wallet_2_name) { +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; + } + + console.log("coin: ", coin); + + const electrumClient = await getElectrumClient(clientConfig); + + let block_header = await electrumClient.request('blockchain.headers.subscribe'); + let currentBlockHeight = block_header.height; + console.log("Current block height: ", currentBlockHeight); + + while (coin.locktime <= currentBlockHeight) { + let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_name, null); + + console.log("transfer_address: ", transfer_address); + + coin = await mercurynodejslib.transferSend(clientConfig, wallet_name, coin.statechain_id, transfer_address.transfer_receive); + + console.log("coin transferSend: ", coin); + + let received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, wallet_name); + + console.log("received_statechain_ids: ", received_statechain_ids); + + 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); + + console.log("Updated coin: ", coin); + } + + let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; + + let txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet_name, coin.statechain_id, withdraw_address, null); + + console.log("txid: ", txid); +} + +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); @@ -108,8 +243,7 @@ async function walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, walle assert(usedToken.spent); - deposit_info["amount"] = amount; - console.log("deposit_coin: ", deposit_info); + await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); let coin = undefined; @@ -126,6 +260,7 @@ async function walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, walle console.log("Waiting for coin to be confirmed..."); console.log(`Check the address ${deposit_info.deposit_address} ...\n`); await sleep(5000); + generateBlock(1); continue; } @@ -147,28 +282,697 @@ async function walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, walle assert(received_statechain_ids.length > 0); assert(received_statechain_ids[0] == coin.statechain_id); - let withdraw_address = "tb1qwrujs6f4gyexsextpf9p50smjtht7p7ypknteu"; + let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; let txid = await mercurynodejslib.broadcastBackupTransaction(clientConfig, wallet_2_name, coin.statechain_id, withdraw_address, null); console.log("txid: ", txid); } +async function depositAndRepeatSend(clientConfig, wallet_1_name) { -(async () => { + const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); + const tokenId = token.token_id; - const clientConfig = client_config.load(); + 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; + + console.log("coin: ", coin); + + 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; + } + + console.log("coin: ", coin); + + 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); + let received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, wallet_1_name); + + 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); + + let received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, wallet_1_name); + + 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; + + console.log("coin: ", coin); + + 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; + } + + console.log("coin: ", coin); + + 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); + + let received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, 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); + + 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); + 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) { + + 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); + + for (let i = 0; i < 10; i++) { + 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 list_coins = await mercurynodejslib.listStatecoins(clientConfig, wallet_name); + + for (let coin of list_coins) { + let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_name, null); + + console.log("transfer_address: ", transfer_address); + + coin = await mercurynodejslib.transferSend(clientConfig, wallet_name, coin.statechain_id, transfer_address.transfer_receive); + + console.log("coin transferSend: ", coin); + + let received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, wallet_name); + + console.log("received_statechain_ids: ", received_statechain_ids); + + 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; + + console.log("coin: ", coin); + + 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; + } + + console.log("coin: ", coin); + + let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); + + console.log("Disconnect mercurylayer_mercury_1 from network"); + await exec("docker network disconnect mercurylayer_default mercurylayer_mercury_1"); + + try { + coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive); + 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 exec("docker network connect mercurylayer_default mercurylayer_mercury_1"); +} + +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 wallet_1_name = "w1"; - let wallet_2_name = "w2"; + 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 exec("docker network disconnect mercurylayer_default mercurylayer_mercury_1"); + + 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 exec("docker network connect mercurylayer_default mercurylayer_mercury_1"); +} + +async function interruptBeforeSignSecond(clientConfig, wallet_1_name, wallet_2_name) { + const token = await mercurynodejslib.newToken(clientConfig, wallet_1_name); + const tokenId = token.token_id; - await removeDatabase(); - await createWallet(clientConfig, wallet_1_name); - await createWallet(clientConfig, wallet_2_name); + 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 walletTransfersToItselfAndWithdraw(clientConfig, wallet_1_name); + await depositCoin(clientConfig, wallet_1_name, amount, deposit_info); - await walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, wallet_1_name, wallet_2_name); + let coinDeposited = undefined; - await removeDatabase(); -})(); \ No newline at end of file + console.log("coin: ", coinDeposited); + + 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(5000); + generateBlock(1); + continue; + } + + coinDeposited = coinsWithStatechainId[0]; + break; + } + + console.log("coin: ", coinDeposited); + + 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.transfer_receive; + + let batchId = (options && options.batchId) || 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); + + // Coin withdrawal is working fine + // let withdraw_address = "bcrt1qgh48u8aj4jvjkalc28lqujyx2wveck4jsm59x9"; + + // let txid = await mercurynodejslib.withdrawCoin(clientConfig, wallet_1_name, coin.statechain_id, withdraw_address, null); + + // console.log("txid: ", txid); + + 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); + + console.log("coin ", coin); + + let received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, 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); +} + +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; + + console.log("coin: ", coin); + + 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; + } + + console.log("coin: ", coin); + + let transfer_address = await mercurynodejslib.newTransferAddress(clientConfig, wallet_2_name, null); + + console.log("Disconnect mercurylayer_electrs_1 from network"); + await exec("docker network disconnect mercurylayer_default mercurylayer_electrs_1"); + + try { + coin = await mercurynodejslib.transferSend(clientConfig, wallet_1_name, coin.statechain_id, transfer_address.transfer_receive); + 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 exec("docker network connect mercurylayer_default mercurylayer_electrs_1"); +} + +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; + + console.log("coin: ", coin); + + 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; + } + + console.log("coin: ", coin); + + 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); + + console.log("Disconnect mercurylayer_electrs_1 from network"); + await exec("docker network disconnect mercurylayer_default mercurylayer_electrs_1"); + + 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:50001"), + `Unexpected error message: ${error.message}`); + } + console.log("Connect mercurylayer_electrs_1 from network"); + await exec("docker network connect mercurylayer_default mercurylayer_electrs_1"); +} + +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; + + console.log("coin: ", coin); + + 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; + } + + console.log("coin: ", coin); + + 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); + + console.log("Disconnect mercurylayer_mercury_1 from network"); + await exec("docker network disconnect mercurylayer_default mercurylayer_mercury_1"); + + 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 exec("docker network connect mercurylayer_default mercurylayer_mercury_1"); +} + +(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); + await walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, wallet_3_name, wallet_4_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"); + + process.exit(0); // Exit successfully + } catch (error) { + console.error("Test encountered an error:", error); + process.exit(1); // Exit with failure + } +})(); diff --git a/clients/libs/nodejs/coin_status.js b/clients/libs/nodejs/coin_status.js index 26e453f2..3134a95b 100644 --- a/clients/libs/nodejs/coin_status.js +++ b/clients/libs/nodejs/coin_status.js @@ -27,8 +27,12 @@ const checkDeposit = async (clientConfig, electrumClient, coin, wallet_network) reversedHash = reversedHash.toString('hex'); let utxo = null; - - let utxo_list = await electrumClient.request('blockchain.scripthash.listunspent', [reversedHash]); + let utxo_list = null; + try { + utxo_list = await electrumClient.request('blockchain.scripthash.listunspent', [reversedHash]); + } catch (error) { + throw new Error("Error getting unspent list from electrs server"); + } for (let unspent of utxo_list) { if (unspent.value === coin.amount) { @@ -177,7 +181,6 @@ const updateCoins = async (clientConfig, electrumClient, db, wallet_name) => { let coin = wallet.coins[i]; if (coin.status == CoinStatus.INITIALISED || coin.status == CoinStatus.IN_MEMPOOL || coin.status == CoinStatus.UNCONFIRMED) { - let depositResult = await checkDeposit(clientConfig, electrumClient, coin, network); if (depositResult) { diff --git a/clients/libs/nodejs/deposit.js b/clients/libs/nodejs/deposit.js index d8b8c538..64de8b07 100644 --- a/clients/libs/nodejs/deposit.js +++ b/clients/libs/nodejs/deposit.js @@ -107,7 +107,6 @@ const init = async (clientConfig, db, wallet, token_id) => { if (torProxy) { socksAgent = { httpAgent: new SocksProxyAgent(torProxy) }; } - const response = await axios.post(url, depositMsg1, socksAgent); if (response.status != 200) { diff --git a/clients/libs/nodejs/transaction.js b/clients/libs/nodejs/transaction.js index 43b07a47..434f30f7 100644 --- a/clients/libs/nodejs/transaction.js +++ b/clients/libs/nodejs/transaction.js @@ -63,8 +63,21 @@ const signFirst = async (clientConfig, signFirstRequestPayload) => { socksAgent = { httpAgent: new SocksProxyAgent(torProxy) }; } - let response = await axios.post(url, signFirstRequestPayload, socksAgent); + let response; + try { + response = await axios.post(url, signFirstRequestPayload, socksAgent); + } catch (error) { + if (error.code === 'ECONNREFUSED') { + console.error('Error: Connection refused. The server at 0.0.0.0:8000 is not available.'); + } else { + console.error('An error occurred:', error.message); + } + } + // Check if response or response.data is undefined + if (!response || !response.data || !response.data.server_pubnonce) { + throw new Error('Server public nonce is not available.'); + } let server_pubnonce_hex = response.data.server_pubnonce; if (server_pubnonce_hex.startsWith("0x")) { @@ -88,8 +101,21 @@ const signSecond = async (clientConfig, partialSigRequest) => { socksAgent = { httpAgent: new SocksProxyAgent(torProxy) }; } - let response = await axios.post(url, partialSigRequest, socksAgent); + let response; + try { + response = await axios.post(url, partialSigRequest, socksAgent); + } catch (error) { + if (error.code === 'ECONNREFUSED') { + console.error('Error: Connection refused. The server at 0.0.0.0:8000 is not available.'); + } else { + console.error('An error occurred:', error.message); + } + } + // Check if response or response data is undefined + if (!response || !response.data || !response.data.partial_sig) { + throw new Error('Server partial signature is not available.'); + } let server_partial_sig_hex = response.data.partial_sig; if (server_partial_sig_hex.startsWith("0x")) { diff --git a/clients/libs/nodejs/transfer_receive.js b/clients/libs/nodejs/transfer_receive.js index 15dd71af..bd3b4d10 100644 --- a/clients/libs/nodejs/transfer_receive.js +++ b/clients/libs/nodejs/transfer_receive.js @@ -113,7 +113,12 @@ const getMsgAddr = async (clientConfig, auth_pubkey) => { socksAgent = { httpAgent: new SocksProxyAgent(torProxy) }; } - const response = await axios.get(url, socksAgent); + let response; + try { + response = await axios.get(url, socksAgent); + } catch (error) { + throw new Error('Failed to get message address from mercury server'); + } return response.data.list_enc_transfer_msg; } diff --git a/clients/libs/nodejs/transfer_send.js b/clients/libs/nodejs/transfer_send.js index 20330d54..3d06cb7e 100644 --- a/clients/libs/nodejs/transfer_send.js +++ b/clients/libs/nodejs/transfer_send.js @@ -160,9 +160,20 @@ const get_new_x1 = async (clientConfig, statechain_id, signed_statechain_id, new socksAgent = { httpAgent: new SocksProxyAgent(torProxy) }; } - const response = await axios.post(url, transferSenderRequestPayload, socksAgent); - - return response.data.x1; + let response; + try { + response = await axios.post(url, transferSenderRequestPayload, socksAgent); + if (response.data) { + console.log('Response:', response.data); + } + } catch (error) { + if (error.code === 'ECONNREFUSED') { + console.error('Error: Connection refused. The server at 0.0.0.0:8000 is not available.'); + } else { + console.error('An error occurred:', error.message); + } + } + return response?.data?.x1; } module.exports = { execute }; \ No newline at end of file diff --git a/clients/libs/nodejs/utils.js b/clients/libs/nodejs/utils.js index cdd9430c..e503c49b 100644 --- a/clients/libs/nodejs/utils.js +++ b/clients/libs/nodejs/utils.js @@ -7,9 +7,12 @@ const infoConfig = async (clientConfig, ecl) => { const statechain_entity_url = clientConfig.statechainEntity; const path = "info/config"; - let fee_rate_btc_per_kb = await ecl.request('blockchain.estimatefee', [3]); // request(promise) - - // console.log("fee_rate_btc_per_kb:", fee_rate_btc_per_kb); + let fee_rate_btc_per_kb; + try { + fee_rate_btc_per_kb = await ecl.request('blockchain.estimatefee', [3]); // request(promise) + } catch (error) { + throw new Error("Error getting fee rate from electrs server"); + } // Why does it happen? if (fee_rate_btc_per_kb <= 0) { @@ -17,8 +20,6 @@ const infoConfig = async (clientConfig, ecl) => { } const fee_rate_sats_per_byte = (fee_rate_btc_per_kb * 100000.0); - // console.log("fee_rate_sats_per_byte: " + fee_rate_sats_per_byte); - const torProxy = clientConfig.torProxy; let socksAgent = undefined; diff --git a/docker-compose-test.yml b/docker-compose-test.yml new file mode 100644 index 00000000..0bdefbc3 --- /dev/null +++ b/docker-compose-test.yml @@ -0,0 +1,68 @@ +version: '3.8' + +services: + postgres: + image: postgres + environment: + POSTGRES_PASSWORD: pgpassword + ports: + - "5432:5432" + + bitcoind: + image: lncm/bitcoind:v22.0@sha256:37a1adb29b3abc9f972f0d981f45e41e5fca2e22816a023faa9fdc0084aa4507 + user: root + command: -regtest -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 -rpcauth=user:63cf03615adebaa9356591f95b07ec7b$$920588e53f94798bda636acac1b6a77e10e3ee7fe57e414d62f3ee9e580cd27a -fallbackfee=0.0001 + ports: + - "18443:18443" + volumes: + - bitcoin_data:/root/.bitcoin + + electrs: + image: getumbrel/electrs:v0.9.4@sha256:b1590ac6cfb0e5b481c6a7af7f0626d76cbb91c63702b0f5c47e2829e9c37997 + user: root + environment: + ELECTRS_LOG_FILTERS: "INFO" + ELECTRS_NETWORK: "regtest" + ELECTRS_DAEMON_RPC_ADDR: "bitcoind:18443" + ELECTRS_DAEMON_P2P_ADDR: "bitcoind:18444" + ELECTRS_ELECTRUM_RPC_ADDR: "0.0.0.0:50001" + ELECTRS_SERVER_BANNER: "Umbrel Electrs" + ports: + - "50001:50001" + volumes: + - electrs-data:/data + - bitcoin_data:/root/.bitcoin + depends_on: + - bitcoind + + enclave-sgx: + build: + context: enclave + dockerfile: Dockerfiles/SIM/Dockerfile + depends_on: + - postgres + environment: + ENCLAVE_DATABASE_URL: postgres://postgres:pgpassword@postgres:5432/postgres + ENCLAVE_PORT: 18080 + SEED_DIR: ./seed + ports: + - "18080:18080" + + mercury: + build: + context: . + dockerfile: ./server/Dockerfile + environment: + NETWORK: regtest + LOCKHEIGHT_INIT: 1100 + LH_DECREMENT: 1 + CONNECTION_STRING: postgres://postgres:pgpassword@postgres:5432/postgres + ENCLAVES: '[{"url": "http://mercurylayer_enclave-sgx_1:18080", "allow_deposit": true}]' + ports: + - "8000:8000" + depends_on: + - postgres + +volumes: + bitcoin_data: + electrs-data: \ No newline at end of file diff --git a/enclave/Dockerfiles/SIM/Dockerfile b/enclave/Dockerfiles/SIM/Dockerfile index 42a77598..7af80dcd 100644 --- a/enclave/Dockerfiles/SIM/Dockerfile +++ b/enclave/Dockerfiles/SIM/Dockerfile @@ -69,4 +69,4 @@ RUN rm -rf Dockerfiles # Use the SGX_MODE argument in the make command RUN make SGX_MODE=SIM INCLUDE_SECP256K1_ZKP=1 -CMD ["/home/lockbox-sgx/app"] +CMD ["/home/lockbox-sgx/app", "-g"] diff --git a/server/.env_example b/server/.env_example index 28584a0d..210c9eb1 100644 --- a/server/.env_example +++ b/server/.env_example @@ -1,5 +1,5 @@ -LOCKBOX = NETWORK = LOCKHEIGHT_INIT = LH_DECREMENT = CONNECTION_STRING = +ENCLAVES =