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

Add multi currency vaults #415

Merged
merged 39 commits into from
Jun 6, 2022
Merged

Add multi currency vaults #415

merged 39 commits into from
Jun 6, 2022

Conversation

bvotteler
Copy link
Contributor

@bvotteler bvotteler commented May 26, 2022

Add KINT as collateral

  • Updated tests/code to work with upgraded docker images (parachain version 1.13.1, clients version 1.11.2)
  • Added new collateral currency, KINT
  • Updated tests to run for KSM as well as KINT collateral currencies
  • Enabled and fixed test checking vault theft and liquidation
  • Update @polkadot/api to version 8.6.2

Breaking changes to exposed API:

  • VaultsAPI.reportVaultTheft now takes vault ID (InterbtcPrimitivesVaultId) instead of only account ID
  • VaultsAPI.isVaultFlaggedForTheft now takes vault ID (InterbtcPrimitivesVaultId) instead of account ID and collateral currency literal ID
  • Removed ConstantsAPI.getTransactionByteFee(); constant no longer exists

@bvotteler bvotteler marked this pull request as ready for review June 1, 2022 16:17
await waitForBlockFinalization(bitcoinCoreClient, userInterBtcAPI.btcRelay);

// report theft
await reporterInterBtcAPI.vaults.reportVaultTheft(vaultToLiquidateId, btcTxId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Anyone can report theft, so you could have reused userInterBtcAPI instead of instantiating reporterInterBtcAPI

"0",
`Expected non-zero amount for refund request with vault 3 (${currencyTicker})`
);
const refundRequests = await interBtcAPI.refund.list();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length of this list should be checked after the for-loop ends, with an expected length of at least vault_3_ids.length. We can merge without this change though, since the assert on the line above already checks this essentially.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot, thanks. I have a follow-up task to address this.

