Skip to content

Commit

Permalink
Fixes bugs in waitForBitcoinTxToBeInMempool. Throwing exception in is…
Browse files Browse the repository at this point in the history
…UtxoRegisteredInBridge when the utxo is found in the bridge but not the count that we are expecting. Adds check to createSenderRecipientInfo to ensure funds. Update test to use new utility functions.
  • Loading branch information
jeremy-then committed Sep 27, 2024
1 parent 3cedd54 commit 61f48f8
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 11 deletions.
19 changes: 16 additions & 3 deletions lib/2wp-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ const createPegoutRequest = async (rskTxHelper, amountInRBTC, requestSize = 1) =
*/
const isUtxoRegisteredInBridge = async (rskTxHelper, peginTxHash, expectedUxtosCount = 1) => {
const bridgeState = await getBridgeState(rskTxHelper.getClient());
return bridgeState.activeFederationUtxos
.filter(utxo => utxo.btcTxHash === peginTxHash).length === expectedUxtosCount;
const foundUtxos = bridgeState.activeFederationUtxos.filter(utxo => utxo.btcTxHash === peginTxHash);
if(foundUtxos.length > 0 && foundUtxos.length !== expectedUxtosCount) {
throw new Error(`Found ${foundUtxos.length} utxo(s) with the pegin tx hash ${peginTxHash} in the bridge, but expected to find ${expectedUxtosCount} utxo(s).`);
}
return foundUtxos.length === expectedUxtosCount;
};

