From c7a13d60cd9d3e1261e7da0bd250933b00d6f001 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 5 Nov 2025 15:55:53 -0800 Subject: [PATCH 1/2] Handle private inputs for FIRO SparkSpend transactions This handles only the receiving inputs side of a transaction. It does not fully support tracking the wallet's own SparkSpend inputs which it cannot even make currently because FIRO is transparent-only for now. --- CHANGELOG.md | 2 ++ .../utxobased/engine/UtxoEngineProcessor.ts | 33 +++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5f4483d..5f675975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- fixed: Fix failed transaction processing for FIRO transactions with SparkSpend inputs. + ## 3.8.7 (2025-11-05) - fixed: Added retry delay to fix dropped transaction thrashing. diff --git a/src/common/utxobased/engine/UtxoEngineProcessor.ts b/src/common/utxobased/engine/UtxoEngineProcessor.ts index 1c63f603..5eb2a09b 100644 --- a/src/common/utxobased/engine/UtxoEngineProcessor.ts +++ b/src/common/utxobased/engine/UtxoEngineProcessor.ts @@ -1361,23 +1361,36 @@ const processTransactionResponse = ( } } - if (vin.txid == null) { - throw new Error(`Unexpected null txid when processing transaction inputs`) - } - - const scriptPubkey = + let scriptPubkey: string = 'unknown' + try { // Note: Blockbook has empirically not sent a hex value as the // scriptPubkey for vins. If we discover this to be changed for some // cases, we may want to use the `hex` field as an optimization. - asMaybe( - raw => validScriptPubkeyFromAddress(raw), - 'unknown' - )({ + scriptPubkey = validScriptPubkeyFromAddress({ address: vin.addresses[0], coin: common.pluginInfo.coinInfo.name }) + } catch (error) { + common.log.warn( + `failed to get scriptPubkey from address ${vin.addresses[0]}: `, + error + ) + } + + // Some inputs may not have a txid in addition to coinbase inputs, such as + // private transaction inputs from privacy chains (e.g. SparkSpend inputs in FIRO). + // Warning: Because the engine doesn't support private transactions, or any + // transaction outputs that wouldn't have a txid as their input in the spend transaction, + // we can safely assume an all-zero txid for these inputs. + // If we're to ever support private transaction spends, then this wouldn't + // be safe to assume as "our own input" because it wouldn't remove the UTXO + // from the DataLayer properly. + const txId = + vin.txid ?? + '0000000000000000000000000000000000000000000000000000000000000000' + return { - txId: vin.txid, + txId, outputIndex: vin.vout, n: vin.n, scriptPubkey, From 8e348af9c3d75ef0ee3af8dcfb9f218d2f822eea Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 5 Nov 2025 15:56:21 -0800 Subject: [PATCH 2/2] Be clearer about the coinbase tx outputIndex --- src/common/utxobased/engine/UtxoEngineProcessor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/utxobased/engine/UtxoEngineProcessor.ts b/src/common/utxobased/engine/UtxoEngineProcessor.ts index 5eb2a09b..6f6b16fa 100644 --- a/src/common/utxobased/engine/UtxoEngineProcessor.ts +++ b/src/common/utxobased/engine/UtxoEngineProcessor.ts @@ -1354,7 +1354,8 @@ const processTransactionResponse = ( return { amount: outputTotalValue, n: 0, - outputIndex: 4294967295, + // UINT32_MAX value, as a coinbase has no previous outpoint + outputIndex: 0xffffffff, scriptPubkey: '', // Maybe there's a bogus scriptPubkey we can use here? sequence: vin.sequence, txId: '0000000000000000000000000000000000000000000000000000000000000000'