From 208b80a3a857f0007978a105ca1741b120c9fa18 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Tue, 15 Oct 2024 18:50:58 -0300 Subject: [PATCH] Implement wait() for Qi TX response --- src/providers/abstract-provider.ts | 19 +++++-- src/providers/provider.ts | 81 ++++++----------------------- src/providers/subscriber-polling.ts | 24 ++++----- 3 files changed, 43 insertions(+), 81 deletions(-) diff --git a/src/providers/abstract-provider.ts b/src/providers/abstract-provider.ts index d33f573a..da94e3c9 100644 --- a/src/providers/abstract-provider.ts +++ b/src/providers/abstract-provider.ts @@ -77,7 +77,7 @@ import { PollingEventSubscriber, PollingOrphanSubscriber, PollingTransactionSubscriber, - QiPollingTransactionSubscriber, + PollingQiTransactionSubscriber, } from './subscriber-polling.js'; import { getNodeLocationFromZone, getZoneFromNodeLocation } from '../utils/shards.js'; import { fromShard } from '../constants/shards.js'; @@ -160,6 +160,12 @@ export type Subscription = hash: string; zone: Zone; } + | { + type: 'qiTransaction'; + tag: string; + hash: string; + zone: Zone; + } | { type: 'event'; tag: string; @@ -312,9 +318,16 @@ async function getSubscription(_event: ProviderEvent, zone?: Zone): Promise_event).orphan) { @@ -1891,7 +1904,7 @@ export class AbstractProvider implements Provider { case 'transaction': return new PollingTransactionSubscriber(this as AbstractProvider, sub.hash, sub.zone); case 'qiTransaction': - return new QiPollingTransactionSubscriber(this as AbstractProvider, sub.hash, sub.zone); + return new PollingQiTransactionSubscriber(this as AbstractProvider, sub.hash, sub.zone); case 'orphan': return new PollingOrphanSubscriber(this as AbstractProvider, sub.filter, sub.zone); } diff --git a/src/providers/provider.ts b/src/providers/provider.ts index 6cfe21e7..5e1b3450 100644 --- a/src/providers/provider.ts +++ b/src/providers/provider.ts @@ -2416,54 +2416,20 @@ export class QiTransactionResponse implements QiTransactionLike, QiTransactionRe const confirms = _confirms == null ? 1 : _confirms; const timeout = _timeout == null ? 0 : _timeout; - const startBlock = this.startBlock; - let stopScanning = startBlock === -1 ? true : false; - - const zoneFromInput = async (txInputs: Array | undefined): Promise => { - if (!txInputs || txInputs.length === 0) { - throw new Error('No transaction inputs provided'); - } - - const firstInput = txInputs[0]; - if (!firstInput.pubkey) { - throw new Error('Public key not found in the first transaction input'); - } - const address = computeAddress(firstInput.pubkey); - const zone = getZoneForAddress(address); - if (!zone) { - throw new Error(`Invalid zone for address: ${address}`); - } - return zone; - }; - - const zone = await zoneFromInput(this.txInputs); - - const response = await this.provider.getTransaction(this.hash, zone); - if (response && response.isMined() && confirms === 0) { - return response as QiTransactionResponse; - } + const tx = await this.provider.getTransaction(this.hash); - if (response) { - if ((await response.confirmations()) >= confirms) { - return response as QiTransactionResponse; - } - } else { - // Allow null only when the confirms is 0 - if (confirms === 0) { - return null; - } + if (confirms === 0 && tx) { + return tx as QiTransactionResponse; } const waiter = new Promise((resolve, reject) => { + // List of things to cancel when we have a result (one way or the other) const cancellers: Array<() => void> = []; const cancel = () => { cancellers.forEach((c) => c()); }; - cancellers.push(() => { - stopScanning = true; - }); - + // Set up any timeout requested if (timeout > 0) { const timer = setTimeout(() => { cancel(); @@ -2474,38 +2440,21 @@ export class QiTransactionResponse implements QiTransactionLike, QiTransactionRe }); } - const txListener = async (response: QiTransactionResponse) => { - if ((await response.confirmations()) >= confirms) { + const txListener = async (tx: QiTransactionResponse) => { + // Done; return it! + if ((await tx.confirmations()) >= confirms) { cancel(); - resolve(response); + try { + resolve(tx); + } catch (error) { + reject(error); + } } }; - cancellers.push(() => { - this.provider.off('qiTransaction', txListener, zone, this.hash); + this.provider.off(this.hash, txListener); }); - this.provider.on('qiTransaction', txListener, zone, this.hash); - - if (startBlock >= 0) { - const blockListener = async () => { - const currentBlock = await this.provider.getBlockNumber(toShard(zone)); - if (currentBlock - startBlock >= confirms) { - const response = await this.provider.getTransaction(this.hash, zone); - if (response && response.isMined()) { - cancel(); - resolve(response); - } - } - - if (!stopScanning) { - this.provider.once('block', blockListener, zone); - } - }; - cancellers.push(() => { - this.provider.off('block', blockListener, zone); - }); - this.provider.once('block', blockListener, zone); - } + this.provider.on(this.hash, txListener); }); return await (>waiter); diff --git a/src/providers/subscriber-polling.ts b/src/providers/subscriber-polling.ts index e97562f4..64870a14 100644 --- a/src/providers/subscriber-polling.ts +++ b/src/providers/subscriber-polling.ts @@ -1,6 +1,6 @@ import { toZone, Zone } from '../constants/index.js'; import { toShard } from '../constants/shards.js'; -import { assert, isHexString } from '../utils/index.js'; +import { assert, getBytes, isHexString } from '../utils/index.js'; import { getZoneFromNodeLocation } from '../utils/shards.js'; import { getZoneFromEventFilter, type EventFilter, type OrphanFilter, type ProviderEvent } from './provider.js'; @@ -31,12 +31,14 @@ export function getPollingSubscriber(provider: AbstractProvider, event: Provider } if (isHexString(event, 32)) { - return new PollingTransactionSubscriber(provider, event, zone); - } + const eventBytes = getBytes(event); + const ninthBit = (eventBytes[1] & 0x01) === 0x01; - if (event === 'qiTransaction') { - assert(hash != null, "hash is required for 'qiTransaction' event", 'MISSING_ARGUMENT'); - return new QiPollingTransactionSubscriber(provider, hash, zone); + if (ninthBit) { + return new PollingQiTransactionSubscriber(provider, event, zone); + } else { + return new PollingTransactionSubscriber(provider, event, zone); + } } assert(false, 'unsupported polling event', 'UNSUPPORTED_OPERATION', { @@ -326,20 +328,18 @@ export class PollingTransactionSubscriber extends OnBlockSubscriber { } } -export class QiPollingTransactionSubscriber extends OnBlockSubscriber { +export class PollingQiTransactionSubscriber extends OnBlockSubscriber { #hash: string; - #zone: Zone; constructor(provider: AbstractProvider, hash: string, zone: Zone) { super(provider, zone); this.#hash = hash; - this.#zone = zone; } async _poll(blockNumber: number, provider: AbstractProvider): Promise { - const tx = await provider.getTransaction(this.#hash, this.#zone); - if (tx) { - provider.emit(this.#hash, this.#zone, tx); + const tx = await provider.getTransaction(this.#hash); + if (tx && tx.isMined()) { + provider.emit(this.#hash, toZone(this.#hash.slice(0, 4)), tx); } } }