Skip to content

Commit

Permalink
Adds 'should reject and not refund a pegout when a contract is trying…
Browse files Browse the repository at this point in the history
… to execute it' test

Cherry-picking pegout from contract commit
  • Loading branch information
jeremy-then committed Oct 28, 2024
1 parent 58d00aa commit 9ed7282
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 7 deletions.
15 changes: 15 additions & 0 deletions contracts/CallReleaseBtcContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity ^0.8.0;

interface BridgeInterface {
function releaseBtc() external payable;
}

contract CallReleaseBtcContract {

BridgeInterface public bridgeContract = BridgeInterface(0x0000000000000000000000000000000001000006);

function callBridgeReleaseBtc() external payable {
bridgeContract.releaseBtc{value:msg.value}();
}

}
40 changes: 40 additions & 0 deletions lib/contractDeployer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

const fs = require('fs');
const path = require('path');
const { btcToWeis } = require('@rsksmart/btc-eth-unit-converter');
const solUtils = require('./sol-utils');
const TEST_RELEASE_BTC_CONTRACT = '../contracts/CallReleaseBtcContract.sol';
const TEST_RELEASE_BTC_CONTRACT_NAME = 'CallReleaseBtcContract';
const SOLIDITY_COMPILER_VERSION = 'v0.8.26+commit.8a97fa7a';
const { sendFromCow } = require('./rsk-utils');

const deployCallReleaseBtcContract = async (rskTxHelper) => {

const address = await rskTxHelper.getClient().eth.personal.newAccount('');
await sendFromCow(rskTxHelper, address, Number(btcToWeis(0.5)));
await rskTxHelper.getClient().eth.personal.unlockAccount(address, '');

const fullPath = path.resolve(__dirname, TEST_RELEASE_BTC_CONTRACT);
const source = fs.readFileSync(fullPath).toString();

const callReleaseBtcContract = await solUtils.compileAndDeploy(
SOLIDITY_COMPILER_VERSION,
source,
TEST_RELEASE_BTC_CONTRACT_NAME,
[],
rskTxHelper,
{
from: address
}
);

return {
creatorAddress: address,
callReleaseBtcContract,
};

};

module.exports = {
deployCallReleaseBtcContract,
};
8 changes: 4 additions & 4 deletions lib/rsk-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,13 @@ const triggerRelease = async (rskTransactionHelpers, btcClient, callbacks = {})
* @param {RskTransactionHelper} rskTxHelper to make transactions to the rsk network
* @param {web3.eth.Contract.ContractSendMethod} method contract method to be invoked
* @param {string} from rsk address to send the transaction from
* @param {number} valueInWeis amount in weis to be sent with the transaction
* @param {number} gas gas to be used in the transaction. Defaults to 100000
* @returns {web3.eth.TransactionReceipt} txReceipt
*/
const sendTransaction = async (rskTxHelper, method, from) => {
const sendTransaction = async (rskTxHelper, method, from, valueInWeis = 0, gas = 100000) => {

const estimatedGas = await method.estimateGas({ from });

const txReceiptPromise = method.send({ from, value: 0, gasPrice: 0, gas: estimatedGas });
const txReceiptPromise = method.send({ from, value: valueInWeis, gas });

await waitForRskMempoolToGetNewTxs(rskTxHelper);
await mineAndSync(getRskTransactionHelpers());
Expand Down
62 changes: 61 additions & 1 deletion lib/tests/2wp.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ 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, btcToWeis } = require('@rsksmart/btc-eth-unit-converter');
const { findEventInBlock, triggerRelease, getPegoutEventsInBlockRange, setFeePerKb } = require('../rsk-utils');
const {
findEventInBlock,
triggerRelease,
getPegoutEventsInBlockRange,
setFeePerKb,
sendTransaction,
} = require('../rsk-utils');
const BridgeTransactionParser = require('@rsksmart/bridge-transaction-parser');
const {
PEGIN_REJECTION_REASONS,
PEGIN_UNREFUNDABLE_REASONS,
Expand All @@ -29,6 +36,7 @@ const { getBtcAddressBalanceInSatoshis } = require('../btc-utils');
const { ensure0x, removePrefix0x } = require('../utils');
const { getBridgeState } = require('@rsksmart/bridge-state-data-parser');
const bitcoinJsLib = require('bitcoinjs-lib');
const { deployCallReleaseBtcContract } = require('../contractDeployer');

let btcTxHelper;
let rskTxHelpers;
Expand Down Expand Up @@ -824,6 +832,37 @@ const execute = (description, getRskHost) => {

});

it('should reject and not refund a pegout when a contract is trying to execute it', async () => {

// Arrange

const initial2wpBalances = await get2wpBalances(rskTxHelper, btcTxHelper);
const pegoutValueInRbtc = MINIMUM_PEGOUT_AMOUNT_IN_RBTC;

const { callReleaseBtcContract, creatorAddress } = await deployCallReleaseBtcContract(rskTxHelper);
const initialRskSenderBalanceInWeisBN = await rskTxHelper.getBalance(creatorAddress);

// Act

const callBridgeReleaseBtcMethod = callReleaseBtcContract.methods.callBridgeReleaseBtc();
const contractCallTxReceipt = await sendTransaction(rskTxHelper, callBridgeReleaseBtcMethod, creatorAddress, btcToWeis(pegoutValueInRbtc));

// Assert

const rskSenderAddressChecksummed = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(callReleaseBtcContract.options.address));
const expectedEvent = createExpectedReleaseRequestRejectedEvent(rskSenderAddressChecksummed, btcToSatoshis(pegoutValueInRbtc), PEGOUT_REJECTION_REASONS.CALLER_CONTRACT);
const actualReleaseRequestRejectedEvent = getReleaseRequestRejectedEventFromContractCallTxReceipt(contractCallTxReceipt);
expect(actualReleaseRequestRejectedEvent).to.be.deep.equal(expectedEvent);

await assert2wpBalancesAfterPegoutFromContract(initial2wpBalances, pegoutValueInRbtc);

// The rsk sender should lose the funds since there's no refund when a smart contract is trying to do a pegout
const expectedRskSenderBalanceInWeisBN = initialRskSenderBalanceInWeisBN.sub(new BN(`${btcToWeis(pegoutValueInRbtc)}`));
const finalRskSenderBalanceInWeisBN = await rskTxHelper.getBalance(creatorAddress);
expect(finalRskSenderBalanceInWeisBN.eq(expectedRskSenderBalanceInWeisBN)).to.be.true;

});

});

};
Expand Down Expand Up @@ -912,6 +951,19 @@ const assert2wpBalanceIsUnchanged = async (initial2wpBalances) => {
expect(final2wpBalances).to.be.deep.equal(initial2wpBalances);
};

const assert2wpBalancesAfterPegoutFromContract = async (initial2wpBalances, pegoutValueInRbtc) => {
const final2wpBalances = await get2wpBalances(rskTxHelper, btcTxHelper);

expect(final2wpBalances.federationAddressBalanceInSatoshis).to.be.equal(initial2wpBalances.federationAddressBalanceInSatoshis);

expect(final2wpBalances.bridgeUtxosBalanceInSatoshis).to.be.equal(initial2wpBalances.bridgeUtxosBalanceInSatoshis);
// When a contract sends funds to the Bridge to try to do a pegout, the pegout is rejected and the Bridge rsk balance is increased by the pegout value
// because there's no refund when a contract is trying to do a pegout.
const expectedBridgeBalanceInWeisBN = initial2wpBalances.bridgeBalanceInWeisBN.add(new BN(btcToWeis(pegoutValueInRbtc)));

expect(final2wpBalances.bridgeBalanceInWeisBN.eq(expectedBridgeBalanceInWeisBN)).to.be.true;
};

const assertBtcPeginTxHashProcessed = async (btcPeginTxHash) => {
const isBtcTxHashAlreadyProcessed = await bridge.methods.isBtcTxHashAlreadyProcessed(btcPeginTxHash).call();
expect(isBtcTxHashAlreadyProcessed).to.be.true;
Expand Down Expand Up @@ -987,6 +1039,14 @@ const assertExpectedReleaseRequestRejectedEventIsEmitted = async (rskSenderAddre
expect(releaseRequestRejectedEvent).to.be.deep.equal(expectedEvent);
};

const getReleaseRequestRejectedEventFromContractCallTxReceipt = (contractCallTxReceipt) => {
const bridgeTxParser = new BridgeTransactionParser(rskTxHelper.getClient());
const logData = contractCallTxReceipt.events['0'].raw;
const releaseRequestRejectedAbiElement = bridgeTxParser.jsonInterfaceMap[PEGOUT_EVENTS.RELEASE_REQUEST_REJECTED.signature];
const releaseRequestRejectedEvent = bridgeTxParser.decodeLog(logData, releaseRequestRejectedAbiElement);
return releaseRequestRejectedEvent;
};

module.exports = {
execute,
};
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"license": "GPL3",
"dependencies": {
"@rsksmart/bridge-state-data-parser": "^1.2.0",
"@rsksmart/bridge-transaction-parser": "^1.1.3",
"@rsksmart/bridge-transaction-parser": "git+https://[email protected]/rsksmart/bridge-transaction-parser#c7cc7f5077eabf2e818bad3bb13fac9b9aabf477",
"@rsksmart/btc-eth-unit-converter": "^1.0.0",
"@rsksmart/btc-rsk-derivation": "^0.0.2",
"@rsksmart/pmt-builder": "^3.0.0",
Expand Down

0 comments on commit 9ed7282

Please sign in to comment.