Skip to content

Commit

Permalink
Implement wait() for Qi TX response
Browse files Browse the repository at this point in the history
  • Loading branch information
alejoacosta74 committed Oct 16, 2024
1 parent 703a25b commit 208b80a
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 81 deletions.
19 changes: 16 additions & 3 deletions src/providers/abstract-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -160,6 +160,12 @@ export type Subscription =
hash: string;
zone: Zone;
}
| {
type: 'qiTransaction';
tag: string;
hash: string;
zone: Zone;
}
| {
type: 'event';
tag: string;
Expand Down Expand Up @@ -312,9 +318,16 @@ async function getSubscription(_event: ProviderEvent, zone?: Zone): Promise<Subs
}

if (isHexString(_event, 32)) {
const eventBytes = getBytes(_event);
const ninthBit = (eventBytes[1] & 0x01) === 0x01;

const hash = _event.toLowerCase();
zone = toZone(hash.slice(0, 4));
return { type: 'transaction', tag: getTag('tx', { hash }), hash, zone };
if (ninthBit) {
return { type: 'qiTransaction', tag: getTag('Tx', { hash }), hash, zone };
} else {
return { type: 'transaction', tag: getTag('tx', { hash }), hash, zone };
}
}

if ((<any>_event).orphan) {
Expand Down Expand Up @@ -1891,7 +1904,7 @@ export class AbstractProvider<C = FetchRequest> 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);
}
Expand Down
81 changes: 15 additions & 66 deletions src/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TxInput> | undefined): Promise<Zone> => {
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();
Expand All @@ -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 (<Promise<QiTransactionResponse>>waiter);
Expand Down
24 changes: 12 additions & 12 deletions src/providers/subscriber-polling.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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', {
Expand Down Expand Up @@ -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<void> {
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);
}
}
}
Expand Down

0 comments on commit 208b80a

Please sign in to comment.