Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/tests for coin expiry #71

Merged
merged 5 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
branches:
- dev
- dev3
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it would be better to rename dev3 to dev before merging this PR. Therefore this line will not be necessary.

Suggested change
- dev3


jobs:
test:
Expand Down
239 changes: 236 additions & 3 deletions clients/apps/nodejs/test_basic_workflow2.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const getElectrumClient = async (clientConfig) => {

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);
await exec(generateBlockCommand);
console.log(`Generated ${numBlocks} blocks`);

const clientConfig = client_config.load();
Expand Down Expand Up @@ -868,6 +868,201 @@ async function interruptTransferReceiveWithMercuryServerUnavailability(clientCon
await exec("docker network connect mercurylayer_default mercurylayer_mercury_1");
}

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;

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);

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);
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;

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);

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 received_statechain_ids = undefined;

let errorMessage;
console.error = (msg) => {
errorMessage = msg;
};

received_statechain_ids = await mercurynodejslib.transferReceive(clientConfig, wallet_2_name);

// Assert the captured error message
const expectedMessage = 'The coin is expired.';
assert.ok(errorMessage.includes(expectedMessage));
console.log("received_statechain_ids: ", received_statechain_ids);

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;

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);

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);
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 {
Expand All @@ -878,7 +1073,7 @@ async function interruptTransferReceiveWithMercuryServerUnavailability(clientCon
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);
// await walletTransfersToAnotherAndBroadcastsBackupTx(clientConfig, wallet_1_name, wallet_2_name);


// Deposit, repeat send
Expand All @@ -887,7 +1082,6 @@ async function interruptTransferReceiveWithMercuryServerUnavailability(clientCon
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
Expand Down Expand Up @@ -949,6 +1143,45 @@ async function interruptTransferReceiveWithMercuryServerUnavailability(clientCon
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) {
Expand Down
7 changes: 7 additions & 0 deletions clients/libs/nodejs/broadcast_backup_tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ const execute = async (clientConfig, electrumClient, db, walletName, statechainI
throw new Error(`Coin status must be CONFIRMED or IN_TRANSFER to transfer it. The current status is ${coin.status}`);
}

const blockHeader = await electrumClient.request('blockchain.headers.subscribe'); // request(promise)
const currentBlockheight = blockHeader.height;

if (currentBlockheight <= coin.locktime) {
throw new Error(`The coin is not expired yet. Coin locktime is ${coin.locktime} and current blockheight is ${currentBlockheight}`);
}

const backupTx = mercury_wasm.latestBackuptxPaysToUserpubkey(backupTxs, coin, wallet.network);

if (!backupTx) {
Expand Down
12 changes: 12 additions & 0 deletions clients/libs/nodejs/transfer_receive.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ const execute = async (clientConfig, electrumClient, db, wallet_name) => {

const serverInfo = await utils.infoConfig(clientConfig, electrumClient);

let blockHeader = undefined;
try {
blockHeader = await electrumClient.request('blockchain.headers.subscribe'); // request(promise)
} catch (error) {
throw new Error("Error getting block height from electrs server");
}
const currentBlockheight = blockHeader.height;

let uniqueAuthPubkeys = new Set();

wallet.coins.forEach(coin => {
Expand Down Expand Up @@ -88,6 +96,10 @@ const execute = async (clientConfig, electrumClient, db, wallet_name) => {
continue;
}
}

if (currentBlockheight >= coin.locktime) {
console.error(`The coin is expired. Coin locktime is ${coin.locktime} and current blockheight is ${currentBlockheight}`);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions clients/libs/nodejs/transfer_send.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ const execute = async (clientConfig, electrumClient, db, walletName, statechainI
const blockHeader = await electrumClient.request('blockchain.headers.subscribe'); // request(promise)
const currentBlockheight = blockHeader.height;

if (currentBlockheight > coin.locktime) {
const serverInfo = await utils.infoConfig(clientConfig, electrumClient);

if (currentBlockheight + serverInfo.interval >= coin.locktime) {
throw new Error(`The coin is expired. Coin locktime is ${coin.locktime} and current blockheight is ${currentBlockheight}`);
}

Expand All @@ -63,8 +65,6 @@ const execute = async (clientConfig, electrumClient, db, walletName, statechainI

const new_x1 = await get_new_x1(clientConfig, statechain_id, signed_statechain_id, new_auth_pubkey, batchId);

const serverInfo = await utils.infoConfig(clientConfig, electrumClient);

let feeRateSatsPerByte = (serverInfo.fee_rate_sats_per_byte > clientConfig.maxFeeRate) ? clientConfig.maxFeeRate: serverInfo.fee_rate_sats_per_byte;

const signed_tx = await transaction.new_transaction(
Expand Down
Loading