2018-10-06 10:36:59 -07:00
|
|
|
// @flow
|
|
|
|
|
2018-11-28 11:56:50 -08:00
|
|
|
import invariant from 'assert';
|
|
|
|
|
2018-10-25 08:18:59 -07:00
|
|
|
import {Connection} from '../connection';
|
2019-11-11 13:01:10 -05:00
|
|
|
import type {Commitment} from '../connection';
|
2018-10-25 08:18:59 -07:00
|
|
|
import {Transaction} from '../transaction';
|
2018-10-06 10:36:59 -07:00
|
|
|
import {sleep} from './sleep';
|
2018-10-25 08:18:59 -07:00
|
|
|
import type {Account} from '../account';
|
2018-11-03 19:02:12 -07:00
|
|
|
import type {TransactionSignature} from '../transaction';
|
2019-03-19 12:44:55 -07:00
|
|
|
import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from '../timing';
|
2018-10-06 10:36:59 -07:00
|
|
|
|
2020-04-09 15:28:02 -07:00
|
|
|
const MS_PER_SECOND = 1000;
|
|
|
|
const MS_PER_SLOT =
|
|
|
|
(DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND) * MS_PER_SECOND;
|
|
|
|
|
|
|
|
const NUM_SEND_RETRIES = 10;
|
|
|
|
const NUM_STATUS_RETRIES = 10;
|
|
|
|
|
2019-11-11 13:01:10 -05:00
|
|
|
/**
|
|
|
|
* Sign, send and confirm a transaction with recent commitment level
|
|
|
|
*/
|
|
|
|
export async function sendAndConfirmRecentTransaction(
|
|
|
|
connection: Connection,
|
|
|
|
transaction: Transaction,
|
|
|
|
...signers: Array<Account>
|
|
|
|
): Promise<TransactionSignature> {
|
|
|
|
return await _sendAndConfirmTransaction(
|
|
|
|
connection,
|
|
|
|
transaction,
|
|
|
|
signers,
|
|
|
|
'recent',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-10-06 10:36:59 -07:00
|
|
|
/**
|
|
|
|
* Sign, send and confirm a transaction
|
|
|
|
*/
|
|
|
|
export async function sendAndConfirmTransaction(
|
|
|
|
connection: Connection,
|
|
|
|
transaction: Transaction,
|
2018-11-18 08:48:14 -08:00
|
|
|
...signers: Array<Account>
|
2019-11-11 13:01:10 -05:00
|
|
|
): Promise<TransactionSignature> {
|
|
|
|
return await _sendAndConfirmTransaction(connection, transaction, signers);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function _sendAndConfirmTransaction(
|
|
|
|
connection: Connection,
|
|
|
|
transaction: Transaction,
|
|
|
|
signers: Array<Account>,
|
|
|
|
commitment: ?Commitment,
|
2018-11-28 11:56:50 -08:00
|
|
|
): Promise<TransactionSignature> {
|
2020-04-06 17:56:26 +08:00
|
|
|
const statusCommitment = commitment || connection.commitment || 'max';
|
|
|
|
|
2020-04-09 15:28:02 -07:00
|
|
|
let sendRetries = NUM_SEND_RETRIES;
|
2018-11-03 19:02:12 -07:00
|
|
|
let signature;
|
2020-04-06 17:56:26 +08:00
|
|
|
|
2018-10-06 10:36:59 -07:00
|
|
|
for (;;) {
|
2018-10-23 13:10:08 -07:00
|
|
|
const start = Date.now();
|
2018-11-18 08:48:14 -08:00
|
|
|
signature = await connection.sendTransaction(transaction, ...signers);
|
2018-10-23 13:10:08 -07:00
|
|
|
|
2019-03-19 12:44:55 -07:00
|
|
|
// Wait up to a couple slots for a confirmation
|
2019-04-10 14:40:49 -07:00
|
|
|
let status = null;
|
2020-04-09 15:28:02 -07:00
|
|
|
let statusRetries = NUM_STATUS_RETRIES;
|
2018-10-23 13:10:08 -07:00
|
|
|
for (;;) {
|
2020-04-06 17:56:26 +08:00
|
|
|
status = (await connection.getSignatureStatus(signature)).value;
|
2019-04-10 13:15:41 -07:00
|
|
|
if (status) {
|
2020-04-09 15:28:02 -07:00
|
|
|
// Recieved a status, if not an error wait for confirmation
|
|
|
|
statusRetries = NUM_STATUS_RETRIES;
|
|
|
|
if (
|
|
|
|
status.err ||
|
|
|
|
status.confirmations === null ||
|
|
|
|
(statusCommitment === 'recent' && status.confirmations >= 1)
|
|
|
|
) {
|
2020-04-06 17:56:26 +08:00
|
|
|
break;
|
|
|
|
}
|
2018-10-23 13:10:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (--statusRetries <= 0) {
|
2019-01-11 16:39:09 -07:00
|
|
|
break;
|
2018-10-23 13:10:08 -07:00
|
|
|
}
|
2019-03-19 12:44:55 -07:00
|
|
|
// Sleep for approximately half a slot
|
2020-04-09 15:28:02 -07:00
|
|
|
await sleep(MS_PER_SLOT / 2);
|
2018-10-23 13:10:08 -07:00
|
|
|
}
|
|
|
|
|
2020-04-04 21:35:08 +08:00
|
|
|
if (status) {
|
|
|
|
if (!status.err) {
|
|
|
|
break;
|
|
|
|
} else if (!('AccountInUse' in status.err)) {
|
|
|
|
throw new Error(
|
|
|
|
`Transaction ${signature} failed (${JSON.stringify(status)})`,
|
|
|
|
);
|
|
|
|
}
|
2018-10-23 08:35:20 -07:00
|
|
|
}
|
2020-04-04 21:35:08 +08:00
|
|
|
|
2019-01-11 16:39:09 -07:00
|
|
|
if (--sendRetries <= 0) {
|
|
|
|
const duration = (Date.now() - start) / 1000;
|
|
|
|
throw new Error(
|
|
|
|
`Transaction '${signature}' was not confirmed in ${duration.toFixed(
|
|
|
|
2,
|
2019-04-10 14:40:49 -07:00
|
|
|
)} seconds (${JSON.stringify(status)})`,
|
2019-01-11 16:39:09 -07:00
|
|
|
);
|
|
|
|
}
|
2018-10-23 08:35:20 -07:00
|
|
|
|
2019-01-11 16:39:09 -07:00
|
|
|
// Retry in 0..100ms to try to avoid another AccountInUse collision
|
2018-10-30 11:10:11 -07:00
|
|
|
await sleep(Math.random() * 100);
|
2018-10-06 10:36:59 -07:00
|
|
|
}
|
2018-11-03 19:02:12 -07:00
|
|
|
|
2018-11-28 11:56:50 -08:00
|
|
|
invariant(signature !== undefined);
|
2018-11-03 19:02:12 -07:00
|
|
|
return signature;
|
2018-10-06 10:36:59 -07:00
|
|
|
}
|