From 8b82eb1cfdf0d8eba0ebe622be0989b0a8461dc2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 1 Mar 2026 14:22:30 -0600 Subject: [PATCH 1/6] fix: checkBlockUTXO only checking the specific UTXO output --- lib/wallets/wallet/impl/particl_wallet.dart | 50 +++++++++++---------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/lib/wallets/wallet/impl/particl_wallet.dart b/lib/wallets/wallet/impl/particl_wallet.dart index 6f2b9764e..6310bda55 100644 --- a/lib/wallets/wallet/impl/particl_wallet.dart +++ b/lib/wallets/wallet/impl/particl_wallet.dart @@ -73,34 +73,36 @@ class ParticlWallet String? blockedReason; String? utxoLabel; + // Only check the specific output this UTXO corresponds to, not all outputs. + final vout = jsonUTXO["tx_pos"] as int; final outputs = jsonTX["vout"] as List? ?? []; - for (final output in outputs) { - if (output is Map) { - if (output['ct_fee'] != null) { - // Blind output, ignore for now. - blocked = true; - blockedReason = "Blind output."; - utxoLabel = "Unsupported output type."; - } else if (output['rangeproof'] != null) { - // Private RingCT output, ignore for now. - blocked = true; - blockedReason = "Confidential output."; - utxoLabel = "Unsupported output type."; - } else if (output['data_hex'] != null) { - // Data output, ignore for now. + final output = outputs.cast?>().firstWhere( + (e) => e?["n"] == vout, + orElse: () => null, + ); + + if (output != null) { + if (output['ct_fee'] != null) { + blocked = true; + blockedReason = "Blind output."; + utxoLabel = "Unsupported output type."; + } else if (output['rangeproof'] != null) { + blocked = true; + blockedReason = "Confidential output."; + utxoLabel = "Unsupported output type."; + } else if (output['data_hex'] != null) { + blocked = true; + blockedReason = "Data output."; + utxoLabel = "Unsupported output type."; + } else if (output['scriptPubKey'] != null) { + if (output['scriptPubKey']?['asm'] is String && + (output['scriptPubKey']['asm'] as String).contains( + "OP_ISCOINSTAKE", + )) { blocked = true; - blockedReason = "Data output."; + blockedReason = "Spending staking"; utxoLabel = "Unsupported output type."; - } else if (output['scriptPubKey'] != null) { - if (output['scriptPubKey']?['asm'] is String && - (output['scriptPubKey']['asm'] as String).contains( - "OP_ISCOINSTAKE", - )) { - blocked = true; - blockedReason = "Spending staking"; - utxoLabel = "Unsupported output type."; - } } } } From 42e8cec00c0ee6b004f6f3e95709b5f218ef79a5 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 1 Mar 2026 14:22:50 -0600 Subject: [PATCH 2/6] fix: witness field parsing in particl updateTransactions --- lib/wallets/wallet/impl/particl_wallet.dart | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/wallets/wallet/impl/particl_wallet.dart b/lib/wallets/wallet/impl/particl_wallet.dart index 6310bda55..d1ed90c5d 100644 --- a/lib/wallets/wallet/impl/particl_wallet.dart +++ b/lib/wallets/wallet/impl/particl_wallet.dart @@ -239,17 +239,12 @@ class ParticlWallet addresses.addAll(prevOut.addresses); } - InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( - scriptSigHex: map["scriptSig"]?["hex"] as String?, - scriptSigAsm: map["scriptSig"]?["asm"] as String?, - sequence: map["sequence"] as int?, + InputV2 input = InputV2.fromElectrumxJson( + json: map, outpoint: outpoint, - valueStringSats: valueStringSats, addresses: addresses, - witness: map["witness"] as String?, + valueStringSats: valueStringSats, coinbase: coinbase, - innerRedeemScriptAsm: map["innerRedeemscriptAsm"] as String?, - // Need addresses before we can know if the wallet owns this input. walletOwns: false, ); From 7cb913ccd4fe4de36eb3d28dbd4b8a160e0e5b70 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 1 Mar 2026 14:23:26 -0600 Subject: [PATCH 3/6] fix: particl transactionVersion property to return 160 --- lib/wallets/crypto_currency/coins/particl.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wallets/crypto_currency/coins/particl.dart b/lib/wallets/crypto_currency/coins/particl.dart index 2b07aad7e..a631e94e4 100644 --- a/lib/wallets/crypto_currency/coins/particl.dart +++ b/lib/wallets/crypto_currency/coins/particl.dart @@ -233,7 +233,7 @@ class Particl extends Bip39HDCurrency with ElectrumXCurrencyInterface { } @override - int get transactionVersion => 1; + int get transactionVersion => 160; @override BigInt get defaultFeeRate => BigInt.from(20000); From 933d26869de25174c585a8e3bac5cefd315a3725 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 1 Mar 2026 14:24:31 -0600 Subject: [PATCH 4/6] fix: temp input script index in particl buildTransaction --- lib/wallets/wallet/impl/particl_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wallets/wallet/impl/particl_wallet.dart b/lib/wallets/wallet/impl/particl_wallet.dart index d1ed90c5d..2d16f0b7c 100644 --- a/lib/wallets/wallet/impl/particl_wallet.dart +++ b/lib/wallets/wallet/impl/particl_wallet.dart @@ -451,7 +451,7 @@ class ParticlWallet tempInputs.add( InputV2.isarCantDoRequiredInDefaultConstructor( - scriptSigHex: txb.inputs.first.script?.toHex, + scriptSigHex: txb.inputs[i].script?.toHex, scriptSigAsm: null, sequence: 0xffffffff - 1, outpoint: OutpointV2.isarCantDoRequiredInDefaultConstructor( From b3d5017b219c98484ef0f4c60b4e4e05135607a9 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 3 Mar 2026 15:53:27 -0600 Subject: [PATCH 5/6] fix: Particl wallet P2PKH signing and checkBlockUTXO cast --- lib/wallets/wallet/impl/particl_wallet.dart | 37 ++++++--------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/lib/wallets/wallet/impl/particl_wallet.dart b/lib/wallets/wallet/impl/particl_wallet.dart index 2d16f0b7c..65bc9c4c7 100644 --- a/lib/wallets/wallet/impl/particl_wallet.dart +++ b/lib/wallets/wallet/impl/particl_wallet.dart @@ -77,10 +77,14 @@ class ParticlWallet final vout = jsonUTXO["tx_pos"] as int; final outputs = jsonTX["vout"] as List? ?? []; - final output = outputs.cast?>().firstWhere( - (e) => e?["n"] == vout, - orElse: () => null, - ); + // Use Map? because ElectrumX returns _Map. + Map? output; + for (final o in outputs) { + if (o is Map && o["n"] == vout) { + output = o; + break; + } + } if (output != null) { if (output['ct_fee'] != null) { @@ -508,6 +512,7 @@ class ParticlWallet ), witnessValue: insAndKeys[i].utxo.value, redeemScript: extraData[i].redeem, + isParticl: true, overridePrefix: cryptoCurrency.networkParams.bech32Hrp, ); } @@ -523,30 +528,8 @@ class ParticlWallet final builtTx = txb.build(cryptoCurrency.networkParams.bech32Hrp); final vSize = builtTx.virtualSize(); - // Strip trailing 0x00 bytes from hex. - // - // This is done to match the previous particl_wallet implementation. - // TODO: [prio=low] Rework Particl tx construction so as to obviate this. - String hexString = builtTx.toHex(isParticl: true).toString(); - if (hexString.length % 2 != 0) { - // Ensure the string has an even length. - Logging.instance.e( - "Hex string has odd length, which is unexpected.", - stackTrace: StackTrace.current, - ); - throw Exception("Invalid hex string length."); - } - // int maxStrips = 3; // Strip up to 3 0x00s (match previous particl_wallet). - while (hexString.endsWith('00') && hexString.length > 2) { - hexString = hexString.substring(0, hexString.length - 2); - // maxStrips--; - // if (maxStrips <= 0) { - // break; - // } - } - return txData.copyWith( - raw: hexString, + raw: builtTx.toHex(isParticl: true), vSize: vSize, tempTx: null, // builtTx.getId() requires an isParticl flag as well but the lib does not support that yet From ea7c0d556828a75254791eaf7a466ed429a147cb Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 3 Mar 2026 16:00:08 -0600 Subject: [PATCH 6/6] feat: update cypherstack/bitcoindart to point to fix/particl off master had been testing this locally TODO: merge https://github.com/cypherstack/bitcoindart/pull/9 and update it again --- scripts/app_config/templates/pubspec.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml index 6b551d4c4..bd3442da3 100644 --- a/scripts/app_config/templates/pubspec.template.yaml +++ b/scripts/app_config/templates/pubspec.template.yaml @@ -95,7 +95,7 @@ dependencies: bitcoindart: git: url: https://github.com/cypherstack/bitcoindart.git - ref: 7145be16bb88cffbd53326f7fa4570e414be09e4 + ref: b02aaf6c6b40fd5a6b3d77f875324717103f2019 stack_wallet_backup: git: