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

Adds 'should create multiple pegouts with mixed values same and above… #163

Open
wants to merge 2 commits into
base: rits-refactors-9-2024-integration
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions lib/2wp-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,19 @@ const assertRefundUtxosSameAsPeginUtxos = async(rskTxHelper, btcTxHelper, peginT
* @param {BN} amountInWeisBN
* @param {string} rskFromAddress
* @param {boolean} mine If true, mines 1 block after sending the transaction. If false, it will not mine the tx and will return undefined. Defaults to true.
* @returns {web3.eth.TransactionReceipt | undefined}
* @returns {Promise<web3.eth.TransactionReceipt | txPromise>} the rsk tx receipt if `mine` is true, otherwise the tx promise.
*/
const sendTxToBridge = async (rskTxHelper, amountInWeisBN, rskFromAddress, mine = true) => {

const txPromise = rskTxHelper.sendTransaction({
from: rskFromAddress,
to: BRIDGE_ADDRESS,
value: amountInWeisBN,
gasPrice: TO_BRIDGE_GAS_PRICE,
});

if(!mine) {

Choose a reason for hiding this comment

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

nit:

Suggested change
if(!mine) {
if (!mine) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just noticed there is a mixed of if ( an if( in the test. I always prefer if(. But what should we always use? @nathanieliov , @marcos-iov , @julia-zack .

Copy link
Contributor

Choose a reason for hiding this comment

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

@apancorb 's suggestion, as it seems to be the rule of our formatting settings. I formatted it in my local and the result was @apancorb suggestion.

return;
return txPromise;
}

// Wait for the rsk tx to be in the rsk mempool before mining
Expand All @@ -78,6 +80,7 @@ const sendTxToBridge = async (rskTxHelper, amountInWeisBN, rskFromAddress, mine
await mineAndSync(getRskTransactionHelpers());
const result = await txPromise;
return result;

};

/**
Expand Down
25 changes: 25 additions & 0 deletions lib/rsk-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,30 @@ const getNewFundedRskAddress = async (rskTxHelper, fundingAmountInRbtc = DEFAULT
return address;
};

/**
* Waits for the rsk mempool to get `atLeastExpectedCount` txs. Attempting `maxAttempts` times, checking every `waitTimeInMilliseconds`.
* @param {RskTransactionHelper} rskTxHelper
* @param {number} atLeastExpectedCount
* @param {number} waitTimeInMilliseconds defaults to 500 milliseconds
* @param {number} maxAttempts defaults to 20 attempts
* @returns {Promise<void>}
* @throws {Error} if the rsk mempool doesn't get at least `atLeastExpectedCount` txs after `maxAttempts` attempts
*/
const waitForRskMempoolToGetAtLeastThisManyTxs = async (rskTxHelper, atLeastExpectedCount, waitTimeInMilliseconds = 500, maxAttempts = 20) => {
let attempts = 1;
while(attempts <= maxAttempts) {
apancorb marked this conversation as resolved.
Show resolved Hide resolved
const rskMempoolTxs = await getRskMempoolTransactionHashes(rskTxHelper);
logger.debug(`[${waitForRskMempoolToGetAtLeastThisManyTxs.name}] rsk mempool txs: ${rskMempoolTxs.length}`);
if(rskMempoolTxs.length >= atLeastExpectedCount) {
logger.debug(`[${waitForRskMempoolToGetAtLeastThisManyTxs.name}] rsk mempool has ${rskMempoolTxs.length} (at least expected was ${atLeastExpectedCount}) txs after ${attempts} attempts`);
return;
}
await wait(waitTimeInMilliseconds);
attempts++;
}
throw new Error(`[${waitForRskMempoolToGetAtLeastThisManyTxs.name}] rsk mempool didn't get at least ${atLeastExpectedCount} txs after ${maxAttempts} attempts`);
};

module.exports = {
mineAndSync,
waitForBlock,
Expand All @@ -558,4 +582,5 @@ module.exports = {
getPegoutEventsInBlockRange,
setFeePerKb,
getNewFundedRskAddress,
waitForRskMempoolToGetAtLeastThisManyTxs,
};
209 changes: 187 additions & 22 deletions lib/tests/2wp.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ const { getBridge } = require('../precompiled-abi-forks-util');
const { getBtcClient } = require('../btc-client-provider');
const { getRskTransactionHelper, getRskTransactionHelpers } = require('../rsk-tx-helper-provider');
const { satoshisToBtc, btcToSatoshis, satoshisToWeis } = require('@rsksmart/btc-eth-unit-converter');
const { findEventInBlock, triggerRelease, getPegoutEventsInBlockRange, setFeePerKb, sendTransaction, getNewFundedRskAddress } = require('../rsk-utils');
const {
findEventInBlock,
triggerRelease,
getPegoutEventsInBlockRange,
setFeePerKb,
sendTransaction,
getRskMempoolTransactionHashes,
getNewFundedRskAddress,
waitForRskMempoolToGetAtLeastThisManyTxs,
} = require('../rsk-utils');
const BridgeTransactionParser = require('@rsksmart/bridge-transaction-parser');
const {
PEGIN_REJECTION_REASONS,
Expand Down Expand Up @@ -905,6 +914,130 @@ const execute = (description, getRskHost) => {

});

it('should create multiple pegouts with mixed values same and above minimum', async () => {

// Arrange

const senderRecipientInfo1 = await createSenderRecipientInfo(rskTxHelper, btcTxHelper);
await fundRskAccountThroughAPegin(rskTxHelper, btcTxHelper, senderRecipientInfo1.btcSenderAddressInfo);

const senderRecipientInfo2 = await createSenderRecipientInfo(rskTxHelper, btcTxHelper);
await fundRskAccountThroughAPegin(rskTxHelper, btcTxHelper, senderRecipientInfo2.btcSenderAddressInfo);

const senderRecipientInfo3 = await createSenderRecipientInfo(rskTxHelper, btcTxHelper);
await fundRskAccountThroughAPegin(rskTxHelper, btcTxHelper, senderRecipientInfo3.btcSenderAddressInfo);
nathanieliov marked this conversation as resolved.
Show resolved Hide resolved

const initial2wpBalances = await get2wpBalances(rskTxHelper, btcTxHelper);

const initialSender1AddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo1.btcSenderAddressInfo.address);
const initialSender2AddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo2.btcSenderAddressInfo.address);
const initialSender3AddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo3.btcSenderAddressInfo.address);

const pegout1ValueInSatoshis = MINIMUM_PEGOUT_AMOUNT_IN_SATOSHIS;
const pegout2ValueInSatoshis = MINIMUM_PEGOUT_AMOUNT_IN_SATOSHIS + 100_000;
const pegout3ValueInSatoshis = MINIMUM_PEGOUT_AMOUNT_IN_SATOSHIS + 150_000;
const totalPegoutValueInSatoshis = pegout1ValueInSatoshis + pegout2ValueInSatoshis + pegout3ValueInSatoshis;

const rskMemPoolTxHashes = await getRskMempoolTransactionHashes(rskTxHelper);
const initialRskMempoolTxHashesSize = rskMemPoolTxHashes.length;

// Act

const shouldMine = false;
const pegoutTransaction1Promise = sendTxToBridge(rskTxHelper, new BN(satoshisToWeis(pegout1ValueInSatoshis)), senderRecipientInfo1.rskRecipientRskAddressInfo.address, shouldMine);
const pegoutTransaction2Promise = sendTxToBridge(rskTxHelper, new BN(satoshisToWeis(pegout2ValueInSatoshis)), senderRecipientInfo2.rskRecipientRskAddressInfo.address, shouldMine);
const pegoutTransaction3Promise = sendTxToBridge(rskTxHelper, new BN(satoshisToWeis(pegout3ValueInSatoshis)), senderRecipientInfo3.rskRecipientRskAddressInfo.address, shouldMine);

// Waiting for our 3 txs to reach the mempool before trying to mine them. + 3 because we are sending 3 pegouts.
const atLeastExpectedCount = initialRskMempoolTxHashesSize + 3;
await waitForRskMempoolToGetAtLeastThisManyTxs(rskTxHelper, atLeastExpectedCount);

// Now our 3 pegout transaction requests will get mined together.
await rskTxHelper.mine();

// After mining, we can get the transaction receipts.
const pegoutTransaction1 = await pegoutTransaction1Promise;
const pegoutTransaction2 = await pegoutTransaction2Promise;
const pegoutTransaction3 = await pegoutTransaction3Promise;

// Assert
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we check the Bridge state at this point? To assert there are 3 pegout requests created


let bridgeStateAfterPegoutCreation;

// Callback to get the bridge state after the pegout is created
const pegoutCreatedCallback = async () => {
bridgeStateAfterPegoutCreation = await getBridgeState(rskTxHelper.getClient());
};
Comment on lines +968 to +970
Copy link
Collaborator

Choose a reason for hiding this comment

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

I might be missing something, what's the value of this callback? It will simply get the Bridge state and do nothing with it


const callbacks = {
pegoutCreatedCallback
};

await triggerRelease(rskTxHelpers, btcTxHelper, callbacks);

// Checking all the pegout events are emitted and in order

Choose a reason for hiding this comment

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

It seems they all get mined in the same block. How are we verifying the order?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They all get mined in the same block, otherwise, the test would fail.
Since they can get mined in different order, I'm forced to use pegoutsEvents.find index of indices and getAddressUtxo to find the corresponding utxos.

const blockNumberAfterPegoutRelease = await rskTxHelper.getBlockNumber();
const pegoutsEvents = await getPegoutEventsInBlockRange(rskTxHelper, pegoutTransaction1.blockNumber, blockNumberAfterPegoutRelease);

// The release_request_received event of the first pegout request
const rskSender1Address = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(senderRecipientInfo1.rskRecipientRskAddressInfo.address));
const releaseRequestReceivedEvent1 = pegoutsEvents.find(event => event.arguments.sender === rskSender1Address);
assertReleaseRequestReceivedEvent(releaseRequestReceivedEvent1, rskSender1Address, senderRecipientInfo1.btcSenderAddressInfo.address, pegout1ValueInSatoshis);

// The release_request_received event of the second pegout request
const rskSender2Address = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(senderRecipientInfo2.rskRecipientRskAddressInfo.address));
const releaseRequestReceivedEvent2 = pegoutsEvents.find(event => event.arguments.sender === rskSender2Address);
assertReleaseRequestReceivedEvent(releaseRequestReceivedEvent2, rskSender2Address, senderRecipientInfo2.btcSenderAddressInfo.address, pegout2ValueInSatoshis);

// The release_request_received event of the third pegout request
const rskSender3Address = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(senderRecipientInfo3.rskRecipientRskAddressInfo.address));
const releaseRequestReceivedEvent3 = pegoutsEvents.find(event => event.arguments.sender === rskSender3Address);
assertReleaseRequestReceivedEvent(releaseRequestReceivedEvent3, rskSender3Address, senderRecipientInfo3.btcSenderAddressInfo.address, pegout3ValueInSatoshis);

const pegoutWaitingForConfirmationWhenPegoutWasCreated = bridgeStateAfterPegoutCreation.pegoutsWaitingForConfirmations[0];
const btcTransaction = bitcoinJsLib.Transaction.fromHex(pegoutWaitingForConfirmationWhenPegoutWasCreated.btcRawTx);
const pegoutCreationRskTransactionHash = ensure0x(pegoutWaitingForConfirmationWhenPegoutWasCreated.rskTxHash.padStart(64, '0'));

// Release requested events
const releaseRequestedEvent = pegoutsEvents[3];
assertReleaseRequestedEvent(releaseRequestedEvent, pegoutCreationRskTransactionHash, btcTransaction.getId(), totalPegoutValueInSatoshis);

const batchPegoutCreatedEvent = pegoutsEvents[4];
assertBatchPegoutCreatedEvent(batchPegoutCreatedEvent, btcTransaction.getId(), [pegoutTransaction1.transactionHash, pegoutTransaction2.transactionHash, pegoutTransaction3.transactionHash]);

// pegout_confirmed event
const pegoutConfirmedEvent = pegoutsEvents[5];
assertPegoutConfirmedEvent(pegoutConfirmedEvent, btcTransaction.getId(), pegoutWaitingForConfirmationWhenPegoutWasCreated.pegoutCreationBlockNumber);

// add_signature events
const addSignatureEvents = pegoutsEvents.slice(6, pegoutsEvents.length - 1);
assertAddSignatureEvents(addSignatureEvents, releaseRequestedEvent);

// release_btc event
const releaseBtcEvent = pegoutsEvents[pegoutsEvents.length - 1];
assertReleaseBtcEvent(releaseBtcEvent, releaseRequestedEvent);

await assert2wpBalanceAfterSuccessfulPegout(initial2wpBalances, totalPegoutValueInSatoshis);

const releaseBtcTransaction = bitcoinJsLib.Transaction.fromHex(removePrefix0x(releaseBtcEvent.arguments.btcRawTransaction));

// Asserting actual value received for sender 1
const finalSenderAddressBalanceInSatoshis1 = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo1.btcSenderAddressInfo.address);
const sender1Utxo = getAddressUtxo(releaseBtcTransaction.outs, senderRecipientInfo1.btcSenderAddressInfo.address);
expect(finalSenderAddressBalanceInSatoshis1).to.be.equal(initialSender1AddressBalanceInSatoshis + sender1Utxo.value);

// Asserting actual value received for sender 2
const finalSenderAddressBalanceInSatoshis2 = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo2.btcSenderAddressInfo.address);
const serder2Utxo = getAddressUtxo(releaseBtcTransaction.outs, senderRecipientInfo2.btcSenderAddressInfo.address);
expect(finalSenderAddressBalanceInSatoshis2).to.be.equal(initialSender2AddressBalanceInSatoshis + serder2Utxo.value);

// Asserting actual value received for sender 3
const finalSenderAddressBalanceInSatoshis3 = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo3.btcSenderAddressInfo.address);
const serder3Utxo = getAddressUtxo(releaseBtcTransaction.outs, senderRecipientInfo3.btcSenderAddressInfo.address);
expect(finalSenderAddressBalanceInSatoshis3).to.be.equal(initialSender3AddressBalanceInSatoshis + serder3Utxo.value);

});

});

};
Expand Down Expand Up @@ -1011,6 +1144,44 @@ const assertBtcPeginTxHashProcessed = async (btcPeginTxHash) => {
expect(isBtcTxHashAlreadyProcessed).to.be.true;
};

