-
Notifications
You must be signed in to change notification settings - Fork 2
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
Using 'parameterized tests' for 2wp.js pegin tests #89
Changes from all commits
40f2ef5
d1c77c3
5ffe2b4
dbea99a
cfb066e
546d9a1
d280986
867470b
3f9d85a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
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 { btcToWeis, btcToSatoshis } = require('@rsksmart/btc-eth-unit-converter'); | ||
const { waitAndUpdateBridge, mineAndSync, findEventInBlock } = require('../rsk-utils'); | ||
const { PEGIN_EVENTS } = require("../constants"); | ||
const { waitForBitcoinTxToBeInMempool } = require('../btc-utils'); | ||
const { | ||
ensurePeginIsRegistered, | ||
donateToBridge, | ||
createSenderRecipientInfo, | ||
createExpectedPeginBtcEvent, | ||
mineForPeginRegistration, | ||
} = require('../2wp-utils'); | ||
const { ensure0x } = require('../utils'); | ||
const bitcoinJsLib = require('bitcoinjs-lib'); | ||
|
||
const DONATION_AMOUNT = 250; | ||
const MINIMUM_PEGIN_VALUE_IN_BTC = 0.5; | ||
|
||
let btcTxHelper; | ||
let rskTxHelper; | ||
let rskTxHelpers; | ||
let bridge; | ||
let federationAddress; | ||
|
||
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 addInputs = (tx, sendersUtxosInfo) => { | ||
sendersUtxosInfo.flatMap(senderUtxosInfo => senderUtxosInfo.utxos).forEach(uxto => { | ||
tx.addInput(Buffer.from(uxto.txid, 'hex').reverse(), uxto.vout); | ||
}); | ||
}; | ||
|
||
const addChangeOutputs = (tx, sendersInfo, sendersChange) => { | ||
sendersChange.forEach((change, index) => { | ||
if(change > 0) { | ||
tx.addOutput( | ||
bitcoinJsLib.address.toOutputScript(sendersInfo[index].btcSenderAddressInfo.address, btcTxHelper.btcConfig.network), | ||
Number(btcToSatoshis(change)) | ||
); | ||
} | ||
}); | ||
}; | ||
|
||
const addOutputsToFed = (tx, outputsToFed) => { | ||
outputsToFed.forEach(outputAmount => { | ||
tx.addOutput( | ||
bitcoinJsLib.address.toOutputScript(federationAddress, btcTxHelper.btcConfig.network), | ||
Number(btcToSatoshis(outputAmount)) | ||
); | ||
}); | ||
}; | ||
|
||
const pushPegin = async (btcPeginTxHash, expectedUtxosCount) => { | ||
await waitForBitcoinTxToBeInMempool(btcTxHelper, btcPeginTxHash); | ||
await mineForPeginRegistration(rskTxHelper, btcTxHelper); | ||
await ensurePeginIsRegistered(rskTxHelper, btcPeginTxHash, expectedUtxosCount); | ||
}; | ||
|
||
const getSendersInfo = async (initialBtcSenderBalancesInBtc) => { | ||
return await Promise.all(initialBtcSenderBalancesInBtc.map(initialBtcSenderBalance => createSenderRecipientInfo(rskTxHelper, btcTxHelper, 'legacy', initialBtcSenderBalance + btcTxHelper.getFee()))); | ||
}; | ||
|
||
const getSendersBtcAddressBalances = async (sendersInfo) => { | ||
return await Promise.all(sendersInfo.map(senderInfo => btcTxHelper.getAddressBalance(senderInfo.btcSenderAddressInfo.address))); | ||
}; | ||
|
||
const getSendersUtxosInfo = async (sendersInfo, btcSenderAmountsToSendToFed) => { | ||
return await Promise.all(sendersInfo.map((senderInfo, index) => btcTxHelper.selectSpendableUTXOsFromAddress(senderInfo.btcSenderAddressInfo.address, btcSenderAmountsToSendToFed[index]))); | ||
}; | ||
|
||
const getSendersChange = (sendersUtxosInfo) => { | ||
return sendersUtxosInfo.map(senderUtxosInfo => senderUtxosInfo.change - btcTxHelper.getFee()); | ||
}; | ||
|
||
const getSendersPrivateKeys = (sendersInfo) => { | ||
return sendersInfo.map(senderInfo => senderInfo.btcSenderAddressInfo.privateKey); | ||
}; | ||
|
||
const getTotalAmountToFed = (outputsToFed) => { | ||
return outputsToFed.reduce((total, amount) => total + amount, 0); | ||
}; | ||
|
||
const getFinalRskRecipientBalances = async (sendersInfo) => { | ||
return await Promise.all(sendersInfo.map(senderInfo => rskTxHelper.getBalance(senderInfo.rskRecipientRskAddressInfo.address))); | ||
}; | ||
|
||
const legacyPeginTestsHappyPath = [ | ||
{ | ||
description: 'should do legacy pegin with one input and one output to the federation', | ||
initialBtcSenderBalancesInBtc: [ MINIMUM_PEGIN_VALUE_IN_BTC ], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can fetch this value using an async function. You just need to execute it once you are executing the test. |
||
btcSenderAmountsToSendToFed: [ MINIMUM_PEGIN_VALUE_IN_BTC ], | ||
outputsToFed: [ MINIMUM_PEGIN_VALUE_IN_BTC ], | ||
}, | ||
{ | ||
description: 'should do legacy pegin with multiple inputs from different accounts and one output to the federation', | ||
initialBtcSenderBalancesInBtc: [ MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC ], // Each sender is funded with some amount | ||
btcSenderAmountsToSendToFed: [ MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC ], // Each sender decides how much to send to the federation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
outputsToFed: [ MINIMUM_PEGIN_VALUE_IN_BTC * 3 ], | ||
}, | ||
{ | ||
description: 'should do legacy pegin with multiple inputs from different accounts and two outputs to the federation', | ||
initialBtcSenderBalancesInBtc: [ MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC ], | ||
btcSenderAmountsToSendToFed: [ MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC ], | ||
outputsToFed: [ MINIMUM_PEGIN_VALUE_IN_BTC, MINIMUM_PEGIN_VALUE_IN_BTC * 2 ], // Example of a pegin with multiple outputs to fed | ||
}, | ||
]; | ||
|
||
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(); | ||
await btcTxHelper.importAddress(federationAddress, 'federation'); | ||
|
||
await waitAndUpdateBridge(rskTxHelper); | ||
await setupBridgeDonation(rskTxHelpers, btcTxHelper); | ||
|
||
}); | ||
|
||
legacyPeginTestsHappyPath.forEach(test => { | ||
|
||
it(test.description, async () => { | ||
|
||
// Arrange | ||
|
||
const { initialBtcSenderBalancesInBtc, btcSenderAmountsToSendToFed, outputsToFed } = test; | ||
|
||
const initialFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); | ||
|
||
const sendersInfo = await getSendersInfo(initialBtcSenderBalancesInBtc); | ||
const senderInfo1 = sendersInfo[0]; | ||
|
||
const initialSendersBtcAddressBalances = await getSendersBtcAddressBalances(sendersInfo); | ||
const sendersUtxosInfo = await getSendersUtxosInfo(sendersInfo, btcSenderAmountsToSendToFed); | ||
const sendersChange = getSendersChange(sendersUtxosInfo); | ||
|
||
const tx = new bitcoinJsLib.Transaction(); | ||
addInputs(tx, sendersUtxosInfo); | ||
addOutputsToFed(tx, outputsToFed); | ||
addChangeOutputs(tx, sendersInfo, sendersChange); | ||
|
||
const sendersPrivateKeys = getSendersPrivateKeys(sendersInfo); | ||
const signedTx = await btcTxHelper.nodeClient.signTransaction(tx.toHex(), [], sendersPrivateKeys); | ||
|
||
// Act | ||
|
||
// Sending the pegin and ensuring the pegin is registered | ||
const btcPeginTxHash = await btcTxHelper.nodeClient.sendTransaction(signedTx); | ||
await pushPegin(btcPeginTxHash, outputsToFed.length); | ||
|
||
// Assert | ||
|
||
const isBtcTxHashAlreadyProcessed = await bridge.methods.isBtcTxHashAlreadyProcessed(btcPeginTxHash).call(); | ||
expect(isBtcTxHashAlreadyProcessed).to.be.true; | ||
|
||
const totalAmountToFed = getTotalAmountToFed(outputsToFed); | ||
|
||
// The expected pegin_btc event should be emitted | ||
const recipient1RskAddressChecksumed = rskTxHelper.getClient().utils.toChecksumAddress(ensure0x(senderInfo1.rskRecipientRskAddressInfo.address)); | ||
const expectedEvent = createExpectedPeginBtcEvent(PEGIN_EVENTS.PEGIN_BTC, recipient1RskAddressChecksumed, btcPeginTxHash, btcToSatoshis(totalAmountToFed)); | ||
const btcTxHashProcessedHeight = Number(await bridge.methods.getBtcTxHashProcessedHeight(btcPeginTxHash).call()); | ||
const peginBtcEvent = await findEventInBlock(rskTxHelper, expectedEvent.name, btcTxHashProcessedHeight); | ||
expect(peginBtcEvent).to.be.deep.equal(expectedEvent); | ||
|
||
// The federation address should have received the total amount sent by the senders | ||
const finalFederationAddressBalanceInBtc = Number(await btcTxHelper.getAddressBalance(federationAddress)); | ||
expect(finalFederationAddressBalanceInBtc).to.be.equal(initialFederationAddressBalanceInBtc + totalAmountToFed); | ||
|
||
// The senders should have their balances reduced by the amount sent to the federation and the fee | ||
const finalSendersBtcAddressBalances = await getSendersBtcAddressBalances(sendersInfo); | ||
|
||
for(let i = 0; i < finalSendersBtcAddressBalances.length; i++) { | ||
const actualFinalBalance = Number(btcToSatoshis(finalSendersBtcAddressBalances[i])); | ||
const expectedFinalBalance = Number(btcToSatoshis(initialSendersBtcAddressBalances[i])) - Number(btcToSatoshis(btcSenderAmountsToSendToFed[i])) - Number(btcToSatoshis(btcTxHelper.getFee())); | ||
expect(actualFinalBalance).to.be.equal(expectedFinalBalance); | ||
} | ||
|
||
const finalRskRecipientBalances = await getFinalRskRecipientBalances(sendersInfo); | ||
|
||
// Extracting the first rsk recipient balance since only the first sender should have the total amount in rsk. | ||
const [ firstRskRecipientBalance, ...restOfFinalRskRecipientBalances ] = finalRskRecipientBalances; | ||
expect(Number(firstRskRecipientBalance)).to.be.equal(Number(btcToWeis(totalAmountToFed))); | ||
|
||
// The other rsk recipients should have their balances unchanged | ||
for(let i = 0; i < restOfFinalRskRecipientBalances.length; i++) { | ||
const actualFinalBalance = Number(restOfFinalRskRecipientBalances[i]); | ||
const expectedFinalBalance = 0; | ||
expect(actualFinalBalance).to.be.equal(expectedFinalBalance); | ||
} | ||
|
||
}); | ||
|
||
}); | ||
|
||
}); | ||
|
||
} | ||
|
||
module.exports = { | ||
execute, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const twoWpTests = require('../lib/tests/2wp'); | ||
|
||
twoWpTests.execute('BTC <=> RSK 2WP', () => Runners.hosts.federate.host); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not simple legacy pegins. I consider these legacy pegins as border cases, as well as uncommon/complex legacy pegins, at least those ones sending multiple outputs to the fed, since there's no reason for wallet to do that.
That being said, I would add a prefix to this variable like
uncommon/complex-LegacyPeginTests
. wdyt?