UNPKG

@bsv/overlay-express

Version:
88 lines 2.96 kB
const TERMINAL_CODES = new Set([ '400', '460', '463', '464', 'DOUBLE_SPEND_ATTEMPTED', 'REJECTED', 'INVALID', 'MALFORMED', 'MINED_IN_STALE_BLOCK' ]); function isTerminalBroadcastFailure(failure) { const code = failure.code.toUpperCase(); const description = failure.description.toUpperCase(); if (TERMINAL_CODES.has(code)) return true; if (code.includes('ORPHAN') || description.includes('ORPHAN')) return true; if (typeof failure.more === 'object' && failure.more !== null && 'terminal' in failure.more && failure.more.terminal === true) { return true; } return false; } /** * Tries transaction propagation providers in priority order. * * Transient provider failures fall through to the next provider. Terminal * validation states, including double spends, stop immediately so a conflicting * transaction is not retried against a second propagation surface. */ export class ProviderChainBroadcaster { providers; constructor(providers) { this.providers = providers; if (providers.length === 0) { throw new TypeError('ProviderChainBroadcaster requires at least one provider'); } } async broadcast(tx) { const failures = []; for (const provider of this.providers) { let response; try { response = await provider.broadcaster.broadcast(tx); } catch (error) { response = { status: 'error', code: '500', description: error instanceof Error ? error.message : 'Internal Server Error', more: { provider: provider.name, terminal: false } }; } if (response.status === 'success') { return { ...response, message: `[${provider.name}] ${response.message}` }; } failures.push({ provider: provider.name, failure: response }); if (isTerminalBroadcastFailure(response)) { return annotateFailure(response, provider.name, failures); } } const last = failures[failures.length - 1]; return annotateFailure(last.failure, last.provider, failures); } } function annotateFailure(failure, provider, failures) { return { ...failure, more: { ...(typeof failure.more === 'object' && failure.more !== null ? failure.more : {}), provider, providerFailures: failures.map(item => ({ provider: item.provider, code: item.failure.code, description: item.failure.description, txid: item.failure.txid, more: item.failure.more })) } }; } //# sourceMappingURL=ProviderChainBroadcaster.js.map