Просмотр исходного кода

network-tests: fix sending transactions

Mokhtar Naamani 4 лет назад
Родитель
Сommit
bdd17715da
3 измененных файлов с 62 добавлено и 52 удалено
  1. 2 0
      tests/network-tests/package.json
  2. 48 50
      tests/network-tests/src/sender.ts
  3. 12 2
      yarn.lock

+ 2 - 0
tests/network-tests/package.json

@@ -15,8 +15,10 @@
     "@joystream/types": "link:../../types",
     "@polkadot/api": "1.26.1",
     "@polkadot/keyring": "3.0.1",
+    "@types/async-lock": "^1.1.2",
     "@types/bn.js": "^4.11.5",
     "@types/lowdb": "^1.0.9",
+    "async-lock": "^1.2.0",
     "bn.js": "^4.11.8",
     "dotenv": "^8.2.0",
     "fs": "^0.0.1-security",

+ 48 - 50
tests/network-tests/src/sender.ts

@@ -1,69 +1,67 @@
-import BN from 'bn.js'
 import { ApiPromise } from '@polkadot/api'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
-import { AccountInfo } from '@polkadot/types/interfaces'
+import { ISubmittableResult } from '@polkadot/types/types/'
 import { KeyringPair } from '@polkadot/keyring/types'
-import { DbService } from './DbService'
+import Debugger from 'debug'
+import AsyncLock from 'async-lock'
+
+const debug = Debugger('sender')
 
 export class Sender {
   private readonly api: ApiPromise
-  private db: DbService = DbService.getInstance()
+  private readonly asyncLock: AsyncLock
+
+  // TODO: add a keyring that is shared here so no need to pass around keys
 
   constructor(api: ApiPromise) {
     this.api = api
+    this.asyncLock = new AsyncLock()
   }
 
-  private async getNonce(address: string): Promise<BN> {
-    const oncahinNonce: BN = (await this.api.query.system.account<AccountInfo>(address)).nonce
-    let nonce: BN
-    if (!this.db.hasNonce(address)) {
-      nonce = oncahinNonce
-    } else {
-      nonce = this.db.getNonce(address)
-    }
-    if (oncahinNonce.gt(nonce)) {
-      nonce = oncahinNonce
-    }
-    const nextNonce: BN = nonce.addn(1)
-    this.db.setNonce(address, nextNonce)
-    return nonce
-  }
-
-  private clearNonce(address: string): void {
-    this.db.removeNonce(address)
-  }
-
+  // Synchronize all sending of transactions into mempool, so we can always safely read
+  // the next account nonce taking mempool into account. This is safe as long as all sending of transactions
+  // from same account occurs in the same process.
+  // Returns a promise that resolves or rejects only after the extrinsic is finalized into a block.
   public async signAndSend(
     tx: SubmittableExtrinsic<'promise'>,
     account: KeyringPair,
-    expectFailure = false
-  ): Promise<void> {
-    return new Promise(async (resolve, reject) => {
-      const nonce: BN = await this.getNonce(account.address)
-      const signedTx = tx.sign(account, { nonce })
-      await signedTx
-        .send(async (result) => {
-          if (result.status.isInBlock && result.events !== undefined) {
-            result.events.forEach((event) => {
-              if (event.event.method === 'ExtrinsicFailed') {
-                if (expectFailure) {
-                  resolve()
-                } else {
-                  reject(new Error('Extrinsic failed unexpectedly'))
-                }
-              }
-            })
-            resolve()
-          }
-          if (result.status.isFuture) {
-            console.log('nonce ' + nonce + ' for account ' + account.address + ' is in future')
-            this.clearNonce(account.address)
-            reject(new Error('Extrinsic nonce is in future'))
+    shouldFail = false
+  ): Promise<any> {
+    let finalizedResolve: { (): void; (value?: any): void }
+    let finalizedReject: { (arg0: Error): void; (reason?: any): void }
+    const finalized = new Promise(async (resolve, reject) => {
+      finalizedResolve = resolve
+      finalizedReject = reject
+    })
+
+    const handleEvents = (result: ISubmittableResult) => {
+      if (result.status.isInBlock && result.events !== undefined) {
+        result.events.forEach((event) => {
+          if (event.event.method === 'ExtrinsicFailed') {
+            if (shouldFail) {
+              finalizedResolve()
+            } else {
+              finalizedReject(new Error('Extrinsic failed unexpectedly'))
+            }
           }
         })
-        .catch((error) => {
-          reject(error)
-        })
+        finalizedResolve()
+      }
+
+      if (result.status.isFuture) {
+        // Its virtually impossible for use to continue with tests
+        // when this occurs and we don't expect the tests to handle this correctly
+        // so just abort!
+        process.exit(-1)
+      }
+    }
+
+    await this.asyncLock.acquire(`${account.address}`, async () => {
+      const nonce = await this.api.rpc.system.accountNextIndex(account.address)
+      const signedTx = tx.sign(account, { nonce })
+      await signedTx.send(handleEvents)
     })
+
+    return finalized
   }
 }

+ 12 - 2
yarn.lock

@@ -3076,7 +3076,7 @@
     is-ipfs "^0.6.0"
     recursive-fs "^1.1.2"
 
-"@polkadot/api-contract@1.26.1", "@polkadot/api-contract@^1.26.1":
+"@polkadot/api-contract@^1.26.1":
   version "1.26.1"
   resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-1.26.1.tgz#a8b52ef469ab8bbddb83191f8d451e31ffd76142"
   integrity sha512-zLGA/MHUJf12vanUEUBBRqpHVAONHWztoHS0JTIWUUS2+3GEXk6hGw+7PPtBDfDsLj0LgU/Qna1bLalC/zyl5w==
@@ -4030,6 +4030,11 @@
   resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
   integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==
 
+"@types/async-lock@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.2.tgz#cbc26a34b11b83b28f7783a843c393b443ef8bef"
+  integrity sha512-j9n4bb6RhgFIydBe0+kpjnBPYumDaDyU8zvbWykyVMkku+c2CSu31MZkLeaBfqIwU+XCxlDpYDfyMQRkM0AkeQ==
+
 "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
   version "7.1.9"
   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d"
@@ -6697,7 +6702,12 @@ bluebird@^3.1.1, bluebird@^3.3.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
   integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
 
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^5.1.2:
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.4.0:
+  version "4.11.9"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
+  integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
+
+bn.js@^5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz#c9686902d3c9a27729f43ab10f9d79c2004da7b0"
   integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==