From cbb3c3f241078ba41d4991509acdfdaedf87d63f Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 7 Sep 2022 20:54:18 +0800 Subject: [PATCH] bitcoin cash sending works for legacy and new addresses bip44 --- .../coins/bitcoincash/bitcoincash_wallet.dart | 123 ++++++++++++------ pubspec.yaml | 4 + 2 files changed, 87 insertions(+), 40 deletions(-) diff --git a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart index 4174dbc8e..65464b5da 100644 --- a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart +++ b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart @@ -41,6 +41,7 @@ import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/prefs.dart'; import 'package:tuple/tuple.dart'; import 'package:uuid/uuid.dart'; +import 'package:bitbox/bitbox.dart' as Bitbox; const int MINIMUM_CONFIRMATIONS = 3; const int DUST_LIMIT = 1000000; @@ -735,6 +736,9 @@ class BitcoinCashWallet extends CoinServiceAPI { /// Refreshes display data for the wallet @override Future refresh() async { + final bchaddr = Bitbox.Address.toCashAddress(await currentReceivingAddress); + print("bchaddr: $bchaddr ${await currentReceivingAddress}"); + if (refreshMutex) { Logging.instance.log("$walletId $walletName refreshMutex denied", level: LogLevel.Info); @@ -1050,7 +1054,14 @@ class BitcoinCashWallet extends CoinServiceAPI { @override bool validateAddress(String address) { - return Address.validateAddress(address, _network); + try { + // 0 for bitcoincash: address scheme, 1 for legacy address + final format = Bitbox.Address.detectFormat(address); + print("format $format"); + return true; + } catch (e, s) { + return false; + } } @override @@ -1947,6 +1958,7 @@ class BitcoinCashWallet extends CoinServiceAPI { List> allTransactions = []; for (final txHash in allTxHashes) { + Logging.instance.log("bch: $txHash", level: LogLevel.Info); final tx = await cachedElectrumXClient.getTransaction( txHash: txHash["tx_hash"] as String, verbose: true, @@ -2318,8 +2330,8 @@ class BitcoinCashWallet extends CoinServiceAPI { vSize: vSizeForOneOutput, feeRatePerKB: selectedTxFeeRate, ); - if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) { - feeForOneOutput = (vSizeForOneOutput + 1) * 1000; + if (feeForOneOutput < (vSizeForOneOutput + 1)) { + feeForOneOutput = (vSizeForOneOutput + 1); } final int amount = satoshiAmountToSend - feeForOneOutput; @@ -2375,11 +2387,11 @@ class BitcoinCashWallet extends CoinServiceAPI { .log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); Logging.instance .log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); - if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) { - feeForOneOutput = (vSizeForOneOutput + 1) * 1000; + if (feeForOneOutput < (vSizeForOneOutput + 1)) { + feeForOneOutput = (vSizeForOneOutput + 1); } - if (feeForTwoOutputs < ((vSizeForTwoOutPuts + 1) * 1000)) { - feeForTwoOutputs = ((vSizeForTwoOutPuts + 1) * 1000); + if (feeForTwoOutputs < ((vSizeForTwoOutPuts + 1))) { + feeForTwoOutputs = ((vSizeForTwoOutPuts + 1)); } Logging.instance @@ -2679,45 +2691,76 @@ class BitcoinCashWallet extends CoinServiceAPI { required List recipients, required List satoshiAmounts, }) async { - Logging.instance - .log("Starting buildTransaction ----------", level: LogLevel.Info); + final builder = Bitbox.Bitbox.transactionBuilder(); - final txb = TransactionBuilder(network: _network); - txb.setVersion(1); + // retrieve address' utxos from the rest api + List _utxos = + []; // await Bitbox.Address.utxo(address) as List; + utxosToUse.forEach((element) { + _utxos.add(Bitbox.Utxo( + element.txid, + element.vout, + Bitbox.BitcoinCash.fromSatoshi(element.value), + element.value, + 0, + MINIMUM_CONFIRMATIONS + 1)); + }); + Logger.print("bch utxos: ${_utxos}"); - // Add transaction inputs - for (var i = 0; i < utxosToUse.length; i++) { - final txid = utxosToUse[i].txid; - txb.addInput(txid, utxosToUse[i].vout, null, - utxoSigningData[txid]["output"] as Uint8List); + // placeholder for input signatures + final signatures = []; + + // placeholder for total input balance + int totalBalance = 0; + + // iterate through the list of address _utxos and use them as inputs for the + // withdrawal transaction + _utxos.forEach((Bitbox.Utxo utxo) { + // add the utxo as an input for the transaction + builder.addInput(utxo.txid, utxo.vout); + final ec = utxoSigningData[utxo.txid]["keyPair"] as ECPair; + + final bitboxEC = Bitbox.ECPair.fromWIF(ec.toWIF()); + + // add a signature to the list to be used later + signatures.add({ + "vin": signatures.length, + "key_pair": bitboxEC, + "original_amount": utxo.satoshis + }); + + totalBalance += utxo.satoshis; + }); + + // calculate the fee based on number of inputs and one expected output + final fee = + Bitbox.BitcoinCash.getByteCount(signatures.length, recipients.length); + + // calculate how much balance will be left over to spend after the fee + final sendAmount = totalBalance - fee; + + // add the output based on the address provided in the testing data + for (int i = 0; i < recipients.length; i++) { + String recipient = recipients[i]; + int satoshiAmount = satoshiAmounts[i]; + builder.addOutput(recipient, satoshiAmount); } - // Add transaction output - for (var i = 0; i < recipients.length; i++) { - txb.addOutput(recipients[i], satoshiAmounts[i]); - } + // sign all inputs + signatures.forEach((signature) { + builder.sign( + signature["vin"] as int, + signature["key_pair"] as Bitbox.ECPair, + signature["original_amount"] as int); + }); - try { - // Sign the transaction accordingly - for (var i = 0; i < utxosToUse.length; i++) { - final txid = utxosToUse[i].txid; - txb.sign( - vin: i, - keyPair: utxoSigningData[txid]["keyPair"] as ECPair, - witnessValue: utxosToUse[i].value, - redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?, - ); - } - } catch (e, s) { - Logging.instance.log("Caught exception while signing transaction: $e\n$s", - level: LogLevel.Error); - rethrow; - } + // build the transaction + final tx = builder.build(); + final txHex = tx.toHex(); + final vSize = tx.virtualSize(); + Logger.print("bch raw hex: $txHex"); - final builtTx = txb.build(); - final vSize = builtTx.virtualSize(); - - return {"hex": builtTx.toHex(), "vSize": vSize}; + return {"hex": txHex, "vSize": vSize}; } @override diff --git a/pubspec.yaml b/pubspec.yaml index f2faf1497..dafa6189e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -69,6 +69,10 @@ dependencies: git: url: https://github.com/cypherstack/stack-bip39.git ref: 3bef5acc21340f3cc78df0ad1dce5868a3ed68a5 + bitbox: + git: + url: https://github.com/Quppy/bitbox-flutter.git + ref: ea65073efbaf395a5557e8cd7bd72f195cd7eb11 bip32: ^2.0.0 bech32: ^0.2.1 bs58check: ^1.0.2