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 {PublicKey} from './publickey';
import {sleep} from './util/sleep';
import type {Account} from './account';
import type {TransactionSignature, TransactionId} from './transaction';
@ -158,6 +159,7 @@ export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntim
*/
export class Connection {
_rpcRequest: RpcRequest;
_lastId: null | TransactionId;
/**
* Establish a JSON RPC connection
@ -169,6 +171,7 @@ export class Connection {
throw new Error('Connection endpoint not specified');
}
this._rpcRequest = createRpcRequest(endpoint);
this._lastId = null;
}
/**
@ -298,7 +301,26 @@ export class Connection {
* Sign and send a transaction
*/
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);
const wireTransaction = transaction.serialize();

View File

@ -6,6 +6,7 @@ import {
SystemProgram,
} from '../src';
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
import {mockGetLastId} from './mockrpc/getlastid';
import {url} from './url';
if (!mockRpcEnabled) {
@ -117,18 +118,7 @@ test('get transaction count', async () => {
test('get last Id', async () => {
const connection = new Connection(url);
mockRpc.push([
url,
{
method: 'getLastId',
params: [],
},
{
error: null,
result: '2BjEqiiT43J6XskiHdz7aoocjPeWkCPiKD72SiFQsrA2',
}
]
);
mockGetLastId();
const lastId = await connection.getLastId();
expect(lastId.length).toBeGreaterThanOrEqual(43);
@ -283,18 +273,7 @@ test('transaction', async () => {
await connection.requestAirdrop(accountTo.publicKey, 21);
expect(await connection.getBalance(accountTo.publicKey)).toBe(21);
mockRpc.push([
url,
{
method: 'getLastId',
params: [],
},
{
error: null,
result: '2BjEqiiT43J6XskiHdz7aoocjPeWkCPiKD72SiFQsrA2',
}
]
);
mockGetLastId();
mockRpc.push([
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 {url} from './url';
import {newAccountWithTokens} from './new-account-with-tokens';
import {mockGetLastId} from './mockrpc/getlastid';
if (!mockRpcEnabled) {
// The default of 5 seconds is too slow for live testing sometimes
jest.setTimeout(10000);
}
function mockGetLastId() {
mockRpc.push([
url,
{
method: 'getLastId',
params: [],
},
{
error: null,
result: '2BjEqiiT43J6XskiHdz7aoocjPeWkCPiKD72SiFQsrA2',
}
]);
}
function mockGetSignatureStatus(result: string = 'Confirmed') {
mockRpc.push([
url,