From 40f2ef55fcda52dbe6f459a4d421d498fa9f6522 Mon Sep 17 00:00:00 2001
From: jeremy-then <jeremy.then@iovlabs.org>
Date: Tue, 17 Sep 2024 11:21:41 -0400
Subject: [PATCH] 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);