fix: add lastId caching
This commit is contained in:
parent
aaf86ff2ba
commit
83d12f50df
|
@ -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
|
||||
|
@ -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: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
// 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) ) {
|
||||
|
||||
// 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;
|
||||
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]]);
|
||||
|
|
|
@ -52,6 +52,7 @@ let initialOwnerTokenAccount: PublicKey;
|
|||
|
||||
test('create new token', async () => {
|
||||
const connection = new Connection(url);
|
||||
connection._disableLastIdCaching = mockRpcEnabled;
|
||||
|
||||
initialOwner = await newAccountWithTokens(connection);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
{
|
||||
|
@ -319,6 +322,7 @@ test('transfer', async () => {
|
|||
|
||||
test('approve/revoke', async () => {
|
||||
const connection = new Connection(url);
|
||||
connection._disableLastIdCaching = mockRpcEnabled;
|
||||
const delegateOwner = await newAccountWithTokens(connection);
|
||||
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue