Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
fix: add lastId caching
Browse files Browse the repository at this point in the history
  • Loading branch information
mvines committed Oct 23, 2018
1 parent b1c64b7 commit ae79cdc
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 19 deletions.
71 changes: 52 additions & 19 deletions src/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,13 @@ export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntim
*/
export class Connection {
_rpcRequest: RpcRequest;
_lastId: null | TransactionId;

_lastIdInfo: {
lastId: TransactionId | null,
seconds: number,
transactionSignatures: Array<string>,
};
_disableLastIdCaching: boolean = false

/**
* Establish a JSON RPC connection
Expand All @@ -171,7 +177,11 @@ export class Connection {
throw new Error('Connection endpoint not specified');
}
this._rpcRequest = createRpcRequest(endpoint);
this._lastId = null;
this._lastIdInfo = {
lastId: null,
seconds: -1,
transactionSignatures: [],
};
}

/**
Expand Down Expand Up @@ -301,27 +311,50 @@ export class Connection {
* Sign and send a transaction
*/
async sendTransaction(from: Account, transaction: Transaction): Promise<TransactionSignature> {

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;
// Attempt to use the previous last id for up to 1 second
const seconds = (new Date()).getSeconds();
if ( (this._lastIdInfo.lastId != null) &&
(this._lastIdInfo.seconds === seconds) ) {

transaction.lastId = this._lastIdInfo.lastId;
transaction.sign(from);
if (!transaction.signature) {
throw new Error('!signature'); // should never happen
}

// If the signature of this transaction has not been seen before with the
// current lastId, all done.
const signature = transaction.signature.toString();
if (!this._lastIdInfo.transactionSignatures.includes(signature)) {
this._lastIdInfo.transactionSignatures.push(signature);
if (this._disableLastIdCaching) {
this._lastIdInfo.seconds = -1;
}
break;
}
}
if (attempts === 20) {
throw new Error('Unable to obtain new last id');

// Fetch a new last id
let attempts = 0;
for (;;) {
const lastId = await this.getLastId();

if (this._lastIdInfo.lastId != lastId) {
this._lastIdInfo = {
lastId,
seconds: (new Date()).getSeconds(),
transactionSignatures: [],
};
break;
}
if (attempts === 8) {
throw new Error('Unable to obtain new last id');
}
await sleep(250);
++attempts;
}
// 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();
const unsafeRes = await this._rpcRequest('sendTransaction', [[...wireTransaction]]);
Expand Down
7 changes: 7 additions & 0 deletions test/token-program.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ let initialOwnerTokenAccount: PublicKey;

test('create new token', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;

initialOwner = await newAccountWithTokens(connection);

Expand Down Expand Up @@ -168,6 +169,7 @@ test('create new token', async () => {

test('create new token account', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const destOwner = await newAccountWithTokens(connection);

{
Expand Down Expand Up @@ -224,6 +226,7 @@ test('create new token account', async () => {

test('transfer', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const destOwner = await newAccountWithTokens(connection);

{
Expand Down Expand Up @@ -319,6 +322,7 @@ test('transfer', async () => {

test('approve/revoke', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const delegateOwner = await newAccountWithTokens(connection);

{
Expand Down Expand Up @@ -453,6 +457,7 @@ test('invalid approve', async () => {
}

const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const owner = await newAccountWithTokens(connection);

const account1 = await testToken.newAccount(owner);
Expand Down Expand Up @@ -488,6 +493,7 @@ test('fail on approve overspend', async () => {
}

const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const owner = await newAccountWithTokens(connection);

const account1 = await testToken.newAccount(owner);
Expand Down Expand Up @@ -552,6 +558,7 @@ test('set owner', async () => {
}

const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const owner = await newAccountWithTokens(connection);
const newOwner = await newAccountWithTokens(connection);

Expand Down

0 comments on commit ae79cdc

Please sign in to comment.