const assertReleaseRequestReceivedEvent = (releaseRequestReceivedEvent, rskSenderAddress, btcRecipientAddress, pegoutValueInSatoshis) => {
expect(releaseRequestReceivedEvent.arguments.sender).to.be.equal(rskSenderAddress);
expect(Number(releaseRequestReceivedEvent.arguments.amount)).to.be.equal(pegoutValueInSatoshis);
expect(releaseRequestReceivedEvent.arguments.btcDestinationAddress).to.be.equal(btcRecipientAddress);
};

const assertReleaseRequestedEvent = (releaseRequestedEvent, pegoutCreationRskTransactionHash, btcTxHash, pegoutValueInSatoshis) => {
expect(releaseRequestedEvent.arguments.rskTxHash).to.be.equal(pegoutCreationRskTransactionHash);
expect(removePrefix0x(releaseRequestedEvent.arguments.btcTxHash)).to.be.equal(btcTxHash);
expect(Number(releaseRequestedEvent.arguments.amount)).to.be.equal(pegoutValueInSatoshis);
};

const assertBatchPegoutCreatedEvent = (batchPegoutCreatedEvent, btcTxHash, pegoutRequestReceivedTransactionHashes) => {
expect(removePrefix0x(batchPegoutCreatedEvent.arguments.btcTxHash)).to.be.equal(btcTxHash);
const releaseRskTxHashes = batchPegoutCreatedEvent.arguments.releaseRskTxHashes;
const allPegoutRequestReceivedTxHashesAreInBatch = pegoutRequestReceivedTransactionHashes.every(hash => releaseRskTxHashes.includes(removePrefix0x(hash)));
expect(allPegoutRequestReceivedTxHashesAreInBatch).to.be.true;
};

