diff --git a/fusiondart b/fusiondart index e260d0151..527a80ff1 160000 --- a/fusiondart +++ b/fusiondart @@ -1 +1 @@ -Subproject commit e260d01513dc8bce1ba3e3fc67a21fa94b4e5854 +Subproject commit 527a80ff1ff65f6e0e7450867dbda2cf642c12b6 diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 9465de12b..796914117 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -241,6 +241,14 @@ class MainDB { QueryBuilder getUTXOs(String walletId) => isar.utxos.where().walletIdEqualTo(walletId); + QueryBuilder getUTXOsByAddress( + String walletId, String address) => + isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .addressEqualTo(address); + Future putUTXO(UTXO utxo) => isar.writeTxn(() async { await isar.utxos.put(utxo); }); diff --git a/lib/models/isar/models/blockchain_data/transaction.dart b/lib/models/isar/models/blockchain_data/transaction.dart index 5b32142c2..c52f584fc 100644 --- a/lib/models/isar/models/blockchain_data/transaction.dart +++ b/lib/models/isar/models/blockchain_data/transaction.dart @@ -11,6 +11,10 @@ import 'dart:convert'; import 'dart:math'; +import 'package:fusiondart/src/models/address.dart' as fusion_address; +import 'package:fusiondart/src/models/input.dart' as fusion_input; +import 'package:fusiondart/src/models/output.dart' as fusion_output; +import 'package:fusiondart/src/models/transaction.dart' as fusion_tx; import 'package:isar/isar.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/input.dart'; @@ -22,7 +26,6 @@ part 'transaction.g.dart'; @Collection() class Transaction { - Transaction({ required this.walletId, required this.txid, @@ -232,6 +235,37 @@ class Transaction { return Tuple2(transaction, address); } } + + // Convert to fusiondart's Transaction type. + fusion_tx.Transaction toFusionTransaction() { + // Initialize Fusion Dart's Transaction object. + fusion_tx.Transaction fusionTransaction = fusion_tx.Transaction(); + + // Map the Inputs and Outputs to Fusion Dart's format + fusionTransaction.Inputs = inputs + .map((e) => fusion_input.Input( + prevTxid: utf8.encode(e.txid), + prevIndex: e.vout, + pubKey: utf8.encode(e.witness), // TODO fix + amount: 0, // TODO fix + )) + .toList(); + + fusionTransaction.Outputs = outputs + .map((e) => fusion_output.Output( + addr: fusion_address.Address( + addr: e.scriptPubKeyAddress, + publicKey: utf8.encode(e.scriptPubKey), + derivationPath: fusion_address.DerivationPath(), // TODO fix + ), + value: e.value, + )) + .toList(); + + // If any other information needs to be altered/added, do so here. + + return fusionTransaction; + } } // Used in Isar db and stored there as int indexes so adding/removing values diff --git a/lib/models/isar/models/blockchain_data/utxo.dart b/lib/models/isar/models/blockchain_data/utxo.dart index 3a87957f0..4c5f5718f 100644 --- a/lib/models/isar/models/blockchain_data/utxo.dart +++ b/lib/models/isar/models/blockchain_data/utxo.dart @@ -8,8 +8,10 @@ * */ +import 'dart:convert'; import 'dart:math'; +import 'package:fusiondart/src/models/input.dart' as FusionInput; import 'package:isar/isar.dart'; part 'utxo.g.dart'; @@ -145,3 +147,14 @@ class UTXO { @ignore int get hashCode => Object.hashAll([walletId, txid, vout]); } + +extension ToFusionInput on UTXO { + FusionInput.Input toFusionInput({required List pubKey}) { + return FusionInput.Input( + prevTxid: utf8.encode(txid), + prevIndex: vout, + pubKey: pubKey, + amount: value, + ); + } +} diff --git a/lib/services/mixins/fusion_wallet_interface.dart b/lib/services/mixins/fusion_wallet_interface.dart index 0bb648b82..265ea8798 100644 --- a/lib/services/mixins/fusion_wallet_interface.dart +++ b/lib/services/mixins/fusion_wallet_interface.dart @@ -1,7 +1,10 @@ +import 'dart:convert'; import 'dart:io'; import 'package:fusiondart/fusiondart.dart'; -import 'package:fusiondart/src/models/address.dart' as cash_fusion; +import 'package:fusiondart/src/models/address.dart' as fusion_address; +import 'package:fusiondart/src/models/input.dart' as fusion_input; +import 'package:fusiondart/src/models/transaction.dart' as fusion_tx; import 'package:isar/isar.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; @@ -39,7 +42,30 @@ mixin FusionWalletInterface { _generateAddressForChain = generateAddressForChain; } - Future createNewReservedChangeAddress() async { + Future> getFusionAddresses() async { + List
_addresses = await _db.getAddresses(_walletId).findAll(); + return _addresses.map((address) => address.toFusionAddress()).toList(); + } + + Future> getTransactionsByAddress( + String address) async { + var _txs = await _db.getTransactions(_walletId).findAll(); + + return _txs + .map((tx) => tx.toFusionTransaction()) + .toSet(); // TODO feed in proper public key + } + + Future> getInputsByAddress(String address) async { + var _utxos = await _db.getUTXOsByAddress(_walletId, address).findAll(); + + return _utxos + .map((utxo) => utxo.toFusionInput( + pubKey: utf8.encode('0000'))) // TODO feed in proper public key + .toList(); + } + + Future createNewReservedChangeAddress() async { int? highestChangeIndex = await _db .getAddresses(_walletId) .filter() @@ -57,14 +83,20 @@ mixin FusionWalletInterface { ); address = address.copyWith(otherData: kReservedFusionAddress); - // TODO if we really want to be sure its not used, call electrumx and check it + // TODO if we really want to be sure it's not used, call electrumx and check it - await _db.putAddress(address); + Address? _address = await _db.getAddress(_walletId, address.value); + if (_address != null) { + // throw Exception("Address already exists"); + await _db.updateAddress(_address, address); + } else { + await _db.putAddress(address); + } return address.toFusionAddress(); } - Future> getUnusedReservedChangeAddresses( + Future> getUnusedReservedChangeAddresses( int numberOfAddresses, ) async { final txns = await _db @@ -84,7 +116,7 @@ mixin FusionWalletInterface { .otherDataEqualTo(kReservedFusionAddress) .findAll(); - final List unusedAddresses = []; + final List unusedAddresses = []; for (final address in addresses) { if (!usedAddresses.contains(address.value)) { @@ -104,17 +136,30 @@ mixin FusionWalletInterface { void fuse() async { // Initial attempt for CashFusion integration goes here. Fusion mainFusionObject = Fusion( - createNewReservedChangeAddress: () => createNewReservedChangeAddress(), + getAddresses: () => getFusionAddresses(), + getTransactionsByAddress: (String address) => + getTransactionsByAddress(address), + getInputsByAddress: (String address) => getInputsByAddress(address), + // createNewReservedChangeAddress: () => createNewReservedChangeAddress(), getUnusedReservedChangeAddresses: (int numberOfAddresses) => getUnusedReservedChangeAddresses(numberOfAddresses), ); - // add stack utxos + // Pass wallet functions to the Fusion object + mainFusionObject.initFusion( + getAddresses: getFusionAddresses, + getTransactionsByAddress: getTransactionsByAddress, + getInputsByAddress: getInputsByAddress, + /*createNewReservedChangeAddress: createNewReservedChangeAddress,*/ + getUnusedReservedChangeAddresses: getUnusedReservedChangeAddresses); + + // Add stack UTXOs. List utxos = await _db.getUTXOs(_walletId).findAll(); + await mainFusionObject.addCoinsFromWallet( utxos.map((e) => (e.txid, e.vout, e.value)).toList()); - // fuse utxos + // Fuse UTXOs. await mainFusionObject.fuse(); //print ("DEBUG FUSION bitcoincash_wallet.dart 1202");