Skip to content
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

Redeem and unstake integration test #638

Merged
merged 10 commits into from
Feb 5, 2019
6 changes: 3 additions & 3 deletions test_integration/02_stake_and_mint/01_stake_and_mint.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ const Utils = require('../../test/test_lib/utils');
const EventDecoder = require('../../test/test_lib/event_decoder');

const StakeAssertion = require('./utils/stake_assertion');
const ProveGatewayAssertion = require('./utils/prove_gateway_assertion');
const ProveGatewayAssertion = require('../lib/prove_gateway_assertion');
const ConfirmStakeIntentAssertion = require('./utils/confirm_stake_intent_assertion');
const ProgressStakeAssertion = require('./utils/progress_stake_assertion');
const ProgressMintAssertion = require('./utils/progress_mint_assertion');
const ProofUtils = require('./utils/proof_utils');
const Anchor = require('./utils/anchor');
const ProofUtils = require('../lib/proof_utils');
const Anchor = require('../lib/anchor');

describe('Stake and mint', async () => {
let assertStake;
Expand Down
269 changes: 269 additions & 0 deletions test_integration/03_redeem_and_unstake/01_redeem_and_unstake.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
// 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 ConfirmRedeemIntentAssertion = require('./utils/confirm_redeem_intent_assertion');
const ProgressRedeemAssertion = require('./utils/progress_redeem_assertion');
const ProgressUnstakeAssertion = require('./utils/progress_unstake_assertion');
const ProveGatewayAssertion = require('../lib/prove_gateway_assertion');

describe('Redeem and Unstake', async () => {
let originAccounts;
let auxiliaryAccounts;
let originWeb3;
let auxiliaryWeb3;

let gateway;
let cogateway;
let token;
let baseToken;
let ostPrime;

let originAnchor;
let redeemRequest;

let redeemAssertion;
let progressRedeemAssertion;
let progressUnstakeAssertion;
let proofUtils;

before(async () => {
originWeb3 = shared.origin.web3;
auxiliaryWeb3 = shared.auxiliary.web3;
originAccounts = shared.origin.accounts;
auxiliaryAccounts = shared.origin.accounts;
token = shared.origin.contracts.Token;
baseToken = shared.origin.contracts.BaseToken;
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,
schemar marked this conversation as resolved.
Show resolved Hide resolved
shared.origin.contracts.Anchor,
shared.origin.organizationAddress,
);

redeemAssertion = new RedeemAssertion(cogateway, ostPrime, auxiliaryWeb3);
progressRedeemAssertion = new ProgressRedeemAssertion(
cogateway,
ostPrime,
auxiliaryWeb3,
);
progressUnstakeAssertion = new ProgressUnstakeAssertion(
gateway,
token,
baseToken,
);
proofUtils = new ProofUtils(auxiliaryWeb3, originWeb3);
});

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 () => {
// Anchor state root.
const blockNumber = await originAnchor.anchorStateRoot(
'latest',
);

// Generate outbox proof for block height for which state root is
// anchored.
const outboxProof = await proofUtils.getOutboxProof(
cogateway.address,
[redeemRequest.messageHash],
auxiliaryWeb3.utils.toHex(blockNumber),
);
redeemRequest.blockHeight = new BN(blockNumber);
// Prove gateway.
let tx = await gateway.proveGateway(
redeemRequest.blockHeight,
outboxProof.encodedAccountValue,
outboxProof.serializedAccountProof,
{ from: originAccounts[0] },
);

let event = EventDecoder.getEvents(tx, gateway);
ProveGatewayAssertion.verify(
event,
redeemRequest.blockHeight,
outboxProof.storageHash,
cogateway.address,
);

tx = await gateway.confirmRedeemIntent(
redeemRequest.redeemer,
redeemRequest.nonce,
redeemRequest.beneficiary,
redeemRequest.amount,
redeemRequest.gasPrice,
redeemRequest.gasLimit,
redeemRequest.blockHeight,
redeemRequest.hashLock,
outboxProof.storageProof[0].serializedProof,
{ from: originAccounts[0] },
);

event = EventDecoder.getEvents(tx, gateway);
// Assert event.
ConfirmRedeemIntentAssertion.verify(event, redeemRequest);
});

it('progresses redeem', async () => {
const initialBalanceBeforeProgress = await progressRedeemAssertion.captureBalances(
redeemRequest.redeemer,
);

const response = await cogateway.progressRedeem(
redeemRequest.messageHash,
redeemRequest.unlockSecret,
{ from: redeemRequest.redeemer },
);

const transactionFeeInProgress = await getTransactionFee(
response,
auxiliaryWeb3,
);
const event = EventDecoder.getEvents(response, cogateway);

await progressRedeemAssertion.verify(
event,
redeemRequest,
transactionFeeInProgress,
initialBalanceBeforeProgress,
);
});

it('progresses unstake', async () => {

const facilitator = originAccounts[0];
const initialBalancesBeforeProgress = await progressUnstakeAssertion.captureBalances(
redeemRequest.beneficiary,
facilitator,
);

const tx = await gateway.progressUnstake(
redeemRequest.messageHash,
redeemRequest.unlockSecret,
{ from: facilitator },
);

const event = EventDecoder.getEvents(tx, gateway);

await progressUnstakeAssertion.verify(
event,
redeemRequest,
initialBalancesBeforeProgress,
facilitator,
);
});
});

/**
* This approves the cogateway for redeem amount by unwrapping the base token.
0xsarvesh marked this conversation as resolved.
Show resolved Hide resolved
* @param {Object} ostPrime OSTPrime contract instance.
* @param {Object} redeemRequest Redeem request object.
* @param {Object} cogateway CoGateway contract instance.
* @return {Promise<void>}
*/
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<BN>} 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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// 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 redeem intent.
0xsarvesh marked this conversation as resolved.
Show resolved Hide resolved
*/
class ConfirmRedeemIntentAssertion {
/**
* This verifies event.
0xsarvesh marked this conversation as resolved.
Show resolved Hide resolved
* @param {Object} event Event object after decoding.
* @param {RedeemRequest} redeemRequest Redeem request parameters.
*/
static verify(event, redeemRequest) {
const eventData = event.RedeemIntentConfirmed;

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(
eventData._beneficiary,
redeemRequest.beneficiary,
`Beneficiary address from event must be equal to ${redeemRequest.beneficiary}.`,
);

assert.strictEqual(
redeemRequest.amount.eq(eventData._amount),
true,
`Amount from event must be equal to ${redeemRequest.amount.toString(10)}.`,
);

assert.strictEqual(
redeemRequest.blockHeight.eq(eventData._blockHeight),
true,
`Block height from event must be equal to ${redeemRequest.blockHeight.toString(10)}.`,
);

assert.strictEqual(
eventData._hashLock,
redeemRequest.hashLock,
`Hash lock from event must be equal to ${redeemRequest.hashLock}.`,
);
}
}

module.exports = ConfirmRedeemIntentAssertion;
Loading