-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add services payment test * new check * fmt * services payment new test
- Loading branch information
Showing
2 changed files
with
440 additions
and
0 deletions.
There are no files selected for viewing
280 changes: 280 additions & 0 deletions
280
test/suites/dev-tanssi-relay/services-payment/test_services_payment_block_credits.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
import "@tanssi/api-augment"; | ||
import { describeSuite, expect, beforeAll, customDevRpcRequest } from "@moonwall/cli"; | ||
import { ApiPromise } from "@polkadot/api"; | ||
import { generateKeyringPair, KeyringPair } from "@moonwall/util"; | ||
import { jumpToSession, jumpSessions } from "util/block"; | ||
import { paraIdTank } from "util/payment"; | ||
|
||
describeSuite({ | ||
id: "DTR0902", | ||
title: "Services payment test suite", | ||
foundationMethods: "dev", | ||
testCases: ({ it, context }) => { | ||
let polkadotJs: ApiPromise; | ||
let alice: KeyringPair; | ||
const blocksPerSession = 10n; | ||
|
||
beforeAll(async () => { | ||
polkadotJs = context.polkadotJs(); | ||
alice = context.keyring.alice; | ||
}); | ||
it({ | ||
id: "E01", | ||
title: "Genesis container chains have credits and collators", | ||
test: async function () { | ||
await context.createBlock(); | ||
await customDevRpcRequest("mock_enableParaInherentCandidate", []); | ||
// Since collators are not assigned until session 2, we need to go till session 2 to actually see heads being injected | ||
await jumpToSession(context, 2); | ||
const parasRegistered = await polkadotJs.query.containerRegistrar.registeredParaIds(); | ||
|
||
for (const paraId of parasRegistered.toJSON()) { | ||
// Should have credits | ||
const credits = await polkadotJs.query.servicesPayment.blockProductionCredits(paraId); | ||
expect( | ||
credits.toJSON(), | ||
`Container chain ${paraId} does not have enough credits at genesis` | ||
).toBeGreaterThanOrEqual(2n * blocksPerSession); | ||
|
||
// Should have assigned collators | ||
const collators = await polkadotJs.query.tanssiCollatorAssignment.collatorContainerChain(); | ||
|
||
// We are evaluating blockCredits for now, so lets put a lot of collatorAssignmentCredits | ||
const tx = polkadotJs.tx.servicesPayment.setCollatorAssignmentCredits(paraId, 1000n); | ||
await context.createBlock([await polkadotJs.tx.sudo.sudo(tx).signAsync(alice)]); | ||
|
||
// Container chain 2001 does not have any collators, this will result in only 1 container chain | ||
// producing blocks at a time. So if both container chains have 1000 credits, container 2000 | ||
// will produce blocks 0-999, and container 2001 will produce blocks 1000-1999. | ||
if (paraId == 2000) { | ||
expect( | ||
collators.toJSON().containerChains[paraId].length, | ||
`Container chain ${paraId} has 0 collators` | ||
).toBeGreaterThan(0); | ||
} | ||
} | ||
}, | ||
}); | ||
|
||
it({ | ||
id: "E02", | ||
title: "Creating a container chain block costs credits", | ||
test: async function () { | ||
// Read num credits of para 2000, then create that many blocks. Check that authorNoting.blockNum does not increase anymore | ||
// and collatorAssignment does not have collators | ||
|
||
// create at least a couple blocks to at least see one block being consumed | ||
// we will be doing this for the whole test, i.e., creating two blocks to ensure the parachain advances | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
|
||
const paraId = 2000n; | ||
|
||
// Create a block, the block number should increase, and the number of credits should decrease | ||
const credits1 = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
const containerBlockNum1 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
|
||
// create at least a couple blocks to at least see one block being consumed | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
|
||
const credits2 = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
const containerBlockNum2 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
expect(containerBlockNum1, "container chain 2000 did not create a block").toBeLessThan( | ||
containerBlockNum2 | ||
); | ||
expect(credits1, "container chain 2000 created a block without burning any credits").toBeGreaterThan( | ||
credits2 | ||
); | ||
|
||
expect( | ||
credits1 - credits2, | ||
"container chain 2000 created a block without burning any credits" | ||
).to.be.eq(containerBlockNum2 - containerBlockNum1); | ||
}, | ||
}); | ||
|
||
it({ | ||
id: "E03", | ||
title: "Collators are unassigned when a container chain does not have enough credits", | ||
test: async function () { | ||
// Create blocks until authorNoting.blockNum does not increase anymore. | ||
// Check that collatorAssignment does not have collators and num credits is less than 2 sessions. | ||
|
||
const paraId = 2000n; | ||
|
||
// Create blocks until the block number stops increasing | ||
let containerBlockNum3 = -1; | ||
let containerBlockNum4 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
|
||
while (containerBlockNum3 != containerBlockNum4) { | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
containerBlockNum3 = containerBlockNum4; | ||
containerBlockNum4 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
} | ||
|
||
// Now the container chain should have less than 2 sessions worth of credits | ||
const credits = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
expect( | ||
credits, | ||
"Container chain 2000 has stopped producing blocks, so it should not have enough credits" | ||
).toBeLessThan(2n * blocksPerSession); | ||
|
||
const collators = await polkadotJs.query.tanssiCollatorAssignment.collatorContainerChain(); | ||
expect( | ||
collators.toJSON().containerChains[paraId], | ||
`Container chain ${paraId} should have 0 collators` | ||
).toBeUndefined(); | ||
}, | ||
}); | ||
|
||
it({ | ||
id: "E04", | ||
title: "Root can remove credits", | ||
test: async function () { | ||
// Remove all the credits of container chain 2001, which should have assigned collators now | ||
// This checks that the node does not panic when we try to subtract credits from 0 (saturating_sub) | ||
|
||
const paraId = 2001n; | ||
const credits = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
expect(credits, "Container chain 2001 does not have enough credits").toBeGreaterThanOrEqual( | ||
2n * blocksPerSession | ||
); | ||
|
||
// Should have assigned collators | ||
const collators = await polkadotJs.query.tanssiCollatorAssignment.collatorContainerChain(); | ||
expect( | ||
collators.toJSON().containerChains[paraId].length, | ||
`Container chain ${paraId} has 0 collators` | ||
).toBeGreaterThan(0); | ||
|
||
await context.createBlock(); | ||
await context.createBlock(); | ||
|
||
// Create a block, the block number should increase, and the number of credits should decrease | ||
const credits1 = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
const containerBlockNum1 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
const credits2 = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
const containerBlockNum2 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
expect(containerBlockNum1, "container chain 2001 did not create a block").toBeLessThan( | ||
containerBlockNum2 | ||
); | ||
expect(credits1, "container chain 2001 created a block without burning any credits").toBeGreaterThan( | ||
credits2 | ||
); | ||
|
||
// Set credits to 0 | ||
const tx = polkadotJs.tx.servicesPayment.setBlockProductionCredits(paraId, 0n); | ||
await context.createBlock([await polkadotJs.tx.sudo.sudo(tx).signAsync(alice)]); | ||
|
||
const credits3 = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON() || 0; | ||
expect(credits3).toBe(0); | ||
// Can still create blocks | ||
const containerBlockNum3 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
const credits4 = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON() || 0; | ||
const containerBlockNum4 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
expect( | ||
containerBlockNum3, | ||
"container chain 2001 did not create a block after root set credits to 0" | ||
).toBeLessThan(containerBlockNum4); | ||
// But credits cannot be lower than 0 | ||
expect(credits4, "container chain 2001 has negative credits").toBe(0); | ||
}, | ||
}); | ||
|
||
it({ | ||
id: "E05", | ||
title: "Can buy additional credits", | ||
test: async function () { | ||
// As alice, buy credits for para 2000. Check that it is assigned collators again | ||
const paraId = 2000n; | ||
|
||
// Create blocks until no collators are assigned to any container chain | ||
for (;;) { | ||
await context.createBlock(); | ||
const collators = await polkadotJs.query.tanssiCollatorAssignment.collatorContainerChain(); | ||
if (Object.keys(collators.toJSON().containerChains).length == 0) { | ||
break; | ||
} | ||
} | ||
|
||
// Use random account instead of alice because alice is getting block rewards | ||
const randomAccount = generateKeyringPair("sr25519"); | ||
const value = 100_000_000_000n; | ||
await context.createBlock([ | ||
await polkadotJs.tx.balances.transferAllowDeath(randomAccount.address, value).signAsync(alice), | ||
]); | ||
|
||
// Now, buy some credits for container chain 2000 | ||
const balanceBefore = ( | ||
await polkadotJs.query.system.account(randomAccount.address) | ||
).data.free.toBigInt(); | ||
const purchasedCredits = 1000n * blocksPerSession; | ||
|
||
const requiredBalance = purchasedCredits * 1_000_000n; | ||
const tx = polkadotJs.tx.servicesPayment.purchaseCredits(paraId, requiredBalance); | ||
await context.createBlock([await tx.signAsync(randomAccount)]); | ||
|
||
const balanceAfter = ( | ||
await polkadotJs.query.system.account(randomAccount.address) | ||
).data.free.toBigInt(); | ||
expect(balanceAfter).toBeLessThan(balanceBefore); | ||
|
||
const balanceTank = (await polkadotJs.query.system.account(paraIdTank(paraId))).data.free.toBigInt(); | ||
expect(balanceTank).toBe(requiredBalance); | ||
|
||
// Check that after 2 sessions, container chain 2000 has collators and is producing blocks | ||
await jumpSessions(context, 2); | ||
|
||
// spend all credits | ||
let creditsRemaining = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
while (creditsRemaining != 0) { | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
creditsRemaining = (await polkadotJs.query.servicesPayment.blockProductionCredits(paraId)).toJSON(); | ||
} | ||
|
||
// create a new block that should trigger para balance to go down | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
|
||
const collators = await polkadotJs.query.tanssiCollatorAssignment.collatorContainerChain(); | ||
expect( | ||
collators.toJSON().containerChains[paraId].length, | ||
`Container chain ${paraId} has 0 collators` | ||
).toBeGreaterThan(0); | ||
expect(balanceTank).toBe(requiredBalance); | ||
|
||
// Create a block, the block number should increase, and the number of credits should decrease | ||
const containerBlockNum3 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
await context.createBlock(); | ||
await context.createBlock(); | ||
const containerBlockNum4 = await (await polkadotJs.query.authorNoting.latestAuthor(paraId)).toJSON() | ||
.blockNumber; | ||
expect(containerBlockNum3, "container chain 2000 did not create a block").toBeLessThan( | ||
containerBlockNum4 | ||
); | ||
const balanceTankAfter = ( | ||
await polkadotJs.query.system.account(paraIdTank(paraId)) | ||
).data.free.toBigInt(); | ||
expect(balanceTank, "container chain 2000 created a block without burning any credits").toBeGreaterThan( | ||
balanceTankAfter | ||
); | ||
}, | ||
}); | ||
}, | ||
}); |
Oops, something went wrong.