From 2a0e48de15feddbb659c8a2ec4bca2a5a4a470d4 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Wed, 24 Feb 2021 21:50:14 -0800 Subject: [PATCH 1/4] Add test for handling of invalid nonce errors --- test/account.test.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/account.test.js b/test/account.test.js index b5ecb83770..67f5ac67cd 100644 --- a/test/account.test.js +++ b/test/account.test.js @@ -1,5 +1,5 @@ -const nearApi = require('../lib/index'); +const { Account, Contract, providers } = require('../lib/index'); const testUtils = require('./test-utils'); const fs = require('fs'); const BN = require('bn.js'); @@ -35,7 +35,7 @@ test('create account and then view account returns the created account', async ( const { amount } = await workingAccount.state(); const newAmount = new BN(amount).div(new BN(10)); await workingAccount.createAccount(newAccountName, newAccountPublicKey, newAmount); - const newAccount = new nearApi.Account(nearjs.connection, newAccountName); + const newAccount = new Account(nearjs.connection, newAccountName); const state = await newAccount.state(); expect(state.amount).toEqual(newAmount.toString()); }); @@ -55,10 +55,20 @@ test('delete account', async() => { const sender = await testUtils.createAccount(nearjs); const receiver = await testUtils.createAccount(nearjs); await sender.deleteAccount(receiver.accountId); - const reloaded = new nearApi.Account(sender.connection, sender); + const reloaded = new Account(sender.connection, sender); await expect(reloaded.state()).rejects.toThrow(); }); +test('multiple parallel transactions', async () => { + const PARALLEL_NUMBER = 5; + await Promise.all([...Array(PARALLEL_NUMBER).keys()].map(async (_, i) => { + const account = new Account(workingAccount.connection, workingAccount.accountId); + // NOTE: Need to have different transactions outside of nonce, or they all succeed by being identical + // TODO: Check if randomization of exponential back off helps to do more transactions without exceeding retries + await account.sendMoney(account.accountId, new BN(i)); + })); +}); + describe('errors', () => { let oldLog; let logs; @@ -97,7 +107,7 @@ describe('with deploy contract', () => { const newPublicKey = await nearjs.connection.signer.createKey(contractId, testUtils.networkId); const data = [...fs.readFileSync(HELLO_WASM_PATH)]; await workingAccount.createAndDeployContract(contractId, newPublicKey, data, HELLO_WASM_BALANCE); - contract = new nearApi.Contract(workingAccount, contractId, { + contract = new Contract(workingAccount, contractId, { viewMethods: ['hello', 'getValue', 'returnHiWithLogs'], changeMethods: ['setValue', 'generateLogs', 'triggerAssert', 'testSetRemove', 'crossContract'] }); @@ -138,7 +148,7 @@ describe('with deploy contract', () => { const setCallValue = testUtils.generateUniqueString('setCallPrefix'); const result2 = await workingAccount.functionCall(contractId, 'setValue', { value: setCallValue }); - expect(nearApi.providers.getTransactionLastResult(result2)).toEqual(setCallValue); + expect(providers.getTransactionLastResult(result2)).toEqual(setCallValue); expect(await workingAccount.viewFunction(contractId, 'getValue', {})).toEqual(setCallValue); }); @@ -219,14 +229,14 @@ describe('with deploy contract', () => { }); test('can have view methods only', async () => { - const contract = new nearApi.Contract(workingAccount, contractId, { + const contract = new Contract(workingAccount, contractId, { viewMethods: ['hello'], }); expect(await contract.hello({ name: 'world' })).toEqual('hello world'); }); test('can have change methods only', async () => { - const contract = new nearApi.Contract(workingAccount, contractId, { + const contract = new Contract(workingAccount, contractId, { changeMethods: ['hello'], }); expect(await contract.hello({ name: 'world' })).toEqual('hello world'); From a0659be0760865bb9031aa6374da639d6dfd7f2b Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Wed, 24 Feb 2021 21:50:58 -0800 Subject: [PATCH 2/4] Add InvalidNonce parsing from string --- src/utils/rpc_errors.ts | 2 ++ test/utils/rpc-errors.test.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/utils/rpc_errors.ts b/src/utils/rpc_errors.ts index b15bd3498b..76f46198db 100644 --- a/src/utils/rpc_errors.ts +++ b/src/utils/rpc_errors.ts @@ -88,6 +88,8 @@ export function getErrorTypeFromErrorMessage(errorMessage) { return 'AccessKeyDoesNotExist'; case /wasm execution failed with error: FunctionCallError\(CompilationError\(CodeDoesNotExist/.test(errorMessage): return 'CodeDoesNotExist'; + case /Transaction nonce \d+ must be larger than nonce of the used access key \d+/.test(errorMessage): + return 'InvalidNonce'; default: return 'UntypedError'; } diff --git a/test/utils/rpc-errors.test.js b/test/utils/rpc-errors.test.js index 5bbdcf0b95..fa05e9d960 100644 --- a/test/utils/rpc-errors.test.js +++ b/test/utils/rpc-errors.test.js @@ -113,10 +113,12 @@ describe('rpc-errors', () => { const err2 = 'Account random2.testnet doesn\'t exist'; const err3 = 'access key ed25519:DvXowCpBHKdbD2qutgfhG6jvBMaXyUh7DxrDSjkLxMHp does not exist while viewing'; const err4 = 'wasm execution failed with error: FunctionCallError(CompilationError(CodeDoesNotExist { account_id: "random.testnet" }))'; + const err5 = '[-32000] Server error: Invalid transaction: Transaction nonce 1 must be larger than nonce of the used access key 1'; expect(getErrorTypeFromErrorMessage(err1)).toEqual('AccountDoesNotExist'); expect(getErrorTypeFromErrorMessage(err2)).toEqual('AccountDoesNotExist'); expect(getErrorTypeFromErrorMessage(err3)).toEqual('AccessKeyDoesNotExist'); expect(getErrorTypeFromErrorMessage(err4)).toEqual('CodeDoesNotExist'); + expect(getErrorTypeFromErrorMessage(err5)).toEqual('InvalidNonce'); expect(getErrorTypeFromErrorMessage('random string')).toEqual('UntypedError'); expect(getErrorTypeFromErrorMessage(undefined)).toEqual('UntypedError'); expect(getErrorTypeFromErrorMessage('')).toEqual('UntypedError'); From 3132ffa85f8cbb37a8b21d17c61f2abaf7547a4c Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Wed, 24 Feb 2021 22:01:24 -0800 Subject: [PATCH 3/4] yarn fix --- test/account.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/account.test.js b/test/account.test.js index 67f5ac67cd..6d1a9c1da4 100644 --- a/test/account.test.js +++ b/test/account.test.js @@ -62,10 +62,10 @@ test('delete account', async() => { test('multiple parallel transactions', async () => { const PARALLEL_NUMBER = 5; await Promise.all([...Array(PARALLEL_NUMBER).keys()].map(async (_, i) => { - const account = new Account(workingAccount.connection, workingAccount.accountId); - // NOTE: Need to have different transactions outside of nonce, or they all succeed by being identical - // TODO: Check if randomization of exponential back off helps to do more transactions without exceeding retries - await account.sendMoney(account.accountId, new BN(i)); + const account = new Account(workingAccount.connection, workingAccount.accountId); + // NOTE: Need to have different transactions outside of nonce, or they all succeed by being identical + // TODO: Check if randomization of exponential back off helps to do more transactions without exceeding retries + await account.sendMoney(account.accountId, new BN(i)); })); }); From f858819d268a33ed4dd577a9b54bdc2a5b9df3c9 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Wed, 24 Feb 2021 22:01:45 -0800 Subject: [PATCH 4/4] yarn compile --- lib/account.d.ts | 2 +- lib/account.js | 2 +- lib/utils/rpc_errors.d.ts | 2 +- lib/utils/rpc_errors.js | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/account.d.ts b/lib/account.d.ts index 3a260d82d5..a793263a08 100644 --- a/lib/account.d.ts +++ b/lib/account.d.ts @@ -121,7 +121,7 @@ export declare class Account { parse?: typeof parseJsonFromRawResponse; }): Promise; /** - * See https://docs.near.org/docs/api/rpc#view-contract-state + * See https://docs.near.org/docs/develop/front-end/rpc#view-contract-state * * Returns the state (key value pairs) of this account's contract based on the key prefix. * Pass an empty string for prefix if you would like to return the entire state. diff --git a/lib/account.js b/lib/account.js index d9e3431c3e..8c5e97bcab 100644 --- a/lib/account.js +++ b/lib/account.js @@ -279,7 +279,7 @@ class Account { return result.result && result.result.length > 0 && parse(Buffer.from(result.result)); } /** - * See https://docs.near.org/docs/api/rpc#view-contract-state + * See https://docs.near.org/docs/develop/front-end/rpc#view-contract-state * * Returns the state (key value pairs) of this account's contract based on the key prefix. * Pass an empty string for prefix if you would like to return the entire state. diff --git a/lib/utils/rpc_errors.d.ts b/lib/utils/rpc_errors.d.ts index 997d8d8943..b51ae559e2 100644 --- a/lib/utils/rpc_errors.d.ts +++ b/lib/utils/rpc_errors.d.ts @@ -6,4 +6,4 @@ declare class ServerTransactionError extends ServerError { export declare function parseRpcError(errorObj: Record): ServerError; export declare function parseResultError(result: any): ServerTransactionError; export declare function formatError(errorClassName: string, errorData: any): string; -export declare function getErrorTypeFromErrorMessage(errorMessage: any): "UntypedError" | "CodeDoesNotExist" | "AccountDoesNotExist" | "AccessKeyDoesNotExist"; +export declare function getErrorTypeFromErrorMessage(errorMessage: any): "UntypedError" | "CodeDoesNotExist" | "AccountDoesNotExist" | "InvalidNonce" | "AccessKeyDoesNotExist"; diff --git a/lib/utils/rpc_errors.js b/lib/utils/rpc_errors.js index 58bc6ee447..56c32bad8e 100644 --- a/lib/utils/rpc_errors.js +++ b/lib/utils/rpc_errors.js @@ -113,6 +113,8 @@ function getErrorTypeFromErrorMessage(errorMessage) { return 'AccessKeyDoesNotExist'; case /wasm execution failed with error: FunctionCallError\(CompilationError\(CodeDoesNotExist/.test(errorMessage): return 'CodeDoesNotExist'; + case /Transaction nonce \d+ must be larger than nonce of the used access key \d+/.test(errorMessage): + return 'InvalidNonce'; default: return 'UntypedError'; }