const assertPegoutConfirmedEvent = (pegoutConfirmedEvent, btcTxHash, pegoutCreationBlockNumber) => {
expect(removePrefix0x(pegoutConfirmedEvent.arguments.btcTxHash)).to.be.equal(btcTxHash);
expect(pegoutConfirmedEvent.arguments.pegoutCreationRskBlockNumber).to.be.equal(pegoutCreationBlockNumber);
};

const assertAddSignatureEvent = (addSignatureEvent, releaseRequestedEvent) => {
expect(addSignatureEvent.arguments.releaseRskTxHash).to.be.equal(releaseRequestedEvent.arguments.rskTxHash);
};

const assertAddSignatureEvents = (addSignatureEvents, releaseRequestedEvent) => {
addSignatureEvents.forEach(addSignatureEvent => {
assertAddSignatureEvent(addSignatureEvent, releaseRequestedEvent);
});
};

const assertReleaseBtcEvent = (releaseBtcEvent, releaseRequestedEvent) => {
expect(releaseBtcEvent.arguments.releaseRskTxHash).to.be.equal(releaseRequestedEvent.arguments.rskTxHash);
};

const assertSuccessfulPegoutEventsAreEmitted = async (pegoutsEvents, pegoutRequestReceivedTransactionHash, senderRecipientInfo, pegoutValueInSatoshis, bridgeStateAfterPegoutCreation) => {

const pegoutWaitingForConfirmationWhenPegoutWasCreated = bridgeStateAfterPegoutCreation.pegoutsWaitingForConfirmations[0];
Expand All @@ -1019,40 +1190,27 @@ const assertSuccessfulPegoutEventsAreEmitted = async (pegoutsEvents, pegoutReque

// release_request_received event
const releaseRequestReceivedEvent = pegoutsEvents[0];
expect(releaseRequestReceivedEvent.arguments.sender).to.be.equal(rskSenderAddress);
expect(Number(releaseRequestReceivedEvent.arguments.amount)).to.be.equal(pegoutValueInSatoshis);
expect(releaseRequestReceivedEvent.arguments.btcDestinationAddress).to.be.equal(senderRecipientInfo.btcSenderAddressInfo.address);
assertReleaseRequestReceivedEvent(releaseRequestReceivedEvent, rskSenderAddress, senderRecipientInfo.btcSenderAddressInfo.address, pegoutValueInSatoshis);

// release_requested event
const pegoutCreationRskTransactionHash = ensure0x(pegoutWaitingForConfirmationWhenPegoutWasCreated.rskTxHash.padStart(64, '0'));
const releaseRequestedEvent = pegoutsEvents[1];
expect(releaseRequestedEvent.arguments.rskTxHash).to.be.equal(pegoutCreationRskTransactionHash);
expect(removePrefix0x(releaseRequestedEvent.arguments.btcTxHash)).to.be.equal(btcTransaction.getId());
expect(Number(releaseRequestReceivedEvent.arguments.amount)).to.be.equal(pegoutValueInSatoshis);
assertReleaseRequestedEvent(releaseRequestedEvent, pegoutCreationRskTransactionHash, btcTransaction.getId(), pegoutValueInSatoshis);

// batch_pegout_created event
const batchPegoutCreatedEvent = pegoutsEvents[2];
expect(removePrefix0x(batchPegoutCreatedEvent.arguments.btcTxHash)).to.be.equal(btcTransaction.getId());
expect(batchPegoutCreatedEvent.arguments.releaseRskTxHashes.includes(removePrefix0x(pegoutRequestReceivedTransactionHash))).to.be.true;

assertBatchPegoutCreatedEvent(batchPegoutCreatedEvent, btcTransaction.getId(), [pegoutRequestReceivedTransactionHash]);

// pegout_confirmed event
const pegoutConfirmedEvent = pegoutsEvents[3];
expect(removePrefix0x(pegoutConfirmedEvent.arguments.btcTxHash)).to.be.equal(btcTransaction.getId());
expect(pegoutConfirmedEvent.arguments.pegoutCreationRskBlockNumber).to.be.equal(pegoutWaitingForConfirmationWhenPegoutWasCreated.pegoutCreationBlockNumber);
assertPegoutConfirmedEvent(pegoutConfirmedEvent, btcTransaction.getId(), pegoutWaitingForConfirmationWhenPegoutWasCreated.pegoutCreationBlockNumber);

// add_signature events
const addSignatureEvent1 = pegoutsEvents[4];
const addSignatureEvent2 = pegoutsEvents[5];
const addSignatureEvent3 = pegoutsEvents[6];

expect(addSignatureEvent1.arguments.releaseRskTxHash).to.be.equal(releaseRequestedEvent.arguments.rskTxHash);
expect(addSignatureEvent2.arguments.releaseRskTxHash).to.be.equal(releaseRequestedEvent.arguments.rskTxHash);
expect(addSignatureEvent3.arguments.releaseRskTxHash).to.be.equal(releaseRequestedEvent.arguments.rskTxHash);
const addSignatureEvents = pegoutsEvents.slice(4, pegoutsEvents.length - 1);
assertAddSignatureEvents(addSignatureEvents, releaseRequestedEvent);

// Final event, release_btc
const releaseBtcEvent = pegoutsEvents[7];
expect(releaseBtcEvent.arguments.releaseRskTxHash).to.be.equal(releaseRequestedEvent.arguments.rskTxHash);
const releaseBtcEvent = pegoutsEvents[pegoutsEvents.length - 1];
assertReleaseBtcEvent(releaseBtcEvent, releaseRequestedEvent);

};

Expand Down Expand Up @@ -1113,6 +1271,13 @@ const getReleaseRequestRejectedEventFromContractCallTxReceipt = (contractCallTxR
return releaseRequestRejectedEvent;
};

const getAddressUtxo = (outputs, address) => {
return outputs.find(output => {
const outputAddress = bitcoinJsLib.address.fromOutputScript(output.script, btcTxHelper.btcConfig.network);
return outputAddress === address;
});
};

module.exports = {
execute,
};
Loading