const mineForPeginRegistration = async (rskTxHelper, btcTxHelper) => {
Expand Down Expand Up @@ -245,7 +248,17 @@ const createSenderRecipientInfo = async (rskTxHelper, btcTxHelper, type = 'legac
await rskTxHelper.importAccount(rskRecipientRskAddressInfo.privateKey);
await rskTxHelper.unlockAccount(rskRecipientRskAddressInfo.address);
if(Number(initialAmountToFundInBtc) > 0) {
await btcTxHelper.fundAddress(btcSenderAddressInfo.address, initialAmountToFundInBtc);
const initialAddressBalance = await btcTxHelper.getAddressBalance(btcSenderAddressInfo.address);
const fundTxHash = await btcTxHelper.fundAddress(btcSenderAddressInfo.address, initialAmountToFundInBtc);
const finalAddressBalance = await btcTxHelper.getAddressBalance(btcSenderAddressInfo.address);
// If the final address balance is the same as the initial balance, then the tx has not yet reached the mempool or has not been mined. Rare condition that may happen randomly.
if(finalAddressBalance === initialAddressBalance) {
const inMempool = await waitForBitcoinTxToBeInMempool(btcTxHelper, fundTxHash);
if(inMempool) {
await btcTxHelper.mine();
}
}

}
return {
btcSenderAddressInfo,
Expand Down
5 changes: 2 additions & 3 deletions lib/btc-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const { retryWithCheck } = require('./utils');
const { getLogger } = require('../logger');
const { btcToSatoshis } = require('@rsksmart/btc-eth-unit-converter');
const BtcTransactionHelper = require('btc-transaction-helper/btc-transaction-helper');
const SpendableUtxosInformation = require('btc-transaction-helper');

const logger = getLogger();

Expand Down Expand Up @@ -108,7 +107,7 @@ const waitForBitcoinTxToBeInMempool = async (btcTxHelper, btcTxHash, maxAttempts
const isTxInMempool = bitcoinMempool.includes(btcTxHash);
if(!isTxInMempool) {
logger.debug(`[${waitForBitcoinTxToBeInMempool.name}::${bitcoinMempoolHasTx.name}] Attempting to check if the btc tx (${btcTxHash}) was already mined since it's not in the mempool yet.`);
const tx = await btcTransactionHelper.getTransaction(btcTxHash);
const tx = await btcTxHelper.getTransaction(btcTxHash);
if(tx) {
logger.debug(`[${waitForBitcoinTxToBeInMempool.name}::${bitcoinMempoolHasTx.name}] The btc tx (${btcTxHash}) was already mined.`);
return true;
Expand Down Expand Up @@ -136,7 +135,7 @@ const waitForBitcoinTxToBeInMempool = async (btcTxHelper, btcTxHash, maxAttempts
throw e;
};

const { result: btcTxAlreadyFoundInMempool } = retryWithCheck(
const { result: btcTxAlreadyFoundInMempool } = await retryWithCheck(
bitcoinMempoolHasTx,
checkBitcoinMempoolHasTx,
maxAttempts,
Expand Down
79 changes: 74 additions & 5 deletions lib/tests/2wp.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ const execute = (description, getRskHost) => {
addOutputToFed(tx, peginValueInSatoshis);

// Adding change outputs
addChangeOutputs(tx, senderRecipientInfo1.btcSenderAddressInfo.address, sender1ChangeInSatoshis);
addChangeOutputs(tx, senderRecipientInfo2.btcSenderAddressInfo.address, sender2ChangeInSatoshis);
addChangeOutput(tx, senderRecipientInfo1.btcSenderAddressInfo.address, sender1ChangeInSatoshis);
addChangeOutput(tx, senderRecipientInfo2.btcSenderAddressInfo.address, sender2ChangeInSatoshis);

const sendersPrivateKeys = [senderRecipientInfo1.btcSenderAddressInfo.privateKey, senderRecipientInfo2.btcSenderAddressInfo.privateKey]
const signedTx = await btcTxHelper.nodeClient.signTransaction(tx.toHex(), [], sendersPrivateKeys);
Expand Down Expand Up @@ -175,13 +175,82 @@ const execute = (description, getRskHost) => {

});

it('should do legacy pegin with multiple inputs from different accounts and two outputs to the federation with value exactly minimum', async () => {

// Arrange

const initial2wpBalances = await get2wpBalances(rskTxHelper, btcTxHelper);
const senderRecipientInfo1 = await createSenderRecipientInfo(rskTxHelper, btcTxHelper);
const senderRecipientInfo2 = await createSenderRecipientInfo(rskTxHelper, btcTxHelper);
const initialSender1AddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo1.btcSenderAddressInfo.address);
const initialSender2AddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo2.btcSenderAddressInfo.address);

const sender1PeginValueInSatoshis = minimumPeginValueInSatoshis;
const sender2PeginValueInSatoshis = minimumPeginValueInSatoshis;
const peginValueInSatoshis = sender1PeginValueInSatoshis + sender2PeginValueInSatoshis;

const sender1UtxosInfo = await getAddressUtxosInfo(btcTxHelper, senderRecipientInfo1.btcSenderAddressInfo.address, satoshisToBtc(sender1PeginValueInSatoshis));
const sender2UtxosInfo = await getAddressUtxosInfo(btcTxHelper, senderRecipientInfo2.btcSenderAddressInfo.address, satoshisToBtc(sender2PeginValueInSatoshis));

const sender1ChangeInSatoshis = btcToSatoshis(sender1UtxosInfo.change);
const sender2ChangeInSatoshis = btcToSatoshis(sender2UtxosInfo.change);

const tx = new bitcoinJsLib.Transaction();

// Adding inputs
addInputs(tx, sender1UtxosInfo);
addInputs(tx, sender2UtxosInfo);

// Adding 2 outputs to the federation
addOutputToFed(tx, sender1PeginValueInSatoshis);
addOutputToFed(tx, sender2PeginValueInSatoshis);

// Adding change outputs
addChangeOutput(tx, senderRecipientInfo1.btcSenderAddressInfo.address, sender1ChangeInSatoshis);
addChangeOutput(tx, senderRecipientInfo2.btcSenderAddressInfo.address, sender2ChangeInSatoshis);

const sendersPrivateKeys = [senderRecipientInfo1.btcSenderAddressInfo.privateKey, senderRecipientInfo2.btcSenderAddressInfo.privateKey];

const signedTx = await btcTxHelper.nodeClient.signTransaction(tx.toHex(), [], sendersPrivateKeys);

// Act

const btcPeginTxHash = await btcTxHelper.nodeClient.sendTransaction(signedTx);
// Assert

// Since we are not using `sendPegin` here, we need to do some extra steps before ensuring the pegin is registered.
const expectedCountOfThisPeginUtxosInTheBridge = 2;
await ensurePeginIsPushed(btcPeginTxHash, expectedCountOfThisPeginUtxosInTheBridge);

await assertExpectedPeginBtcEventIsEmitted(btcPeginTxHash, senderRecipientInfo1.rskRecipientRskAddressInfo.address, peginValueInSatoshis);

await assert2wpBalancesAfterSuccessfulPegin(initial2wpBalances, peginValueInSatoshis);

// The senders should have their balances reduced by the amount sent to the federation and the fee
const finalSender1AddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo1.btcSenderAddressInfo.address);
expect(finalSender1AddressBalanceInSatoshis).to.be.equal(initialSender1AddressBalanceInSatoshis - sender1PeginValueInSatoshis - btcFeeInSatoshis);

const finalSender2AddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo2.btcSenderAddressInfo.address);
expect(finalSender2AddressBalanceInSatoshis).to.be.equal(initialSender2AddressBalanceInSatoshis - sender2PeginValueInSatoshis - btcFeeInSatoshis);

// Only the first sender should have the total amount in rsk since in legacy pegins the rsk address is derived from the first input.
const finalRskRecipient1BalanceInWeisBN = await rskTxHelper.getBalance(senderRecipientInfo1.rskRecipientRskAddressInfo.address);
const expectedFinalRskRecipient1BalanceInWeisBN = rskTxHelper.getClient().utils.BN(satoshisToWeis(peginValueInSatoshis));
expect(finalRskRecipient1BalanceInWeisBN.eq(expectedFinalRskRecipient1BalanceInWeisBN)).to.be.true;

// Other senders should have 0 balance in rsk.
const finalRskRecipient2BalanceInWeisBN = await rskTxHelper.getBalance(senderRecipientInfo2.rskRecipientRskAddressInfo.address);
expect(finalRskRecipient2BalanceInWeisBN.eq(new BN('0'))).to.be.true;

});

});

}

const assertExpectedPeginBtcEventIsEmitted = async (btcPeginTxHash, rskRecipientAddress, peginValueInSatoshis) => {
const recipient1RskAddressChecksumed = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(rskRecipientAddress));
const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, recipient1RskAddressChecksumed, btcPeginTxHash, peginValueInSatoshis);
const recipientRskAddressChecksummed = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(rskRecipientAddress));
const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, recipientRskAddressChecksummed, btcPeginTxHash, peginValueInSatoshis);
const btcTxHashProcessedHeight = Number(await bridge.methods.getBtcTxHashProcessedHeight(btcPeginTxHash).call());
const peginBtcEvent = await findEventInBlock(rskTxHelper, expectedEvent.name, btcTxHashProcessedHeight);
expect(peginBtcEvent).to.be.deep.equal(expectedEvent);
Expand Down Expand Up @@ -213,7 +282,7 @@ const addInputs = (tx, senderUtxosInfo) => {
});
};

const addChangeOutputs = (tx, changeAddress, changeInSatoshis) => {
const addChangeOutput = (tx, changeAddress, changeInSatoshis) => {
if(changeInSatoshis > 0) {
tx.addOutput(
bitcoinJsLib.address.toOutputScript(changeAddress, btcTxHelper.btcConfig.network),
Expand Down

0 comments on commit 61f48f8

Please sign in to comment.