Comment on lines +96 to +100
interBtcAPI.setAccount(vault_3);
await interBtcAPI.replace.request(
replaceAmount,
currencyIdToMonetaryCurrency(vault_3_id.currencies.collateral) as CollateralCurrency
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cleaner to refactor this to use the callWith utility.

Copy link
Contributor Author

@bvotteler bvotteler Jun 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nice! Thank you for that. Added these to the follow-up task, too.

Comment on lines +146 to +153
interBtcAPI.setAccount(vault_2);

// fetch tokens held by vault
const tokensInVault = await interBtcAPI.vaults.getIssuedAmount(
newAccountId(api, vault_2.address),
currencyIdToLiteral(vault_2_id.currencies.collateral)
);
assert.fail("Expected error to be thrown due to lack of issued tokens for vault, but call completed.");
} catch (e) {
assert.isTrue(e instanceof Error, "Expected replace request to fail with Error");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cleaner to refactor this to use the callWith utility.

Comment on lines +158 to +163
await interBtcAPI.replace.request(
replaceAmount,
currencyIdToMonetaryCurrency(vault_2_id.currencies.collateral) as CollateralCurrency
);
assert.fail(`Expected error to be thrown due to lack of issued tokens
for vault (collateral: ${currencyTicker}), but call completed.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cleaner to check that the Promise is rejected, also not blocking

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also added to the follow-up task.
Thank you for the suggestion.

@daniel-savu
Copy link
Contributor

For later: We should think of a way to make it clear that the tests are running on new parachain and clients versions - maybe tag parachain-launch versions. The yarn docker-parachain-start script just pulls the master branch of the parachain-launch script, so reviewers of this PR would be unaware of any changes.

Copy link
Contributor

@daniel-savu daniel-savu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, all my comments can be fixed later

@bvotteler
Copy link
Contributor Author

We should think of a way to make it clear that the tests are running on new parachain and clients versions - maybe tag parachain-launch versions. The yarn docker-parachain-start script just pulls the master branch of the parachain-launch script, so reviewers of this PR would be unaware of any changes.

Yes! I was thinking along those lines, too.
Certainly a fan of tagging. Using separate versions for our fork of the official parachain-launch can get tricky. So I want to think about that for a bit.

I also want to at least check if we can possibly go back to the upstream version of parachain-launch rather than maintaining a fork, and keep the configuration details in the lib to then be generated as part of yarn build.

Copy link
Member

@gregdhill gregdhill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks good, though it is hard to comment on whether there is enough to catch flaws in our multi-collateral architecture. There are a fair number of skipped and commented tests but I assume these are for future work? We should definitely sync on what can be moved into the parachain / client tests or even removed to make your life easier. I like the overly verbose nature of the assertions and the reduced reliance on test ordering - idempotent tests make for easier debugging. Anyway, none of my comments are particularly critical to block merging this as-is so feel free address in future PRs.

// report theft
await reporterInterBtcAPI.vaults.reportVaultTheft(vaultToLiquidateId, btcTxId);

const finalizedPromise = new Promise<void>((resolve, _) => userInterBtcAPI.system.subscribeToFinalizedBlockHeads(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: could await here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, there are two places where I do that.

assert.isTrue(flaggedForTheft);
assert.isTrue(
flaggedForTheft,
`Expected vault (collateral: ${collateralCurrency.ticker}) to be flagged for theft, but it was not.`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for useful expectation messages

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you.
It would drive me bonkers if I saw a failure and wouldn't know which collateral variation failed without manually running the test with breakpoints. :)

Comment on lines +179 to +188
await issueAndRedeem(
userInterBtcAPI,
userBitcoinCoreClient,
userAccount,
vaultToBanId,
issueAmount,
redeemAmount,
false,
ExecuteRedeem.False
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future ref: #416

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes. I hope to have some time to add more builder patterns soon™ :)

Comment on lines +194 to +200
await new Promise<void>((resolve, _) => {
// redeemAPI.subscribeToRedeemExpiry(newAccountId(api, userAccount.address), (requestId) => {
// if (stripHexPrefix(redeemRequest.id.toString()) === stripHexPrefix(requestId.toString())) {
// resolve();
// }
// });
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to implement this now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had a chat last week with @nud3l to pare down the scope of tests to add/fix to get multi-collateral vaults unblocked sooner rather than later.
This test fell victim to that.

@@ -103,8 +98,7 @@ describe("Initialize parachain state", () => {
userInterBtcAPI = new DefaultInterBtcApi(api, "regtest", userAccount, ESPLORA_BASE_PATH);
sudoInterBtcAPI = new DefaultInterBtcApi(api, "regtest", sudoAccount, ESPLORA_BASE_PATH);

wrappedCurrency = userInterBtcAPI.getWrappedCurrency();
collateralCurrency = getCorrespondingCollateralCurrency(userInterBtcAPI.getGovernanceCurrency());
collateralCurrency = getCorrespondingCollateralCurrencies(userInterBtcAPI.getGovernanceCurrency())[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this implicitly assume KSM? What happens to the tests if I change the return value of getCorrespondingCollateralCurrency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only needs one collateral currency, whether that is KSM, or KINT doesn't matter here.
The currency is used in conjunction with vault account ids to wait for them to be available - as means to delay test execution until the parachain / docker images are good to go with vaults registered.

Comment on lines +250 to +256
await new Promise<void>((resolve, _) => {
// userInterBtcAPI.issue.subscribeToIssueExpiry(newAccountId(api, userAccount.address), (requestId) => {
// if (stripHexPrefix(requestResult.id.toString()) === stripHexPrefix(requestId.toString())) {
// resolve();
// }
// });
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as above, for a future PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, I just added the looping for the time being.

Comment on lines +266 to +270
} catch (e) {
// Set issue period back to its initial value to minimize side effects.
await sudo(userInterBtcAPI, () => userInterBtcAPI.issue.setIssuePeriod(initialIssuePeriod));
throw e;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to reset the state, though I wonder if we should have some common routine that runs after most tests to "re-genesis" from a blank state. However I guess that may dramatically slowdown the pipeline if Substrate doesn't have an easy way to do that though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we have instant seal, we might be able to afford re-genesis delays, assuming it can be done.
I'm certainly open to the idea.

for(const collateralCurrency of collateralCurrencies) {
const bitcoinAmount = BitcoinAmount.from.BTC(100);
const exchangeRate = await interBtcAPI.oracle.getExchangeRate(collateralCurrency);
const expectedCollateral = exchangeRate.toBig(undefined).mul(bitcoinAmount.toBig(BitcoinUnit.BTC)).round(0, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! That was supposed to be exchangeRate.toBig() - looks like me stripping the previously defined, single collateralCurrency, auto-refactored that into undefined.
Awkward, but at least not breaking.

Comment on lines +243 to +248
for (const [vaultId, amount] of premiumRedeemVaults) {
if (encodeVaultId(vaultId) === encodeVaultId(vault_3_id)) {
premiumRedeemAmount = amount;
break;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is probably a more functional way to write this logic but that's probably more a nit from my side

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. A version of array.find().map() would do the trick.
Keeping this for my clean up task.

Comment on lines +263 to +267
const expectedThresholdByTicker: Map<string, string> = new Map([
[Polkadot.ticker, "1.1"],
[Kusama.ticker, "1.1"],
[Kintsugi.ticker, "2"]
]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these set during initialization? If not we should definitely review our use of magic numbers in tests so us changing the genesis on the parachain doesn't randomly break things here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, those are magic numbers.
Happy to set during initialization - I couldn't find a way when working the tests.

I think that piece of work would fit in well with the proposed changes by Dom here: #415 (comment)

@bvotteler
Copy link
Contributor Author

There are a fair number of skipped and commented tests but I assume these are for future work? We should definitely sync on what can be moved into the parachain / client tests or even removed to make your life easier.

As for the skipped/commented tests: Yes, they are future work and haven't been deemed critical for the goal of getting multi-collateral vaults out.

As for reducing integration test numbers: Yes, I absolutely would love to get rid of the ones that the parachain and/or clients test themselves.
That, and the introduction of instant-seal should make these tests much, much faster.

@bvotteler bvotteler merged commit 2935ca2 into interlay:master Jun 6, 2022
@bvotteler bvotteler deleted the brendon/test-add-multi-currency-vaults branch June 6, 2022 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants