diff --git a/test_integration/02_stake_and_mint/utils/revert_stake_assertion.js b/test_integration/02_stake_and_mint/utils/revert_stake_assertion.js index 8a0e6b14..5e345018 100644 --- a/test_integration/02_stake_and_mint/utils/revert_stake_assertion.js +++ b/test_integration/02_stake_and_mint/utils/revert_stake_assertion.js @@ -193,7 +193,7 @@ class RevertStakeAssertion { assert.strictEqual( eventData._staker, stakeRequest.staker, - `Expected message hash ${ + `Expected staker address ${ eventData._staker } is different from actual message hash ${stakeRequest.staker}`, ); diff --git a/test_integration/03_redeem_and_unstake/03_revert_redeem.js b/test_integration/03_redeem_and_unstake/03_revert_redeem.js new file mode 100644 index 00000000..d210fba6 --- /dev/null +++ b/test_integration/03_redeem_and_unstake/03_revert_redeem.js @@ -0,0 +1,340 @@ +// Copyright 2019 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const BN = require('bn.js'); +const shared = require('../shared'); +const Utils = require('../../test/test_lib/utils'); +const EventDecoder = require('../../test/test_lib/event_decoder'); +const ProofUtils = require('../lib/proof_utils'); +const Anchor = require('../lib/anchor'); +const RedeemAssertion = require('./utils/redeem_assertion'); +const RevertRedeemAssertion = require('./utils/revert_redeem_assertion'); +const ProgressRevertRedeemAssertion = require('./utils/progress_revert_redeem_assertion'); +const ConfirmRedeemIntentAssertion = require('./utils/confirm_redeem_intent_assertion'); +const ConfirmRevertRedeemIntentAssertion = require('./utils/confirm_revert_redeem_intent_assertion'); +const ProveGatewayAssertion = require('../lib/prove_gateway_assertion'); + +describe('Revert Redeem', async () => { + let originAccounts; + let auxiliaryAccounts; + let originWeb3; + let auxiliaryWeb3; + + let gateway; + let cogateway; + let ostPrime; + + let originAnchor; + let auxiliaryAnchor; + let redeemRequest; + + let redeemAssertion; + let proofUtils; + let messageBoxOffset; + let assertRevertRedeem; + let assertProgressRevertRedeem; + + /** + * Prove cogateway account on gateway contract on origin chain. + * + * @return {Promise} Promise contains ProofCoGatewayResponse object. + */ + const proveCoGateway = async () => { + // Anchor state root. + const blockNumber = await originAnchor.anchorStateRoot( + 'latest', + ); + + // Generate outbox proof for block height for which state root is + // anchored. + const proofData = await proofUtils.getOutboxProof( + cogateway.address, + [redeemRequest.messageHash], + messageBoxOffset, + auxiliaryWeb3.utils.toHex(blockNumber), + ); + redeemRequest.blockHeight = new BN(blockNumber); + // Prove gateway. + const tx = await gateway.proveGateway( + redeemRequest.blockHeight, + proofData.encodedAccountValue, + proofData.serializedAccountProof, + { from: originAccounts[0] }, + ); + + const event = EventDecoder.getEvents(tx, gateway); + ProveGatewayAssertion.verify( + event, + redeemRequest.blockHeight, + proofData.storageHash, + cogateway.address, + ); + + return { proofData, blockNumber: new BN(blockNumber) }; + }; + + /** + * Prove gateway account on cogateway contract on auxiliary chain. + * + * @return {Promise} Promise contains ProofGatewayResponse object. + */ + const proveGateway = async () => { + // Anchor state root. + const blockNumber = await auxiliaryAnchor.anchorStateRoot( + 'latest', + ); + + // Generate inbox proof for block height for which state root is + // anchored. + const proofData = await proofUtils.getInboxProof( + gateway.address, + [redeemRequest.messageHash], + messageBoxOffset, + originWeb3.utils.toHex(blockNumber), + ); + + // Prove gateway. + const tx = await cogateway.proveGateway( + new BN(blockNumber), + proofData.encodedAccountValue, + proofData.serializedAccountProof, + { from: auxiliaryAccounts[0] }, + ); + + const event = EventDecoder.getEvents(tx, cogateway); + ProveGatewayAssertion.verify( + event, + new BN(blockNumber), + proofData.storageHash, + gateway.address, + ); + + return { proofData, blockNumber: new BN(blockNumber) }; + }; + + before(async () => { + originWeb3 = shared.origin.web3; + auxiliaryWeb3 = shared.auxiliary.web3; + originAccounts = shared.origin.accounts; + auxiliaryAccounts = shared.auxiliary.accounts; + gateway = shared.origin.contracts.EIP20Gateway; + cogateway = shared.auxiliary.contracts.EIP20CoGateway; + ostPrime = shared.auxiliary.contracts.OSTPrime; + + const hasher = Utils.generateHashLock(); + + redeemRequest = { + amount: new BN(50), + gasPrice: new BN(1), + gasLimit: new BN(10), + redeemer: auxiliaryAccounts[2], + bounty: await cogateway.bounty.call(), + nonce: await cogateway.getNonce.call(auxiliaryAccounts[2]), + beneficiary: originAccounts[2], + hashLock: hasher.l, + unlockSecret: hasher.s, + }; + + originAnchor = new Anchor( + auxiliaryWeb3, + shared.origin.contracts.Anchor, + shared.origin.organizationAddress, + ); + + auxiliaryAnchor = new Anchor( + originWeb3, + shared.auxiliary.contracts.Anchor, + shared.auxiliary.organizationAddress, + ); + redeemAssertion = new RedeemAssertion(cogateway, ostPrime, auxiliaryWeb3); + proofUtils = new ProofUtils(auxiliaryWeb3, originWeb3); + messageBoxOffset = await gateway.MESSAGE_BOX_OFFSET.call(); + assertRevertRedeem = new RevertRedeemAssertion(cogateway, ostPrime, auxiliaryWeb3); + assertProgressRevertRedeem = new ProgressRevertRedeemAssertion(cogateway, ostPrime, auxiliaryWeb3); + }); + + it('redeems', async () => { + await approveCogatewayForRedeemAmount( + ostPrime, + redeemRequest, + cogateway, + ); + + const initialBalancesBeforeRedeem = await redeemAssertion.captureBalances( + redeemRequest.redeemer, + ); + + const response = await cogateway.redeem( + redeemRequest.amount, + redeemRequest.beneficiary, + redeemRequest.gasPrice, + redeemRequest.gasLimit, + redeemRequest.nonce, + redeemRequest.hashLock, + { + from: redeemRequest.redeemer, + value: redeemRequest.bounty, + }, + ); + + const transactionFeeInRedeem = await getTransactionFee( + response, + auxiliaryWeb3, + ); + + const event = EventDecoder.getEvents(response, cogateway); + + await redeemAssertion.verify( + event, + redeemRequest, + transactionFeeInRedeem, + initialBalancesBeforeRedeem, + ); + redeemRequest.messageHash = event.RedeemIntentDeclared._messageHash; + }); + + it('confirms redeem', async () => { + const { proofData, blockNumber } = await proveCoGateway(); + redeemRequest.blockHeight = new BN(blockNumber); + + const tx = await gateway.confirmRedeemIntent( + redeemRequest.redeemer, + redeemRequest.nonce, + redeemRequest.beneficiary, + redeemRequest.amount, + redeemRequest.gasPrice, + redeemRequest.gasLimit, + redeemRequest.blockHeight, + redeemRequest.hashLock, + proofData.storageProof[0].serializedProof, + { from: originAccounts[0] }, + ); + + const event = EventDecoder.getEvents(tx, gateway); + // Assert event. + ConfirmRedeemIntentAssertion.verify(event, redeemRequest); + }); + + it('reverts redeem', async () => { + // Capture initial token and base token balance of redeemer and cogateway. + const initialBalances = await assertRevertRedeem.captureBalances( + redeemRequest.redeemer, + ); + + const penalty = await cogateway.penalty.call(redeemRequest.messageHash); + + const tx = await cogateway.revertRedeem( + redeemRequest.messageHash, + { + from: redeemRequest.redeemer, + value: penalty, + }, + ); + + const events = EventDecoder.getEvents(tx, cogateway); + + const transactionFeeInRevertRedeem = await getTransactionFee( + tx, + auxiliaryWeb3, + ); + + await assertRevertRedeem.verify( + events, + redeemRequest, + transactionFeeInRevertRedeem, + initialBalances, + ); + }); + + it('confirms redeem intent', async () => { + const { proofData, blockNumber } = await proveCoGateway(); + redeemRequest.blockHeight = new BN(blockNumber); + + const tx = await gateway.confirmRevertRedeemIntent( + redeemRequest.messageHash, + redeemRequest.blockHeight, + proofData.storageProof[0].serializedProof, + { from: originAccounts[0] }, + ); + + const event = EventDecoder.getEvents(tx, gateway); + // Assert event. + ConfirmRevertRedeemIntentAssertion.verify(event, redeemRequest); + }); + + it('progress revert redeem', async () => { + // Capture initial token and base token balance of redeemer and cogateway. + const initialBalances = await assertProgressRevertRedeem.captureBalances( + redeemRequest.redeemer, + ); + + const { proofData, blockNumber } = await proveGateway(); + redeemRequest.blockHeight = new BN(blockNumber); + + const tx = await cogateway.progressRevertRedeem( + redeemRequest.messageHash, + redeemRequest.blockHeight, + proofData.storageProof[0].serializedProof, + { from: auxiliaryAccounts[0] }, + ); + + const events = EventDecoder.getEvents(tx, cogateway); + await assertProgressRevertRedeem.verify( + events, + redeemRequest, + initialBalances, + ); + }); +}); + +/** + * This approves the cogateway for redeem amount by wrapping the base token. + * @param {Object} ostPrime OSTPrime contract instance. + * @param {Object} redeemRequest Redeem request object. + * @param {Object} cogateway CoGateway contract instance. + * @return {Promise} + */ +async function approveCogatewayForRedeemAmount(ostPrime, redeemRequest, cogateway) { + await ostPrime.wrap( + { + value: redeemRequest.amount, + from: redeemRequest.redeemer, + }, + ); + + await ostPrime.approve( + cogateway.address, + redeemRequest.amount, + { from: redeemRequest.redeemer }, + ); +} + +/** + * This returns transaction fee. + * @param {Object} response Transaction object by truffle. + * @param {Web3} web3 + * @return {Promise} transaction fee. + */ +async function getTransactionFee(response, web3) { + const transaction = await web3.eth.getTransaction(response.tx); + const gasUsed = new BN(response.receipt.gasUsed); + const gasPrice = new BN(transaction.gasPrice); + return gasUsed.mul(gasPrice); +} diff --git a/test_integration/03_redeem_and_unstake/utils/confirm_redeem_intent_assertion.js b/test_integration/03_redeem_and_unstake/utils/confirm_redeem_intent_assertion.js index 4145da33..7419703f 100644 --- a/test_integration/03_redeem_and_unstake/utils/confirm_redeem_intent_assertion.js +++ b/test_integration/03_redeem_and_unstake/utils/confirm_redeem_intent_assertion.js @@ -25,7 +25,7 @@ const assert = require('assert'); * @typedef {Object} RedeemRequest * @property {BN} amount Redeem amount. * @property {BN} gasPrice Gas price that Redeemer is ready to pay to get the - * redeem and unStake process done. + * redeem and unstake process done. * @property {BN} gasLimit Gas limit that redeemer is ready to pay. * @property {string} redeemer Address of Redeemer. * @property {BN} bounty Bounty amount paid for redeem and unstake message diff --git a/test_integration/03_redeem_and_unstake/utils/confirm_revert_redeem_intent_assertion.js b/test_integration/03_redeem_and_unstake/utils/confirm_revert_redeem_intent_assertion.js new file mode 100644 index 00000000..94def0c9 --- /dev/null +++ b/test_integration/03_redeem_and_unstake/utils/confirm_revert_redeem_intent_assertion.js @@ -0,0 +1,79 @@ +// Copyright 2019 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const assert = require('assert'); + +/** + * Redeem Request object contains all the properties for redeem and unstake. + * @typedef {Object} RedeemRequest + * @property {BN} amount Redeem amount. + * @property {BN} gasPrice Gas price that Redeemer is ready to pay to get the + * redeem and unstake process done. + * @property {BN} gasLimit Gas limit that redeemer is ready to pay. + * @property {string} redeemer Address of Redeemer. + * @property {BN} bounty Bounty amount paid for redeem and unstake message + * transfers. + * @property {BN} nonce Redeem nonce. + * @property {string} beneficiary Address of beneficiary on origin chain. + * @property {string} hashLock Hash Lock provided by the redeemer. + * @property {string} unlockSecret Unlock secret to unlock hash lock. + * @property {string} messageHash Identifier for redeem and unstake process. + * @property {BN} blockHeight Height at which anchor state root is done. + */ + +/** + * Class to assert confirm revert redeem intent. + */ +class ConfirmRevertRedeemIntentAssertion { + /** + * This verifies event. + * @param {Object} event Event object after decoding. + * @param {RedeemRequest} redeemRequest Redeem request parameters. + */ + static verify(event, redeemRequest) { + const eventData = event.RevertRedeemIntentConfirmed; + + assert.strictEqual( + eventData._messageHash, + redeemRequest.messageHash, + `Message hash from event must be equal to ${redeemRequest.messageHash}.`, + ); + + assert.strictEqual( + eventData._redeemer, + redeemRequest.redeemer, + `Redeemer address from event must be equal to ${redeemRequest.redeemer}.`, + ); + + assert.strictEqual( + redeemRequest.nonce.eq(eventData._redeemerNonce), + true, + `Redeem nonce from event must be equal to ${redeemRequest.nonce.toString(10)}.`, + ); + + assert.strictEqual( + redeemRequest.amount.eq(eventData._amount), + true, + `Amount from event must be equal to ${redeemRequest.amount.toString(10)}.`, + ); + } +} + +module.exports = ConfirmRevertRedeemIntentAssertion; diff --git a/test_integration/03_redeem_and_unstake/utils/progress_redeem_assertion.js b/test_integration/03_redeem_and_unstake/utils/progress_redeem_assertion.js index a7ad7a79..32a34a8d 100644 --- a/test_integration/03_redeem_and_unstake/utils/progress_redeem_assertion.js +++ b/test_integration/03_redeem_and_unstake/utils/progress_redeem_assertion.js @@ -24,11 +24,11 @@ const BN = require('bn.js'); const Utils = require('../../../test/test_lib/utils.js'); /** - * Redeem Request object contains all the properties for redeem and unStake. + * Redeem Request object contains all the properties for redeem and unstake. * @typedef {Object} RedeemRequest * @property {BN} amount Redeem amount. * @property {BN} gasPrice Gas price that Redeemer is ready to pay to get the - * redeem and unStake process done. + * redeem and unstake process done. * @property {BN} gasLimit Gas limit that redeemer is ready to pay. * @property {string} redeemer Address of Redeemer. * @property {BN} bounty Bounty amount paid for redeem and unstake message @@ -44,10 +44,10 @@ const Utils = require('../../../test/test_lib/utils.js'); /** * BaseToken(ETH) and OSTPrime ERC20 balance of cogateway, redeemer. * @typedef {Object} Balances - * @property balances.ostPrime.cogateway ERC20 balance of cogateway contract. - * @property balances.ostPrime.redeemer ERC20 balance of beneficiary. - * @property balances.baseToken.cogateway Base token(ETH) balance of cogateway. - * @property balances.baseToken.redeemer Base token(ETH) balance of redeemer. + * @property {BN} balances.ostPrime.cogateway ERC20 balance of cogateway contract. + * @property {BN} balances.ostPrime.redeemer ERC20 balance of beneficiary. + * @property {BN} balances.baseToken.cogateway Base token(ETH) balance of cogateway. + * @property {BN} balances.baseToken.redeemer Base token(ETH) balance of redeemer. */ /** @@ -208,8 +208,7 @@ class ProgressRedeemAssertion { Utils.ZERO_BYTES32, `Unlock secret must be ${Utils.ZERO_BYTES32}.`, ); - } - else { + } else { assert.strictEqual( eventData._unlockSecret, redeemRequest.unlockSecret, diff --git a/test_integration/03_redeem_and_unstake/utils/progress_revert_redeem_assertion.js b/test_integration/03_redeem_and_unstake/utils/progress_revert_redeem_assertion.js new file mode 100644 index 00000000..fd2ca9dd --- /dev/null +++ b/test_integration/03_redeem_and_unstake/utils/progress_revert_redeem_assertion.js @@ -0,0 +1,241 @@ +// Copyright 2019 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const assert = require('assert'); +const BN = require('bn.js'); + +/** + * Redeem Request object contains all the properties for redeem and unstake. + * @typedef {Object} RedeemRequest + * @property {BN} amount Redeem amount. + * @property {BN} gasPrice Gas price that Redeemer is ready to pay to get the + * redeem and unstake process done. + * @property {BN} gasLimit Gas limit that redeemer is ready to pay. + * @property {string} redeemer Address of Redeemer. + * @property {BN} bounty Bounty amount paid for redeem and unstake message + * transfers. + * @property {BN} nonce Redeem nonce. + * @property {string} beneficiary Address of beneficiary on origin chain. + * @property {string} hashLock Hash Lock provided by the redeemer. + * @property {string} unlockSecret Unlock secret to unlock hash lock. + * @property {string} messageHash Identifier for redeem and unstake process. + * @property {BN} blockHeight Height at which anchor state root is done. + */ + +/** + * BaseToken(ETH) and OSTPrime ERC20 balance of cogateway, redeemer. + * @typedef {Object} Balances + * @property {BN} balances.ostPrime.cogateway ERC20 balance of cogateway contract. + * @property {BN} balances.ostPrime.redeemer ERC20 balance of beneficiary. + * @property {BN} balances.baseToken.cogateway Base token(ETH) balance of cogateway. + * @property {BN} balances.baseToken.redeemer Base token(ETH) balance of redeemer. + */ + +/** + * Class to assert event and balances after progress revert redeem. + */ +class ProgressRevertRedeemAssertion { + /** + * Constructor. + * @param {Object} cogateway Truffle cogateway instance. + * @param {Object} ostPrime Truffle token instance. + * @param {Web3} web3 Web3 instance. + */ + constructor(cogateway, ostPrime, web3) { + this.cogateway = cogateway; + this.token = ostPrime; + this.web3 = web3; + } + + /** + * This verifies event and balances. + * @param {Object} event Event object after decoding. + * @param {RedeemRequest} redeemRequest Redeem request parameters. + * @param {BN} transactionFees Transaction fees in progress revert redeem request. + * @param {Balances} initialBalances Initial baseToken and token balances. + */ + async verify(event, redeemRequest, transactionFees, initialBalances) { + await this._assertBalancesForProgressRevertRedeem( + redeemRequest, + transactionFees, + initialBalances, + ); + + ProgressRevertRedeemAssertion._assertProgressRevertRedeemEvent( + event, + redeemRequest, + ); + } + + /** + * This captures base token and token balance of cogateway, redeemer and + * burner address. + * @param {string} redeemer Redeemer address. + * @return {Promise} + */ + async captureBalances(redeemer) { + const burner = await this.cogateway.burner.call(); + + return { + baseToken: { + burner: await this._getBaseTokenBalance(burner), + cogateway: await this._getBaseTokenBalance(this.cogateway.address), + redeemer: await this._getBaseTokenBalance(redeemer), + }, + token: { + burner: await this.token.balanceOf(burner), + cogateway: await this.token.balanceOf(this.cogateway.address), + redeemer: await this.token.balanceOf(redeemer), + }, + }; + } + + /** + * This asserts balances of redeemer and cogateway after progress revert redeem. + * @param {RedeemRequest} redeemRequest Redeem request parameters. + * @param {Balances} initialBalances Initial balance of redeemer and cogateway + * generated by captureBalances method. + * @private + */ + async _assertBalancesForProgressRevertRedeem(redeemRequest, initialBalances) { + const finalBalances = await this.captureBalances(redeemRequest.redeemer); + + // Assert cogateway balance + const expectedCoGatewayBaseTokenBalance = initialBalances.baseToken.cogateway + .sub(redeemRequest.bounty); + + // Assert Penalty is transferred to cogateway. + assert.strictEqual( + expectedCoGatewayBaseTokenBalance.eq(finalBalances.baseToken.cogateway), + true, + `CoGateway base token balance must be ${expectedCoGatewayBaseTokenBalance.toString(10)}` + + ` instead of ${finalBalances.baseToken.cogateway.toString(10)}`, + ); + + const expectedCoGatewayTokenBalance = initialBalances.token.cogateway.sub(redeemRequest.amount); + + // Assert redeem amount is transferred to cogateway. + assert.strictEqual( + expectedCoGatewayTokenBalance.eq(finalBalances.token.cogateway), + true, + `CoGateway token balance must be ${expectedCoGatewayBaseTokenBalance.toString(10)}` + + ` instead of ${finalBalances.token.cogateway.toString(10)}`, + ); + + // Assert redeemer balance + const expectedRedeemerBaseTokenBalance = initialBalances.baseToken.redeemer; + + assert.strictEqual( + expectedRedeemerBaseTokenBalance.eq(finalBalances.baseToken.redeemer), + true, + `Redeemer base token balance must be ${expectedRedeemerBaseTokenBalance.toString(10)}` + + ` instead of ${finalBalances.baseToken.redeemer.toString(10)}`, + ); + + const expectedRedeemerTokenBalance = initialBalances.token.redeemer.add(redeemRequest.amount); + + assert.strictEqual( + expectedRedeemerTokenBalance.eq(finalBalances.token.redeemer), + true, + `Redeemer token balance must be ${expectedRedeemerTokenBalance.toString(10)}` + + ` instead of ${finalBalances.token.redeemer.toString(10)}`, + ); + + // Assert burner balance + const expectedBurnerBaseTokenBalance = initialBalances.baseToken + .burner.add(redeemRequest.bounty); + + assert.strictEqual( + expectedBurnerBaseTokenBalance.eq(finalBalances.baseToken.burner), + true, + `Burner base token balance must be ${ + expectedBurnerBaseTokenBalance.toString(10) + } instead of ${finalBalances.baseToken.burner.toString(10)}`, + ); + + const expectedBurnerTokenBalance = initialBalances.token.burner; + + assert.strictEqual( + expectedBurnerTokenBalance.eq(finalBalances.token.burner), + true, + `Burner token balance must be ${ + expectedBurnerTokenBalance.toString(10) + } instead of ${finalBalances.token.burner.toString()}`, + ); + } + + /** + * This assert event after progress revert redeem method. + * @param {Object} event Event object after decoding. + * @param {RedeemRequest} redeemRequest Redeem request parameters. + * @private + */ + static _assertProgressRevertRedeemEvent(event, redeemRequest) { + const eventData = event.RedeemReverted; + + assert.strictEqual( + eventData._messageHash, + redeemRequest.messageHash, + `Message hash from the event must be equal to ${ + redeemRequest.messageHash + }.`, + ); + + assert.strictEqual( + eventData._redeemer, + redeemRequest.redeemer, + `Redeemer address from the event must be equal to ${ + redeemRequest.redeemer + }.`, + ); + + assert.strictEqual( + eventData._redeemerNonce.eq(redeemRequest.nonce), + true, + `Redeemer nonce ${eventData._redeemerNonce.toString( + 10, + )} from the event must be equal to ${redeemRequest.nonce.toString( + 10, + )}.`, + ); + + assert.strictEqual( + eventData._amount.eq(redeemRequest.amount), + true, + `Redeem amount ${eventData._amount.toString( + 10, + )} from the event must be equal to ${redeemRequest.amount.toString( + 10, + )}.`, + ); + } + + /** + * Returns Base token balance (ETH) wrapped in BN. + * @param {string} address Address for which balance is requested. + * @return {Promise} Base token (ETH) Balance. + * @private + */ + async _getBaseTokenBalance(address) { + return new BN(await this.web3.eth.getBalance(address)); + } +} + +module.exports = ProgressRevertRedeemAssertion; diff --git a/test_integration/03_redeem_and_unstake/utils/progress_unstake_assertion.js b/test_integration/03_redeem_and_unstake/utils/progress_unstake_assertion.js index 9fae63db..f5acdedb 100644 --- a/test_integration/03_redeem_and_unstake/utils/progress_unstake_assertion.js +++ b/test_integration/03_redeem_and_unstake/utils/progress_unstake_assertion.js @@ -23,11 +23,11 @@ const assert = require('assert'); const Utils = require('../../../test/test_lib/utils.js'); /** - * Redeem Request object contains all the properties for redeem and unStake. + * Redeem Request object contains all the properties for redeem and unstake. * @typedef {Object} RedeemRequest * @property {BN} amount Redeem amount. * @property {BN} gasPrice Gas price that Redeemer is ready to pay to get the - * redeem and unStake process done. + * redeem and unstake process done. * @property {BN} gasLimit Gas limit that redeemer is ready to pay. * @property {string} redeemer Address of Redeemer. * @property {BN} bounty Bounty amount paid for redeem and unstake message @@ -44,12 +44,12 @@ const Utils = require('../../../test/test_lib/utils.js'); * BaseToken(ETH) and token ERC20 balance of gateway, beneficiary and * stakeVault. * @typedef {Object} Balances - * @property balances.token.gateway ERC20 balance of gateway contract. - * @property balances.token.beneficiary ERC20 balance of beneficiary. - * @property balances.token.stakeVault ERC20 balance of stakeVault contract. - * @property balances.baseToken.gateway Base token(ETH) balance of gateway. - * @property balances.baseToken.beneficiary Base token(ETH) balance of beneficiary. - * @property balances.baseToken.stakeVault Base token(ETH) balance of stakeVault + * @property {BN} balances.token.gateway ERC20 balance of gateway contract. + * @property {BN} balances.token.beneficiary ERC20 balance of beneficiary. + * @property {BN} balances.token.stakeVault ERC20 balance of stakeVault contract. + * @property {BN} balances.baseToken.gateway Base token(ETH) balance of gateway. + * @property {BN} balances.baseToken.beneficiary Base token(ETH) balance of beneficiary. + * @property {BN} balances.baseToken.stakeVault Base token(ETH) balance of stakeVault */ /** @@ -270,8 +270,7 @@ class ProgressUnStakeAssertion { Utils.ZERO_BYTES32, `Unlock secret must be ${Utils.ZERO_BYTES32}.`, ); - } - else { + } else { assert.strictEqual( eventData._unlockSecret, redeemRequest.unlockSecret, diff --git a/test_integration/03_redeem_and_unstake/utils/redeem_assertion.js b/test_integration/03_redeem_and_unstake/utils/redeem_assertion.js index 23280476..2981ca79 100644 --- a/test_integration/03_redeem_and_unstake/utils/redeem_assertion.js +++ b/test_integration/03_redeem_and_unstake/utils/redeem_assertion.js @@ -22,11 +22,11 @@ const assert = require('assert'); const BN = require('bn.js'); /** - * Redeem Request object contains all the properties for redeem and unStake. + * Redeem Request object contains all the properties for redeem and unstake. * @typedef {Object} RedeemRequest * @property {BN} amount Redeem amount. * @property {BN} gasPrice Gas price that Redeemer is ready to pay to get the - * redeem and unStake process done. + * redeem and unstake process done. * @property {BN} gasLimit Gas limit that redeemer is ready to pay. * @property {string} redeemer Address of Redeemer. * @property {BN} bounty Bounty amount paid for redeem and unstake message @@ -42,10 +42,10 @@ const BN = require('bn.js'); /** * BaseToken(ETH) and OSTPrime ERC20 balance of cogateway, redeemer. * @typedef {Object} Balances - * @property balances.ostPrime.cogateway ERC20 balance of cogateway contract. - * @property balances.ostPrime.redeemer ERC20 balance of beneficiary. - * @property balances.baseToken.cogateway Base token(ETH) balance of cogateway. - * @property balances.baseToken.redeemer Base token(ETH) balance of redeemer. + * @property {BN} balances.ostPrime.cogateway ERC20 balance of cogateway contract. + * @property {BN} balances.ostPrime.redeemer ERC20 balance of beneficiary. + * @property {BN} balances.baseToken.cogateway Base token(ETH) balance of cogateway. + * @property {BN} balances.baseToken.redeemer Base token(ETH) balance of redeemer. */ /** diff --git a/test_integration/03_redeem_and_unstake/utils/revert_redeem_assertion.js b/test_integration/03_redeem_and_unstake/utils/revert_redeem_assertion.js new file mode 100644 index 00000000..ef2633b1 --- /dev/null +++ b/test_integration/03_redeem_and_unstake/utils/revert_redeem_assertion.js @@ -0,0 +1,233 @@ +// Copyright 2019 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const assert = require('assert'); +const BN = require('bn.js'); + +/** + * Redeem Request object contains all the properties for redeem and unstake. + * @typedef {Object} RedeemRequest + * @property {BN} amount Redeem amount. + * @property {BN} gasPrice Gas price that Redeemer is ready to pay to get the + * redeem and unstake process done. + * @property {BN} gasLimit Gas limit that redeemer is ready to pay. + * @property {string} redeemer Address of Redeemer. + * @property {BN} bounty Bounty amount paid for redeem and unstake message + * transfers. + * @property {BN} nonce Redeem nonce. + * @property {string} beneficiary Address of beneficiary on origin chain. + * @property {string} hashLock Hash Lock provided by the redeemer. + * @property {string} unlockSecret Unlock secret to unlock hash lock. + * @property {string} messageHash Identifier for redeem and unstake process. + * @property {BN} blockHeight Height at which anchor state root is done. + */ + +/** + * BaseToken(ETH) and OSTPrime ERC20 balance of cogateway, redeemer. + * @typedef {Object} Balances + * @property {BN} balances.ostPrime.cogateway ERC20 balance of cogateway contract. + * @property {BN} balances.ostPrime.redeemer ERC20 balance of beneficiary. + * @property {BN} balances.baseToken.cogateway Base token(ETH) balance of cogateway. + * @property {BN} balances.baseToken.redeemer Base token(ETH) balance of redeemer. + */ + +/** + * Class to assert event and balances after revert redeem. + */ +class RevertRedeemAssertion { + /** + * Constructor. + * @param {Object} cogateway Truffle cogateway instance. + * @param {Object} ostPrime Truffle token instance. + * @param {Web3} web3 Web3 instance. + */ + constructor(cogateway, ostPrime, web3) { + this.cogateway = cogateway; + this.token = ostPrime; + this.web3 = web3; + } + + /** + * This verifies event and balances. + * @param {Object} event Event object after decoding. + * @param {RedeemRequest} redeemRequest Redeem request parameters. + * @param {BN} transactionFees Transaction fees in revert redeem request. + * @param {Balances} initialBalances Initial baseToken and token balances. + */ + async verify(event, redeemRequest, transactionFees, initialBalances) { + await this._assertBalancesForRevertRedeem(redeemRequest, transactionFees, initialBalances); + + RevertRedeemAssertion._assertRevertRedeemEvent(event, redeemRequest); + } + + /** + * This captures base token and token balance of cogateway and redeemer + * @param {string} redeemer Redeemer address. + * @return {Promise} + */ + async captureBalances(redeemer) { + const burner = await this.cogateway.burner.call(); + return { + baseToken: { + burner: await this._getEthBalance(burner), + cogateway: await this._getEthBalance(this.cogateway.address), + redeemer: await this._getEthBalance(redeemer), + }, + token: { + burner: await this.token.balanceOf(burner), + cogateway: await this.token.balanceOf(this.cogateway.address), + redeemer: await this.token.balanceOf(redeemer), + }, + }; + } + + /** + * This asserts balances of redeemer and cogateway after revert redeem. + * @param redeemRequest Redeem request parameters. + * @param {BN} transactionFees Transaction fees in revert redeem request. + * @param {Balances} initialBalances Initial balance of redeemer and cogateway + * generated by captureBalances method. + * @private + */ + async _assertBalancesForRevertRedeem(redeemRequest, transactionFees, initialBalances) { + const finalBalances = await this.captureBalances(redeemRequest.redeemer); + + // Assert cogateway balance + const penalty = await this.cogateway.penalty.call(redeemRequest.messageHash); + + const expectedCoGatewayBaseTokenBalance = initialBalances.baseToken.cogateway; + + // Assert cogateway's base token balance is unchanged. + assert.strictEqual( + expectedCoGatewayBaseTokenBalance.eq(finalBalances.baseToken.cogateway), + true, + `CoGateway base token balance must be ${ + expectedCoGatewayBaseTokenBalance.toString(10) + } instead of ${finalBalances.baseToken.cogateway.toString(10)}`, + ); + + const expectedCoGatewayTokenBalance = initialBalances.token.cogateway; + + // Assert cogateway's token balance is unchanged. + assert.strictEqual( + expectedCoGatewayTokenBalance.eq(finalBalances.token.cogateway), + true, + `CoGateway token balance must be ${ + expectedCoGatewayTokenBalance.toString(10) + } instead of ${finalBalances.token.cogateway.toString(10)}`, + ); + + // Assert redeemer balance + const expectedRedeemerBaseTokenBalance = initialBalances.baseToken.redeemer + .sub(penalty).sub(transactionFees); + + // Assert penalty is transferred to cogateway. + assert.strictEqual( + expectedRedeemerBaseTokenBalance.eq(finalBalances.baseToken.redeemer), + true, + `Redeemer base token balance must be ${ + expectedRedeemerBaseTokenBalance.toString(10) + } instead of ${finalBalances.baseToken.redeemer.toString(10)}`, + ); + + const expectedRedeemerTokenBalance = initialBalances.token.redeemer; + + assert.strictEqual( + expectedRedeemerTokenBalance.eq(finalBalances.token.redeemer), + true, + `Redeemer token balance must be ${ + expectedRedeemerTokenBalance.toString(10) + } instead of ${finalBalances.token.redeemer.toString(10)}`, + ); + + // Assert burner balance + const expectedBurnerBaseTokenBalance = initialBalances.baseToken.burner.add(penalty); + + assert.strictEqual( + expectedBurnerBaseTokenBalance.eq(finalBalances.baseToken.burner), + true, + `Burner base token balance must be ${ + expectedBurnerBaseTokenBalance.toString(10) + } instead of ${finalBalances.baseToken.burner.toString(10)}`, + ); + + const expectedBurnerTokenBalance = initialBalances.token.burner; + + assert.strictEqual( + expectedBurnerTokenBalance.eq(finalBalances.token.burner), + true, + `Burner token balance must be ${ + expectedBurnerTokenBalance.toString(10) + } instead of ${finalBalances.token.burner.toString()}`, + ); + } + + /** + * This assert event after revert redeem method. + * @param {Object} event Event object after decoding. + * @param {RedeemRequest} redeemRequest Redeem request parameters. + * @private + */ + static _assertRevertRedeemEvent(event, redeemRequest) { + const eventData = event.RevertRedeemDeclared; + + assert.strictEqual( + eventData._messageHash, + redeemRequest.messageHash, + `Expected message hash ${ + eventData._messageHash + } is different from actual message hash ${redeemRequest.messageHash}`, + ); + assert.strictEqual( + eventData._redeemer, + redeemRequest.redeemer, + `Expected redeemer address ${ + eventData._redeemer + } is different from actual message hash ${redeemRequest.redeemer}`, + ); + assert.strictEqual( + eventData._amount.eq(redeemRequest.amount), + true, + `Expected redeem amount ${ + eventData._amount + } is different from actual redeem amount ${redeemRequest.amount}`, + ); + assert.strictEqual( + eventData._redeemerNonce.eq(redeemRequest.nonce), + true, + `Expected redeemer nonce ${ + eventData._redeemerNonce + } is different from actual redeemer nonce ${redeemRequest.nonce}`, + ); + } + + /** + * Returns ETH balance wrapped in BN. + * @param {string} address Address for which balance is requested. + * @return {Promise} ETH Balance. + * @private + */ + async _getEthBalance(address) { + const balance = await this.web3.eth.getBalance(address); + return new BN(balance); + } +} + +module.exports = RevertRedeemAssertion;