Browse Source

Integration-tests: Add decremental tip functionality to ensure council votes are processed in the expected order

Leszek Wiesner 3 years ago
parent
commit
8f2bcc32f9

+ 25 - 6
tests/network-tests/src/Api.ts

@@ -201,8 +201,12 @@ export class Api {
 
   public async sendExtrinsicsAndGetResults(
     // Extrinsics can be separated into batches in order to makes sure they are processed in specified order
+    // (each batch will only be processed after the previous one has been fully executed)
     txs: SubmittableExtrinsic<'promise'>[] | SubmittableExtrinsic<'promise'>[][],
-    sender: AccountId | string | AccountId[] | string[]
+    sender: AccountId | string | AccountId[] | string[],
+    // Including decremental tip allows ensuring that the submitted transactions within a batch are processed in the expected order
+    // even when we're using different accounts
+    decrementalTipAmount = 0
   ): Promise<ISubmittableResult[]> {
     let results: ISubmittableResult[] = []
     const batches = (Array.isArray(txs[0]) ? txs : [txs]) as SubmittableExtrinsic<'promise'>[][]
@@ -210,9 +214,14 @@ export class Api {
       const batch = batches[i]
       results = results.concat(
         await Promise.all(
-          batch.map((tx, j) =>
-            this.sender.signAndSend(tx, Array.isArray(sender) ? sender[parseInt(i) * batch.length + j] : sender)
-          )
+          batch.map((tx, j) => {
+            const tip = Array.isArray(sender) ? decrementalTipAmount * (batch.length - 1 - j) : 0
+            return this.sender.signAndSend(
+              tx,
+              Array.isArray(sender) ? sender[parseInt(i) * batch.length + j] : sender,
+              tip
+            )
+          })
         )
       )
     }
@@ -314,7 +323,10 @@ export class Api {
 
   public async prepareAccountsForFeeExpenses(
     accountOrAccounts: string | string[],
-    extrinsics: SubmittableExtrinsic<'promise'>[]
+    extrinsics: SubmittableExtrinsic<'promise'>[],
+    // Including decremental tip allows ensuring that the submitted transactions are processed in the expected order
+    // even when we're using different accounts
+    decrementalTipAmount = 0
   ): Promise<void> {
     const fees = await Promise.all(
       extrinsics.map((tx, i) =>
@@ -323,7 +335,14 @@ export class Api {
     )
 
     if (Array.isArray(accountOrAccounts)) {
-      await Promise.all(fees.map((fee, i) => this.treasuryTransferBalance(accountOrAccounts[i], fee)))
+      await Promise.all(
+        fees.map((fee, i) =>
+          this.treasuryTransferBalance(
+            accountOrAccounts[i],
+            fee.addn(decrementalTipAmount * (accountOrAccounts.length - 1 - i))
+          )
+        )
+      )
     } else {
       await this.treasuryTransferBalance(
         accountOrAccounts,

+ 5 - 2
tests/network-tests/src/fixtures/council/ElectCouncilFixture.ts

@@ -64,8 +64,11 @@ export class ElectCouncilFixture extends BaseQueryNodeFixture {
       const commitment = blake2AsHex(payload)
       return api.tx.referendum.vote(commitment, voteStake)
     })
-    await api.prepareAccountsForFeeExpenses(votersStakingAccounts, votingTxs)
-    await api.sendExtrinsicsAndGetResults(votingTxs, votersStakingAccounts)
+    // Due to the fact that we need the transactions to be processed in the expected order
+    // (which is not guaranteed by the nonce, because we're using different voter accounts),
+    // we'll be including a small, decremental tip (10 JOY * (votersStakingAccounts.length - 1 - accIndex))
+    await api.prepareAccountsForFeeExpenses(votersStakingAccounts, votingTxs, 10)
+    await api.sendExtrinsicsAndGetResults(votingTxs, votersStakingAccounts, 10)
 
     // Revealing stage
     await this.api.untilCouncilStage('Revealing')

+ 0 - 2
tests/network-tests/src/flows/council/elect.ts

@@ -3,8 +3,6 @@ import { extendDebug } from '../../Debugger'
 import { FixtureRunner } from '../../Fixture'
 import { ElectCouncilFixture } from '../../fixtures/council/ElectCouncilFixture'
 
-// Currently only used by proposals flow
-
 export default async function electCouncil({ api, query }: FlowProps): Promise<void> {
   const debug = extendDebug('flow:elect-council')
   debug('Started')

+ 0 - 2
tests/network-tests/src/flows/council/failToElect.ts

@@ -3,8 +3,6 @@ import { extendDebug } from '../../Debugger'
 import { FixtureRunner } from '../../Fixture'
 import { NotEnoughCandidatesFixture, NotEnoughCandidatesWithVotesFixture } from '../../fixtures/council'
 
-// Currently only used by Olympia flow
-
 export default async function failToElectCouncil({ api, query }: FlowProps): Promise<void> {
   const debug = extendDebug('flow:fail-to-elect-council')
   debug('Started')

+ 5 - 3
tests/network-tests/src/sender.ts

@@ -8,6 +8,7 @@ import { extendDebug, Debugger } from './Debugger'
 import AsyncLock from 'async-lock'
 import { assert } from 'chai'
 import BN from 'bn.js'
+import { formatBalance } from '@polkadot/util'
 
 export enum LogLevel {
   None,
@@ -43,7 +44,8 @@ export class Sender {
 
   public async signAndSend(
     tx: SubmittableExtrinsic<'promise'>,
-    account: AccountId | string
+    account: AccountId | string,
+    tip?: BN | number
   ): Promise<ISubmittableResult> {
     const addr = this.keyring.encodeAddress(account)
     const senderKeyPair: KeyringPair = this.keyring.getPair(addr)
@@ -135,13 +137,13 @@ export class Sender {
       const nodeNonce = await this.api.rpc.system.accountNextIndex(senderKeyPair.address)
       const cachedNonce = nonceCacheByAccount.get(senderKeyPair.address)
       const nonce = BN.max(nodeNonce, new BN(cachedNonce || 0))
-      const signedTx = tx.sign(senderKeyPair, { nonce })
+      const signedTx = tx.sign(senderKeyPair, { nonce, tip })
       sentTx = signedTx.toHuman()
       const { method, section } = signedTx.method
       try {
         await signedTx.send(handleEvents)
         if (this.logs === LogLevel.Verbose) {
-          this.debug('Submitted tx:', `${section}.${method} (nonce: ${nonce})`)
+          this.debug('Submitted tx:', `${section}.${method} (nonce: ${nonce}, tip: ${formatBalance(tip)})`)
         }
         nonceCacheByAccount.set(account.toString(), nonce.toNumber() + 1)
       } catch (err) {