From 0977110e4a927938ed17ada07ddcecb6a70a73e0 Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Tue, 17 Sep 2024 11:21:41 -0400 Subject: [PATCH 01/10] Adds new 2wp file starting with one pegin test checking events, etc. --- lib/2wp-utils.js | 30 +++++++++++++- lib/assertions/2wp.js | 13 +++++- lib/constants.js | 10 ++++- lib/tests/2wp-new.js | 93 +++++++++++++++++++++++++++++++++++++++++++ tests/01_01_01-2wp.js | 3 ++ 5 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 lib/tests/2wp-new.js create mode 100644 tests/01_01_01-2wp.js diff --git a/lib/2wp-utils.js b/lib/2wp-utils.js index 9a915188..70b108e2 100644 --- a/lib/2wp-utils.js +++ b/lib/2wp-utils.js @@ -7,10 +7,11 @@ const { waitForRskMempoolToGetNewTxs, waitAndUpdateBridge } = require('./rsk-utils'); -const { retryWithCheck } = require('./utils'); +const { retryWithCheck, ensure0x } = require('./utils'); const { waitForBitcoinTxToBeInMempool, waitForBitcoinMempoolToGetTxs } = require('./btc-utils'); const { getBridge } = require('./precompiled-abi-forks-util'); const { getBridgeState } = require('@rsksmart/bridge-state-data-parser'); +const { getDerivedRSKAddressInformation } = require('@rsksmart/btc-rsk-derivation'); const btcEthUnitConverter = require('@rsksmart/btc-eth-unit-converter'); const peginVerifier = require('pegin-address-verificator'); @@ -227,6 +228,31 @@ const disableWhitelisting = async (rskTxHelper, btcTxHelper, blockDelay = 1) => } }; +const createSenderRecipientInfo = async (rskTxHelper, btcTxHelper, type = 'legacy', initialAmountToFundInBtc = 1) => { + const btcSenderAddressInfo = await btcTxHelper.generateBtcAddress(type); + const rskRecipientRskAddressInfo = getDerivedRSKAddressInformation(btcSenderAddressInfo.privateKey, btcTxHelper.btcConfig.network); + await rskTxHelper.importAccount(rskRecipientRskAddressInfo.privateKey); + await rskTxHelper.unlockAccount(rskRecipientRskAddressInfo.address); + initialAmountToFundInBtc && await btcTxHelper.fundAddress(btcSenderAddressInfo.address, initialAmountToFundInBtc); + return { + btcSenderAddressInfo, + rskRecipientRskAddressInfo + }; +}; + +const createExpectedPeginBtcEvent = (partialExpectedEvent, rskRecipientRskAddress, btcPeginTxHash, peginValueInBtc, protocolVersion = '0') => { + const expectedEvent = { + ...partialExpectedEvent, + arguments: { + receiver: ensure0x(rskRecipientRskAddress), + btcTxHash: ensure0x(btcPeginTxHash), + amount: `${btcEthUnitConverter.btcToSatoshis(peginValueInBtc)}`, + protocolVersion, + }, + } + return expectedEvent; +}; + module.exports = { sendTxToBridge, assertRefundUtxosSameAsPeginUtxos, @@ -240,4 +266,6 @@ module.exports = { mineForPeginRegistration, MIN_PEGOUT_VALUE_IN_RBTC, disableWhitelisting, + createSenderRecipientInfo, + createExpectedPeginBtcEvent, }; diff --git a/lib/assertions/2wp.js b/lib/assertions/2wp.js index f6fa6be7..6782cd10 100644 --- a/lib/assertions/2wp.js +++ b/lib/assertions/2wp.js @@ -2,6 +2,9 @@ const expect = require('chai').expect; var {wait, removePrefix0x} = require('../utils'); var bitcoin = require('peglib').bitcoin; var rsk = require('peglib').rsk; +const rskUtils = require('../rsk-utils'); +const CustomError = require('../CustomError'); + const {MAX_ESTIMATED_FEE_PER_PEGOUT, FEE_DIFFERENCE_PER_PEGOUT} = require('../constants'); const {encodeOutpointValuesAsMap, decodeOutpointValues} = require("../varint"); @@ -134,11 +137,19 @@ const assertRejectedPeginEvent = (rejectedPeginTx, expectedRejectionReason, expe expect(outpointValues.every(value => value in federationUtxoValues)).to.be.true; } +const findAndCheckPeginBtcEventAtBlock = async (rskTxHelper, atBlock, expectedEvent) => { + const peginBtcEvent = await rskUtils.findEventInBlock(rskTxHelper, expectedEvent.name, atBlock); + expect(peginBtcEvent).to.exist; + peginBtcEvent.arguments.receiver = peginBtcEvent.arguments.receiver.toLowerCase(); + expect(peginBtcEvent).to.be.deep.equal(expectedEvent); +}; + module.exports = { with: (btcClient, rskClient, pegClient) => ({ assertBitcoinBalance: assertBitcoinBalance(btcClient, rskClient, pegClient), assertLock: assertLock(btcClient, rskClient, pegClient), }), assertCallToPegoutBatchingBridgeMethods, - assertRejectedPeginEvent + assertRejectedPeginEvent, + findAndCheckPeginBtcEventAtBlock, }; diff --git a/lib/constants.js b/lib/constants.js index 193f6fd5..32ab0936 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -55,7 +55,14 @@ const PEGOUT_EVENTS = { BATCH_PEGOUT_CREATED: "batch_pegout_created", PEGOUT_TRANSACTION_CREATED: "pegout_transaction_created", PEGOUT_CONFIRMED: "pegout_confirmed" -} +}; + +const PEGIN_EVENTS = { + PEGIN_BTC: { + name: "pegin_btc", + signature: '0x44cdc782a38244afd68336ab92a0b39f864d6c0b2a50fa1da58cafc93cd2ae5a' + } +}; module.exports = { KEY_TYPE_BTC, @@ -77,4 +84,5 @@ module.exports = { PEGOUT_EVENTS, FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_BEGIN, FUNDS_MIGRATION_AGE_SINCE_ACTIVATION_END, + PEGIN_EVENTS, }; diff --git a/lib/tests/2wp-new.js b/lib/tests/2wp-new.js new file mode 100644 index 00000000..7a68d341 --- /dev/null +++ b/lib/tests/2wp-new.js @@ -0,0 +1,93 @@ +const expect = require('chai').expect; +const { getBridge } = require('../precompiled-abi-forks-util'); +const { getBtcClient } = require('../btc-client-provider'); +const { getRskTransactionHelpers, getRskTransactionHelper } = require('../rsk-tx-helper-provider'); +const { satoshisToBtc, btcToWeis } = require('@rsksmart/btc-eth-unit-converter'); +const { waitAndUpdateBridge, mineAndSync } = require('../rsk-utils'); +const { PEGIN_EVENTS } = require("../constants"); +const { findAndCheckPeginBtcEventAtBlock } = require('../assertions/2wp'); +const { sendPegin, + ensurePeginIsRegistered, + donateToBridge, + createSenderRecipientInfo, + createExpectedPeginBtcEvent +} = require('../2wp-utils'); + +const DONATION_AMOUNT = 250; + +let btcTxHelper; +let rskTxHelper; +let rskTxHelpers; +let bridge; +let federationAddress; +let minimumPeginValueInBtc; + +const setupBridgeDonation = async (rskTxHelpers, btcTxHelper) => { + const donatingBtcAddressInformation = await btcTxHelper.generateBtcAddress('legacy'); + await mineAndSync(rskTxHelpers); + await btcTxHelper.fundAddress(donatingBtcAddressInformation.address, DONATION_AMOUNT + btcTxHelper.getFee()); + await donateToBridge(rskTxHelpers[0], btcTxHelper, donatingBtcAddressInformation, DONATION_AMOUNT); +}; + +const execute = (description, getRskHost) => { + + describe(description, () => { + + before(async () => { + + rskTxHelpers = getRskTransactionHelpers(); + btcTxHelper = getBtcClient(); + rskTxHelper = getRskTransactionHelper(getRskHost()); + bridge = getBridge(rskTxHelper.getClient()); + + federationAddress = await bridge.methods.getFederationAddress().call(); + const minimumPeginValueInSatoshis = await bridge.methods.getMinimumLockTxValue().call(); + minimumPeginValueInBtc = Number(satoshisToBtc(minimumPeginValueInSatoshis)); + + await btcTxHelper.importAddress(federationAddress, 'federation'); + await waitAndUpdateBridge(rskTxHelper); + await setupBridgeDonation(rskTxHelpers, btcTxHelper); + + }); + + it('should transfer BTC to RBTC', async () => { + + // Arrange + + const senderInfo = await createSenderRecipientInfo(rskTxHelper, btcTxHelper); + const initialFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); + const initialSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); + const initialRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); + + // Act + + const btcPeginTxHash = await sendPegin(rskTxHelper, btcTxHelper, senderInfo.btcSenderAddressInfo, minimumPeginValueInBtc); + await ensurePeginIsRegistered(rskTxHelper, btcPeginTxHash); + + // Assert + + const isBtcTxHashAlreadyProcessed = await bridge.methods.isBtcTxHashAlreadyProcessed(btcPeginTxHash).call(); + expect(isBtcTxHashAlreadyProcessed).to.be.true; + + const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, senderInfo.rskRecipientRskAddressInfo.address, btcPeginTxHash, minimumPeginValueInBtc, '0'); + const btcTxHashProcessedHeight = Number(await bridge.methods.getBtcTxHashProcessedHeight(btcPeginTxHash).call()); + await findAndCheckPeginBtcEventAtBlock(rskTxHelper, btcTxHashProcessedHeight, expectedEvent); + + const finalFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); + expect(finalFederationAddressBalanceInBtc).to.be.equal(initialFederationAddressBalanceInBtc + minimumPeginValueInBtc); + + const finalSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); + expect(finalSenderAddressBalanceInBtc).to.be.equal(initialSenderAddressBalanceInBtc - minimumPeginValueInBtc - btcTxHelper.getFee()); + + const finalRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); + expect(finalRskRecipientBalance).to.be.equal(initialRskRecipientBalance + Number(btcToWeis(minimumPeginValueInBtc))); + + }); + + }); + +} + +module.exports = { + execute, +}; diff --git a/tests/01_01_01-2wp.js b/tests/01_01_01-2wp.js new file mode 100644 index 00000000..6ee4652d --- /dev/null +++ b/tests/01_01_01-2wp.js @@ -0,0 +1,3 @@ +const twoWpTests = require('../lib/tests/2wp-new'); + +twoWpTests.execute('BTC <=> RSK 2WP', () => Runners.hosts.federate.host); From d22bb7e673789e273111901abc018235e2cdc591 Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Tue, 17 Sep 2024 11:29:55 -0400 Subject: [PATCH 02/10] Updates doc comment return type on donateToBridge. --- lib/2wp-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/2wp-utils.js b/lib/2wp-utils.js index 70b108e2..03c6be43 100644 --- a/lib/2wp-utils.js +++ b/lib/2wp-utils.js @@ -199,7 +199,7 @@ const ensurePeginIsRegistered = async (rskTxHelper, peginBtcTxHash, expectedUtxo * @param {RskTransactionHelper} rskTxHelper * @param {BtcTransactionHelper} btcTxHelper * @param {number} amountInBtc - * @returns {string} the pegin tx hash + * @returns {Promise} the pegin tx hash */ const donateToBridge = async (rskTxHelper, btcTxHelper, donatingBtcAddressInformation, amountInBtc) => { const data = []; From 8d3806930ba6a5a2b7630fce28a67c611a9e6628 Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Tue, 17 Sep 2024 11:38:18 -0400 Subject: [PATCH 03/10] Renames find and check event function. --- lib/assertions/2wp.js | 4 +-- lib/tests/{2wp-new.js => 2wp.js} | 42 ++++++++++++++++---------------- tests/01_01_01-2wp.js | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) rename lib/tests/{2wp-new.js => 2wp.js} (53%) diff --git a/lib/assertions/2wp.js b/lib/assertions/2wp.js index 6782cd10..88e1f6e3 100644 --- a/lib/assertions/2wp.js +++ b/lib/assertions/2wp.js @@ -137,7 +137,7 @@ const assertRejectedPeginEvent = (rejectedPeginTx, expectedRejectionReason, expe expect(outpointValues.every(value => value in federationUtxoValues)).to.be.true; } -const findAndCheckPeginBtcEventAtBlock = async (rskTxHelper, atBlock, expectedEvent) => { +const findAndCheckPeginBtcEventInBlock = async (rskTxHelper, atBlock, expectedEvent) => { const peginBtcEvent = await rskUtils.findEventInBlock(rskTxHelper, expectedEvent.name, atBlock); expect(peginBtcEvent).to.exist; peginBtcEvent.arguments.receiver = peginBtcEvent.arguments.receiver.toLowerCase(); @@ -151,5 +151,5 @@ module.exports = { }), assertCallToPegoutBatchingBridgeMethods, assertRejectedPeginEvent, - findAndCheckPeginBtcEventAtBlock, + findAndCheckPeginBtcEventInBlock, }; diff --git a/lib/tests/2wp-new.js b/lib/tests/2wp.js similarity index 53% rename from lib/tests/2wp-new.js rename to lib/tests/2wp.js index 7a68d341..81f85457 100644 --- a/lib/tests/2wp-new.js +++ b/lib/tests/2wp.js @@ -5,7 +5,7 @@ const { getRskTransactionHelpers, getRskTransactionHelper } = require('../rsk-tx const { satoshisToBtc, btcToWeis } = require('@rsksmart/btc-eth-unit-converter'); const { waitAndUpdateBridge, mineAndSync } = require('../rsk-utils'); const { PEGIN_EVENTS } = require("../constants"); -const { findAndCheckPeginBtcEventAtBlock } = require('../assertions/2wp'); +const { findAndCheckPeginBtcEventInBlock } = require('../assertions/2wp'); const { sendPegin, ensurePeginIsRegistered, donateToBridge, @@ -52,35 +52,35 @@ const execute = (description, getRskHost) => { it('should transfer BTC to RBTC', async () => { - // Arrange + // Arrange - const senderInfo = await createSenderRecipientInfo(rskTxHelper, btcTxHelper); - const initialFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); - const initialSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); - const initialRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); + const senderInfo = await createSenderRecipientInfo(rskTxHelper, btcTxHelper); + const initialFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); + const initialSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); + const initialRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); - // Act + // Act - const btcPeginTxHash = await sendPegin(rskTxHelper, btcTxHelper, senderInfo.btcSenderAddressInfo, minimumPeginValueInBtc); - await ensurePeginIsRegistered(rskTxHelper, btcPeginTxHash); + const btcPeginTxHash = await sendPegin(rskTxHelper, btcTxHelper, senderInfo.btcSenderAddressInfo, minimumPeginValueInBtc); + await ensurePeginIsRegistered(rskTxHelper, btcPeginTxHash); - // Assert + // Assert - const isBtcTxHashAlreadyProcessed = await bridge.methods.isBtcTxHashAlreadyProcessed(btcPeginTxHash).call(); - expect(isBtcTxHashAlreadyProcessed).to.be.true; + const isBtcTxHashAlreadyProcessed = await bridge.methods.isBtcTxHashAlreadyProcessed(btcPeginTxHash).call(); + expect(isBtcTxHashAlreadyProcessed).to.be.true; - const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, senderInfo.rskRecipientRskAddressInfo.address, btcPeginTxHash, minimumPeginValueInBtc, '0'); - const btcTxHashProcessedHeight = Number(await bridge.methods.getBtcTxHashProcessedHeight(btcPeginTxHash).call()); - await findAndCheckPeginBtcEventAtBlock(rskTxHelper, btcTxHashProcessedHeight, expectedEvent); + const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, senderInfo.rskRecipientRskAddressInfo.address, btcPeginTxHash, minimumPeginValueInBtc, '0'); + const btcTxHashProcessedHeight = Number(await bridge.methods.getBtcTxHashProcessedHeight(btcPeginTxHash).call()); + await findAndCheckPeginBtcEventInBlock(rskTxHelper, btcTxHashProcessedHeight, expectedEvent); - const finalFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); - expect(finalFederationAddressBalanceInBtc).to.be.equal(initialFederationAddressBalanceInBtc + minimumPeginValueInBtc); + const finalFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); + expect(finalFederationAddressBalanceInBtc).to.be.equal(initialFederationAddressBalanceInBtc + minimumPeginValueInBtc); - const finalSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); - expect(finalSenderAddressBalanceInBtc).to.be.equal(initialSenderAddressBalanceInBtc - minimumPeginValueInBtc - btcTxHelper.getFee()); + const finalSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); + expect(finalSenderAddressBalanceInBtc).to.be.equal(initialSenderAddressBalanceInBtc - minimumPeginValueInBtc - btcTxHelper.getFee()); - const finalRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); - expect(finalRskRecipientBalance).to.be.equal(initialRskRecipientBalance + Number(btcToWeis(minimumPeginValueInBtc))); + const finalRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); + expect(finalRskRecipientBalance).to.be.equal(initialRskRecipientBalance + Number(btcToWeis(minimumPeginValueInBtc))); }); diff --git a/tests/01_01_01-2wp.js b/tests/01_01_01-2wp.js index 6ee4652d..5e39c8c5 100644 --- a/tests/01_01_01-2wp.js +++ b/tests/01_01_01-2wp.js @@ -1,3 +1,3 @@ -const twoWpTests = require('../lib/tests/2wp-new'); +const twoWpTests = require('../lib/tests/2wp'); twoWpTests.execute('BTC <=> RSK 2WP', () => Runners.hosts.federate.host); From f530baa28831bdb7cdf1205a8e2d9e5ccaa920e2 Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Tue, 17 Sep 2024 11:56:44 -0400 Subject: [PATCH 04/10] Adds doc comments. --- lib/2wp-utils.js | 21 +++++++++++++++++++-- lib/assertions/2wp.js | 7 +++++++ lib/tests/2wp.js | 4 ++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/2wp-utils.js b/lib/2wp-utils.js index 03c6be43..176637e6 100644 --- a/lib/2wp-utils.js +++ b/lib/2wp-utils.js @@ -228,6 +228,14 @@ const disableWhitelisting = async (rskTxHelper, btcTxHelper, blockDelay = 1) => } }; +/** + * Creates a btc sender and rsk recipient information (private keys and addresses) and funds the btc sender address with the specified amount. + * @param {RskTransactionHelper} rskTxHelper to make transactions to the rsk network. + * @param {BtcTransactionHelper} btcTxHelper to make transactions to the bitcoin network. + * @param {string} type the btc address type to generate. Defaults to 'legacy'. + * @param {number} initialAmountToFundInBtc the initial amount to fund the btc sender address. Defaults to 1. + * @returns {Promise<{btcSenderAddressInfo: {address: string, privateKey: string}, rskRecipientRskAddressInfo: {address: string, privateKey: string}>}} + */ const createSenderRecipientInfo = async (rskTxHelper, btcTxHelper, type = 'legacy', initialAmountToFundInBtc = 1) => { const btcSenderAddressInfo = await btcTxHelper.generateBtcAddress(type); const rskRecipientRskAddressInfo = getDerivedRSKAddressInformation(btcSenderAddressInfo.privateKey, btcTxHelper.btcConfig.network); @@ -240,13 +248,22 @@ const createSenderRecipientInfo = async (rskTxHelper, btcTxHelper, type = 'legac }; }; -const createExpectedPeginBtcEvent = (partialExpectedEvent, rskRecipientRskAddress, btcPeginTxHash, peginValueInBtc, protocolVersion = '0') => { +/** + * Creates a pegin_btc event with the specified parameters. + * @param {Object} partialExpectedEvent an object with some pegin_btc event default values. + * @param {string} rskRecipientRskAddress the rsk address that receives the funds expected to be in the event. + * @param {string} btcPeginTxHash the pegin btc tx hash expected to be in the event. + * @param {number} peginValueInSatoshis the pegin value in satoshis expected to be in the event. + * @param {string} protocolVersion the pegin protocol version expected to be in the event. Defaults to '0'. + * @returns {BridgeEvent} + */ +const createExpectedPeginBtcEvent = (partialExpectedEvent, rskRecipientRskAddress, btcPeginTxHash, peginValueInSatoshis, protocolVersion = '0') => { const expectedEvent = { ...partialExpectedEvent, arguments: { receiver: ensure0x(rskRecipientRskAddress), btcTxHash: ensure0x(btcPeginTxHash), - amount: `${btcEthUnitConverter.btcToSatoshis(peginValueInBtc)}`, + amount: `${peginValueInSatoshis}`, protocolVersion, }, } diff --git a/lib/assertions/2wp.js b/lib/assertions/2wp.js index 88e1f6e3..0fd839c8 100644 --- a/lib/assertions/2wp.js +++ b/lib/assertions/2wp.js @@ -137,6 +137,13 @@ const assertRejectedPeginEvent = (rejectedPeginTx, expectedRejectionReason, expe expect(outpointValues.every(value => value in federationUtxoValues)).to.be.true; } +/** + * Searches for and checks that the pegin_btc event is found in the block. + * @param {RskTransactionHelper} rskTxHelper to make transactions to the rsk network. + * @param {number} atBlock the block number to look for the event. + * @param {BridgeEvent} expectedEvent the expected event to be found in the block. + * @returns {Promise} + */ const findAndCheckPeginBtcEventInBlock = async (rskTxHelper, atBlock, expectedEvent) => { const peginBtcEvent = await rskUtils.findEventInBlock(rskTxHelper, expectedEvent.name, atBlock); expect(peginBtcEvent).to.exist; diff --git a/lib/tests/2wp.js b/lib/tests/2wp.js index 81f85457..c325a690 100644 --- a/lib/tests/2wp.js +++ b/lib/tests/2wp.js @@ -2,7 +2,7 @@ const expect = require('chai').expect; const { getBridge } = require('../precompiled-abi-forks-util'); const { getBtcClient } = require('../btc-client-provider'); const { getRskTransactionHelpers, getRskTransactionHelper } = require('../rsk-tx-helper-provider'); -const { satoshisToBtc, btcToWeis } = require('@rsksmart/btc-eth-unit-converter'); +const { satoshisToBtc, btcToWeis, btcToSatoshis } = require('@rsksmart/btc-eth-unit-converter'); const { waitAndUpdateBridge, mineAndSync } = require('../rsk-utils'); const { PEGIN_EVENTS } = require("../constants"); const { findAndCheckPeginBtcEventInBlock } = require('../assertions/2wp'); @@ -69,7 +69,7 @@ const execute = (description, getRskHost) => { const isBtcTxHashAlreadyProcessed = await bridge.methods.isBtcTxHashAlreadyProcessed(btcPeginTxHash).call(); expect(isBtcTxHashAlreadyProcessed).to.be.true; - const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, senderInfo.rskRecipientRskAddressInfo.address, btcPeginTxHash, minimumPeginValueInBtc, '0'); + const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, senderInfo.rskRecipientRskAddressInfo.address, btcPeginTxHash, btcToSatoshis(minimumPeginValueInBtc), '0'); const btcTxHashProcessedHeight = Number(await bridge.methods.getBtcTxHashProcessedHeight(btcPeginTxHash).call()); await findAndCheckPeginBtcEventInBlock(rskTxHelper, btcTxHashProcessedHeight, expectedEvent); From 13bf6a72e86862ff7c9d92c8f4cfc9fac4f08cda Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Mon, 23 Sep 2024 20:01:35 -0400 Subject: [PATCH 05/10] Refactors to use satoshis. Renames. --- lib/assertions/2wp.js | 17 ---------------- lib/btc-utils.js | 8 +++++++- lib/tests/2wp.js | 47 ++++++++++++++++++++++++++----------------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/assertions/2wp.js b/lib/assertions/2wp.js index 0fd839c8..7a9081fe 100644 --- a/lib/assertions/2wp.js +++ b/lib/assertions/2wp.js @@ -2,8 +2,6 @@ const expect = require('chai').expect; var {wait, removePrefix0x} = require('../utils'); var bitcoin = require('peglib').bitcoin; var rsk = require('peglib').rsk; -const rskUtils = require('../rsk-utils'); -const CustomError = require('../CustomError'); const {MAX_ESTIMATED_FEE_PER_PEGOUT, FEE_DIFFERENCE_PER_PEGOUT} = require('../constants'); const {encodeOutpointValuesAsMap, decodeOutpointValues} = require("../varint"); @@ -137,20 +135,6 @@ const assertRejectedPeginEvent = (rejectedPeginTx, expectedRejectionReason, expe expect(outpointValues.every(value => value in federationUtxoValues)).to.be.true; } -/** - * Searches for and checks that the pegin_btc event is found in the block. - * @param {RskTransactionHelper} rskTxHelper to make transactions to the rsk network. - * @param {number} atBlock the block number to look for the event. - * @param {BridgeEvent} expectedEvent the expected event to be found in the block. - * @returns {Promise} - */ -const findAndCheckPeginBtcEventInBlock = async (rskTxHelper, atBlock, expectedEvent) => { - const peginBtcEvent = await rskUtils.findEventInBlock(rskTxHelper, expectedEvent.name, atBlock); - expect(peginBtcEvent).to.exist; - peginBtcEvent.arguments.receiver = peginBtcEvent.arguments.receiver.toLowerCase(); - expect(peginBtcEvent).to.be.deep.equal(expectedEvent); -}; - module.exports = { with: (btcClient, rskClient, pegClient) => ({ assertBitcoinBalance: assertBitcoinBalance(btcClient, rskClient, pegClient), @@ -158,5 +142,4 @@ module.exports = { }), assertCallToPegoutBatchingBridgeMethods, assertRejectedPeginEvent, - findAndCheckPeginBtcEventInBlock, }; diff --git a/lib/btc-utils.js b/lib/btc-utils.js index 19556ea9..0e97fe9b 100644 --- a/lib/btc-utils.js +++ b/lib/btc-utils.js @@ -3,6 +3,7 @@ const merkleLib = require('merkle-lib'); const pmtBuilder = require('@rsksmart/pmt-builder'); const { retryWithCheck } = require('./utils'); const { getLogger } = require('../logger'); +const { btcToSatoshis } = require('@rsksmart/btc-eth-unit-converter'); const logger = getLogger(); @@ -181,10 +182,15 @@ const waitForBitcoinMempoolToGetTxs = async (btcTxHelper, maxAttempts = 3, check return bitcoinMempoolHasTx; } + const getBtcAddressBalanceInSatoshis = async (btcTxHelper, btcAddress) => { + return Number(btcToSatoshis(await btcTxHelper.getAddressBalance(btcAddress))); + }; + module.exports = { publicKeyToCompressed, fundAddressAndGetData, getBitcoinTransactionsInMempool, waitForBitcoinTxToBeInMempool, - waitForBitcoinMempoolToGetTxs + waitForBitcoinMempoolToGetTxs, + getBtcAddressBalanceInSatoshis, } diff --git a/lib/tests/2wp.js b/lib/tests/2wp.js index c325a690..0bf3e11d 100644 --- a/lib/tests/2wp.js +++ b/lib/tests/2wp.js @@ -2,16 +2,17 @@ const expect = require('chai').expect; const { getBridge } = require('../precompiled-abi-forks-util'); const { getBtcClient } = require('../btc-client-provider'); const { getRskTransactionHelpers, getRskTransactionHelper } = require('../rsk-tx-helper-provider'); -const { satoshisToBtc, btcToWeis, btcToSatoshis } = require('@rsksmart/btc-eth-unit-converter'); -const { waitAndUpdateBridge, mineAndSync } = require('../rsk-utils'); +const { satoshisToBtc, btcToSatoshis, satoshisToWeis } = require('@rsksmart/btc-eth-unit-converter'); +const { waitAndUpdateBridge, mineAndSync, findEventInBlock } = require('../rsk-utils'); const { PEGIN_EVENTS } = require("../constants"); -const { findAndCheckPeginBtcEventInBlock } = require('../assertions/2wp'); const { sendPegin, ensurePeginIsRegistered, donateToBridge, createSenderRecipientInfo, createExpectedPeginBtcEvent } = require('../2wp-utils'); +const { ensure0x } = require('../utils'); +const { getBtcAddressBalanceInSatoshis } = require('../btc-utils'); const DONATION_AMOUNT = 250; @@ -20,7 +21,9 @@ let rskTxHelper; let rskTxHelpers; let bridge; let federationAddress; +let minimumPeginValueInSatoshis; let minimumPeginValueInBtc; +let btcFeeInSatoshis; const setupBridgeDonation = async (rskTxHelpers, btcTxHelper) => { const donatingBtcAddressInformation = await btcTxHelper.generateBtcAddress('legacy'); @@ -41,8 +44,9 @@ const execute = (description, getRskHost) => { bridge = getBridge(rskTxHelper.getClient()); federationAddress = await bridge.methods.getFederationAddress().call(); - const minimumPeginValueInSatoshis = await bridge.methods.getMinimumLockTxValue().call(); + minimumPeginValueInSatoshis = Number(await bridge.methods.getMinimumLockTxValue().call()); minimumPeginValueInBtc = Number(satoshisToBtc(minimumPeginValueInSatoshis)); + btcFeeInSatoshis = btcToSatoshis(await btcTxHelper.getFee()); await btcTxHelper.importAddress(federationAddress, 'federation'); await waitAndUpdateBridge(rskTxHelper); @@ -50,37 +54,44 @@ const execute = (description, getRskHost) => { }); - it('should transfer BTC to RBTC', async () => { + it('should do a basic legacy pegin', async () => { // Arrange - const senderInfo = await createSenderRecipientInfo(rskTxHelper, btcTxHelper); - const initialFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); - const initialSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); - const initialRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); + const initialFederationAddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, federationAddress); + const senderRecipientInfo = await createSenderRecipientInfo(rskTxHelper, btcTxHelper); + const initialSenderAddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo.btcSenderAddressInfo.address); + const peginValueInSatoshis = minimumPeginValueInSatoshis; // Act - const btcPeginTxHash = await sendPegin(rskTxHelper, btcTxHelper, senderInfo.btcSenderAddressInfo, minimumPeginValueInBtc); + const btcPeginTxHash = await sendPegin(rskTxHelper, btcTxHelper, senderRecipientInfo.btcSenderAddressInfo, satoshisToBtc(peginValueInSatoshis)); await ensurePeginIsRegistered(rskTxHelper, btcPeginTxHash); // Assert + // The btc pegin tx is already marked as processed by the bridge const isBtcTxHashAlreadyProcessed = await bridge.methods.isBtcTxHashAlreadyProcessed(btcPeginTxHash).call(); expect(isBtcTxHashAlreadyProcessed).to.be.true; - const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, senderInfo.rskRecipientRskAddressInfo.address, btcPeginTxHash, btcToSatoshis(minimumPeginValueInBtc), '0'); + // The pegin_btc event is emitted with the expected values + const recipient1RskAddressChecksumed = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(senderRecipientInfo.rskRecipientRskAddressInfo.address)); + const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, recipient1RskAddressChecksumed, btcPeginTxHash, btcToSatoshis(minimumPeginValueInBtc)); const btcTxHashProcessedHeight = Number(await bridge.methods.getBtcTxHashProcessedHeight(btcPeginTxHash).call()); - await findAndCheckPeginBtcEventInBlock(rskTxHelper, btcTxHashProcessedHeight, expectedEvent); + const peginBtcEvent = await findEventInBlock(rskTxHelper, expectedEvent.name, btcTxHashProcessedHeight); + expect(peginBtcEvent).to.be.deep.equal(expectedEvent); - const finalFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); - expect(finalFederationAddressBalanceInBtc).to.be.equal(initialFederationAddressBalanceInBtc + minimumPeginValueInBtc); + // The federation balance is increased by the pegin value + const finalFederationAddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, federationAddress); + expect(finalFederationAddressBalanceInSatoshis).to.be.equal(initialFederationAddressBalanceInSatoshis + peginValueInSatoshis); - const finalSenderAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address)); - expect(finalSenderAddressBalanceInBtc).to.be.equal(initialSenderAddressBalanceInBtc - minimumPeginValueInBtc - btcTxHelper.getFee()); + // The sender address balance is decreased by the pegin value and the btc fee + const finalSenderAddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo.btcSenderAddressInfo.address); + expect(finalSenderAddressBalanceInSatoshis).to.be.equal(initialSenderAddressBalanceInSatoshis - peginValueInSatoshis - btcFeeInSatoshis); - const finalRskRecipientBalance = Number(await rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address)); - expect(finalRskRecipientBalance).to.be.equal(initialRskRecipientBalance + Number(btcToWeis(minimumPeginValueInBtc))); + // The recipient rsk address balance is increased by the pegin value + const finalRskRecipientBalance = Number(await rskTxHelper.getBalance(senderRecipientInfo.rskRecipientRskAddressInfo.address)); + expect(finalRskRecipientBalance).to.be.equal(Number(satoshisToWeis(peginValueInSatoshis))); }); From e136ffed1c324e319ed89c42590cb104694bc0b4 Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Mon, 23 Sep 2024 20:04:18 -0400 Subject: [PATCH 06/10] Renames tests --- lib/tests/2wp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tests/2wp.js b/lib/tests/2wp.js index 0bf3e11d..fc6d7f51 100644 --- a/lib/tests/2wp.js +++ b/lib/tests/2wp.js @@ -54,7 +54,7 @@ const execute = (description, getRskHost) => { }); - it('should do a basic legacy pegin', async () => { + it('should do a basic legacy pegin with the exact minimum value', async () => { // Arrange From a81d285e80ff389618306f49f61ee1b342420112 Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Mon, 23 Sep 2024 20:47:32 -0400 Subject: [PATCH 07/10] Using peginValueInSatoshis for the createExpectedPeginBtcEvent --- lib/tests/2wp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tests/2wp.js b/lib/tests/2wp.js index fc6d7f51..02b0f4d6 100644 --- a/lib/tests/2wp.js +++ b/lib/tests/2wp.js @@ -76,7 +76,7 @@ const execute = (description, getRskHost) => { // The pegin_btc event is emitted with the expected values const recipient1RskAddressChecksumed = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(senderRecipientInfo.rskRecipientRskAddressInfo.address)); - const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, recipient1RskAddressChecksumed, btcPeginTxHash, btcToSatoshis(minimumPeginValueInBtc)); + const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, recipient1RskAddressChecksumed, 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); From 58deab1d50dff85dffa411249ded1e69edd3213e Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Wed, 25 Sep 2024 12:47:36 -0400 Subject: [PATCH 08/10] Asserting that the bridge own balance and bridge utxos sum is as expected after a successful legacy pegin. --- lib/tests/2wp.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/tests/2wp.js b/lib/tests/2wp.js index 02b0f4d6..413890ef 100644 --- a/lib/tests/2wp.js +++ b/lib/tests/2wp.js @@ -3,18 +3,17 @@ const { getBridge } = require('../precompiled-abi-forks-util'); const { getBtcClient } = require('../btc-client-provider'); const { getRskTransactionHelpers, getRskTransactionHelper } = require('../rsk-tx-helper-provider'); const { satoshisToBtc, btcToSatoshis, satoshisToWeis } = require('@rsksmart/btc-eth-unit-converter'); -const { waitAndUpdateBridge, mineAndSync, findEventInBlock } = require('../rsk-utils'); +const { findEventInBlock } = require('../rsk-utils'); const { PEGIN_EVENTS } = require("../constants"); const { sendPegin, ensurePeginIsRegistered, - donateToBridge, createSenderRecipientInfo, - createExpectedPeginBtcEvent + createExpectedPeginBtcEvent, + BRIDGE_ADDRESS } = require('../2wp-utils'); const { ensure0x } = require('../utils'); const { getBtcAddressBalanceInSatoshis } = require('../btc-utils'); - -const DONATION_AMOUNT = 250; +const { getBridgeState } = require('@rsksmart/bridge-state-data-parser'); let btcTxHelper; let rskTxHelper; @@ -25,11 +24,10 @@ let minimumPeginValueInSatoshis; let minimumPeginValueInBtc; let btcFeeInSatoshis; -const setupBridgeDonation = async (rskTxHelpers, btcTxHelper) => { - const donatingBtcAddressInformation = await btcTxHelper.generateBtcAddress('legacy'); - await mineAndSync(rskTxHelpers); - await btcTxHelper.fundAddress(donatingBtcAddressInformation.address, DONATION_AMOUNT + btcTxHelper.getFee()); - await donateToBridge(rskTxHelpers[0], btcTxHelper, donatingBtcAddressInformation, DONATION_AMOUNT); +const getBridgeUtxosBalance = async (rskTxHelper) => { + const bridgeState = await getBridgeState(rskTxHelper.getClient()); + const utxosSum = bridgeState.activeFederationUtxos.reduce((sum, utxo) => sum + utxo.valueInSatoshis, 0); + return utxosSum; }; const execute = (description, getRskHost) => { @@ -49,8 +47,6 @@ const execute = (description, getRskHost) => { btcFeeInSatoshis = btcToSatoshis(await btcTxHelper.getFee()); await btcTxHelper.importAddress(federationAddress, 'federation'); - await waitAndUpdateBridge(rskTxHelper); - await setupBridgeDonation(rskTxHelpers, btcTxHelper); }); @@ -58,6 +54,8 @@ const execute = (description, getRskHost) => { // Arrange + const initialBridgeBalance = Number(await rskTxHelper.getBalance(BRIDGE_ADDRESS)); + const initialBridgeUtxosBalance = await getBridgeUtxosBalance(rskTxHelper); const initialFederationAddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, federationAddress); const senderRecipientInfo = await createSenderRecipientInfo(rskTxHelper, btcTxHelper); const initialSenderAddressBalanceInSatoshis = await getBtcAddressBalanceInSatoshis(btcTxHelper, senderRecipientInfo.btcSenderAddressInfo.address); @@ -93,6 +91,14 @@ const execute = (description, getRskHost) => { const finalRskRecipientBalance = Number(await rskTxHelper.getBalance(senderRecipientInfo.rskRecipientRskAddressInfo.address)); expect(finalRskRecipientBalance).to.be.equal(Number(satoshisToWeis(peginValueInSatoshis))); + // After the successful pegin, the Bridge balance should be reduced by the pegin value + const finalBridgeBalance = Number(await rskTxHelper.getBalance(BRIDGE_ADDRESS)); + expect(finalBridgeBalance).to.be.equal(initialBridgeBalance - satoshisToWeis(peginValueInSatoshis)); + + // After the successful pegin, the Bridge utxos sum should be incremented by the pegin value + const finalBridgeUtxosBalance = await getBridgeUtxosBalance(rskTxHelper); + expect(finalBridgeUtxosBalance).to.be.equal(initialBridgeUtxosBalance + peginValueInSatoshis); + }); }); From 955ad29caf2e749389f1d3c42f796d86fb19f181 Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Wed, 25 Sep 2024 13:17:32 -0400 Subject: [PATCH 09/10] Moves getBridgeUtxosBalance function to 2wp-utils.js --- lib/2wp-utils.js | 12 ++++++++++++ lib/tests/2wp.js | 10 ++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/2wp-utils.js b/lib/2wp-utils.js index 176637e6..97f558b3 100644 --- a/lib/2wp-utils.js +++ b/lib/2wp-utils.js @@ -270,6 +270,17 @@ const createExpectedPeginBtcEvent = (partialExpectedEvent, rskRecipientRskAddres return expectedEvent; }; +/** + * Gets the Bridge state and sums the utxos amount + * @param {RskTransactionHelper} rskTxHelper to make transactions to the rsk network + * @returns {number} the sum of the utxos in the Bridge + */ +const getBridgeUtxosBalance = async (rskTxHelper) => { + const bridgeState = await getBridgeState(rskTxHelper.getClient()); + const utxosSum = bridgeState.activeFederationUtxos.reduce((sum, utxo) => sum + utxo.valueInSatoshis, 0); + return utxosSum; +}; + module.exports = { sendTxToBridge, assertRefundUtxosSameAsPeginUtxos, @@ -285,4 +296,5 @@ module.exports = { disableWhitelisting, createSenderRecipientInfo, createExpectedPeginBtcEvent, + getBridgeUtxosBalance, }; diff --git a/lib/tests/2wp.js b/lib/tests/2wp.js index 413890ef..d866bb87 100644 --- a/lib/tests/2wp.js +++ b/lib/tests/2wp.js @@ -9,11 +9,11 @@ const { sendPegin, ensurePeginIsRegistered, createSenderRecipientInfo, createExpectedPeginBtcEvent, - BRIDGE_ADDRESS + BRIDGE_ADDRESS, + getBridgeUtxosBalance, } = require('../2wp-utils'); const { ensure0x } = require('../utils'); const { getBtcAddressBalanceInSatoshis } = require('../btc-utils'); -const { getBridgeState } = require('@rsksmart/bridge-state-data-parser'); let btcTxHelper; let rskTxHelper; @@ -24,12 +24,6 @@ let minimumPeginValueInSatoshis; let minimumPeginValueInBtc; let btcFeeInSatoshis; -const getBridgeUtxosBalance = async (rskTxHelper) => { - const bridgeState = await getBridgeState(rskTxHelper.getClient()); - const utxosSum = bridgeState.activeFederationUtxos.reduce((sum, utxo) => sum + utxo.valueInSatoshis, 0); - return utxosSum; -}; - const execute = (description, getRskHost) => { describe(description, () => { From d4fe6d96dda84e2d0363dce665f428e8e3d8803d Mon Sep 17 00:00:00 2001 From: jeremy-then Date: Wed, 25 Sep 2024 13:22:31 -0400 Subject: [PATCH 10/10] Updates getBridgeUtxosBalance doc comment --- lib/2wp-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/2wp-utils.js b/lib/2wp-utils.js index 97f558b3..5c36ded3 100644 --- a/lib/2wp-utils.js +++ b/lib/2wp-utils.js @@ -273,7 +273,7 @@ const createExpectedPeginBtcEvent = (partialExpectedEvent, rskRecipientRskAddres /** * Gets the Bridge state and sums the utxos amount * @param {RskTransactionHelper} rskTxHelper to make transactions to the rsk network - * @returns {number} the sum of the utxos in the Bridge + * @returns {Promise} the sum of the utxos in the Bridge */ const getBridgeUtxosBalance = async (rskTxHelper) => { const bridgeState = await getBridgeState(rskTxHelper.getClient());