fix: wait for the next lastId before sending a new transaction

This commit is contained in:
Michael Vines
2018-10-22 15:31:56 -07:00
parent d9b98918b6
commit 9c8cc0bd24
4 changed files with 50 additions and 39 deletions

View File

@ -7,6 +7,7 @@ import {struct} from 'superstruct';
import {Transaction} from './transaction'; import {Transaction} from './transaction';
import {PublicKey} from './publickey'; import {PublicKey} from './publickey';
import {sleep} from './util/sleep';
import type {Account} from './account'; import type {Account} from './account';
import type {TransactionSignature, TransactionId} from './transaction'; import type {TransactionSignature, TransactionId} from './transaction';
@ -158,6 +159,7 @@ export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntim
*/ */
export class Connection { export class Connection {
_rpcRequest: RpcRequest; _rpcRequest: RpcRequest;
_lastId: null | TransactionId;
/** /**
* Establish a JSON RPC connection * Establish a JSON RPC connection
@ -169,6 +171,7 @@ export class Connection {
throw new Error('Connection endpoint not specified'); throw new Error('Connection endpoint not specified');
} }
this._rpcRequest = createRpcRequest(endpoint); this._rpcRequest = createRpcRequest(endpoint);
this._lastId = null;
} }
/** /**
@ -298,7 +301,26 @@ export class Connection {
* Sign and send a transaction * Sign and send a transaction
*/ */
async sendTransaction(from: Account, transaction: Transaction): Promise<TransactionSignature> { async sendTransaction(from: Account, transaction: Transaction): Promise<TransactionSignature> {
transaction.lastId = await this.getLastId();
let attempts = 0;
for (;;) {
transaction.lastId = await this.getLastId();
// TODO: Waiting for the next lastId is really only necessary if a second
// transaction with the raw input bytes as an in-flight transaction
// is issued.
if (this._lastId != transaction.lastId) {
this._lastId = transaction.lastId;
break;
}
if (attempts === 20) {
throw new Error('Unable to obtain new last id');
}
// TODO: Add a pubsub notification for obtaining the next last id instead
// of polling?
await sleep(100);
++attempts;
}
transaction.sign(from); transaction.sign(from);
const wireTransaction = transaction.serialize(); const wireTransaction = transaction.serialize();

View File

@ -6,6 +6,7 @@ import {
SystemProgram, SystemProgram,
} from '../src'; } from '../src';
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch'; import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
import {mockGetLastId} from './mockrpc/getlastid';
import {url} from './url'; import {url} from './url';
if (!mockRpcEnabled) { if (!mockRpcEnabled) {
@ -117,18 +118,7 @@ test('get transaction count', async () => {
test('get last Id', async () => { test('get last Id', async () => {
const connection = new Connection(url); const connection = new Connection(url);
mockRpc.push([ mockGetLastId();
url,
{
method: 'getLastId',
params: [],
},
{
error: null,
result: '2BjEqiiT43J6XskiHdz7aoocjPeWkCPiKD72SiFQsrA2',
}
]
);
const lastId = await connection.getLastId(); const lastId = await connection.getLastId();
expect(lastId.length).toBeGreaterThanOrEqual(43); expect(lastId.length).toBeGreaterThanOrEqual(43);
@ -283,18 +273,7 @@ test('transaction', async () => {
await connection.requestAirdrop(accountTo.publicKey, 21); await connection.requestAirdrop(accountTo.publicKey, 21);
expect(await connection.getBalance(accountTo.publicKey)).toBe(21); expect(await connection.getBalance(accountTo.publicKey)).toBe(21);
mockRpc.push([ mockGetLastId();
url,
{
method: 'getLastId',
params: [],
},
{
error: null,
result: '2BjEqiiT43J6XskiHdz7aoocjPeWkCPiKD72SiFQsrA2',
}
]
);
mockRpc.push([ mockRpc.push([
url, url,
{ {

View File

@ -0,0 +1,23 @@
// @flow
import {
Account,
} from '../../src';
import {url} from '../url';
import {mockRpc} from '../__mocks__/node-fetch';
export function mockGetLastId() {
const lastId = new Account();
mockRpc.push([
url,
{
method: 'getLastId',
params: [],
},
{
error: null,
result: lastId.publicKey.toBase58(),
}
]);
}

View File

@ -10,26 +10,13 @@ import {SYSTEM_TOKEN_PROGRAM_ID} from '../src/token-program';
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch'; import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
import {url} from './url'; import {url} from './url';
import {newAccountWithTokens} from './new-account-with-tokens'; import {newAccountWithTokens} from './new-account-with-tokens';
import {mockGetLastId} from './mockrpc/getlastid';
if (!mockRpcEnabled) { if (!mockRpcEnabled) {
// The default of 5 seconds is too slow for live testing sometimes // The default of 5 seconds is too slow for live testing sometimes
jest.setTimeout(10000); jest.setTimeout(10000);
} }
function mockGetLastId() {
mockRpc.push([
url,
{
method: 'getLastId',
params: [],
},
{
error: null,
result: '2BjEqiiT43J6XskiHdz7aoocjPeWkCPiKD72SiFQsrA2',
}
]);
}
function mockGetSignatureStatus(result: string = 'Confirmed') { function mockGetSignatureStatus(result: string = 'Confirmed') {
mockRpc.push([ mockRpc.push([
url, url,