mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-24 20:39:21 +00:00
some FusionTransaction extension TODO cleanup
This commit is contained in:
parent
d1603d5f72
commit
af40bf3667
1 changed files with 79 additions and 63 deletions
|
@ -2,7 +2,6 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:convert/convert.dart';
|
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:fusiondart/fusiondart.dart';
|
import 'package:fusiondart/fusiondart.dart';
|
||||||
import 'package:fusiondart/src/models/address.dart' as fusion_address;
|
import 'package:fusiondart/src/models/address.dart' as fusion_address;
|
||||||
|
@ -17,6 +16,7 @@ import 'package:stackwallet/services/tor_service.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/extensions/impl/string.dart';
|
||||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||||
|
|
||||||
const String kReservedFusionAddress = "reserved_fusion_address";
|
const String kReservedFusionAddress = "reserved_fusion_address";
|
||||||
|
@ -92,7 +92,13 @@ mixin FusionWalletInterface {
|
||||||
|
|
||||||
// Use Future.wait to await all the futures in the set and then convert it to a set.
|
// Use Future.wait to await all the futures in the set and then convert it to a set.
|
||||||
final resultSet = await Future.wait(
|
final resultSet = await Future.wait(
|
||||||
_txs.map((tx) => tx.toFusionTransaction(_cachedElectrumX)));
|
_txs.map(
|
||||||
|
(tx) => tx.toFusionTransaction(
|
||||||
|
dbInstance: _db,
|
||||||
|
cachedElectrumX: _cachedElectrumX,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return resultSet;
|
return resultSet;
|
||||||
}
|
}
|
||||||
|
@ -499,100 +505,110 @@ extension FusionUTXO on UTXO {
|
||||||
|
|
||||||
/// An extension of Stack Wallet's Transaction class that adds CashFusion functionality.
|
/// An extension of Stack Wallet's Transaction class that adds CashFusion functionality.
|
||||||
extension FusionTransaction on Transaction {
|
extension FusionTransaction on Transaction {
|
||||||
|
/// Fetch the public key of an address stored in the database.
|
||||||
|
Future<String?> _getAddressDerivationPathString({
|
||||||
|
required String address,
|
||||||
|
required MainDB dbInstance,
|
||||||
|
}) async {
|
||||||
|
final Address? addr = await dbInstance.getAddress(walletId, address);
|
||||||
|
|
||||||
|
return addr?.derivationPath?.value;
|
||||||
|
}
|
||||||
|
|
||||||
// WIP.
|
// WIP.
|
||||||
Future<fusion_tx.Transaction> toFusionTransaction(
|
Future<fusion_tx.Transaction> toFusionTransaction({
|
||||||
CachedElectrumX cachedElectrumX) async {
|
required CachedElectrumX cachedElectrumX,
|
||||||
|
required MainDB dbInstance,
|
||||||
|
}) async {
|
||||||
// Initialize Fusion Dart's Transaction object.
|
// Initialize Fusion Dart's Transaction object.
|
||||||
fusion_tx.Transaction fusionTransaction = fusion_tx.Transaction();
|
fusion_tx.Transaction fusionTransaction = fusion_tx.Transaction();
|
||||||
|
|
||||||
// WIP.
|
// WIP.
|
||||||
fusionTransaction.Inputs = await Future.wait(inputs.map((e) async {
|
fusionTransaction.Inputs = await Future.wait(inputs.map((input) async {
|
||||||
// Find input amount.
|
// Find input amount.
|
||||||
Map<String, dynamic> _tx = await cachedElectrumX.getTransaction(
|
Map<String, dynamic> _tx = await cachedElectrumX.getTransaction(
|
||||||
coin: Coin.bitcoincash,
|
coin: Coin.bitcoincash,
|
||||||
txHash: e.txid,
|
txHash: input.txid,
|
||||||
verbose: true); // TODO is verbose needed?
|
verbose: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_tx.isEmpty) {
|
||||||
|
throw Exception("Transaction not found for input: ${input.txid}");
|
||||||
|
}
|
||||||
|
|
||||||
// Check if output amount is available.
|
// Check if output amount is available.
|
||||||
if (_tx.isEmpty) {
|
final txVoutAmount = Decimal.tryParse(
|
||||||
throw Exception("Transaction not found for input: ${e.txid}");
|
_tx["vout"]?[input.vout]?["value"].toString() ?? "",
|
||||||
}
|
);
|
||||||
if (_tx["vout"] == null) {
|
if (txVoutAmount == null) {
|
||||||
throw Exception("Vout in transaction ${e.txid} is null");
|
|
||||||
}
|
|
||||||
if (_tx["vout"][e.vout] == null) {
|
|
||||||
throw Exception("Vout index ${e.vout} in transaction is null");
|
|
||||||
}
|
|
||||||
if (_tx["vout"][e.vout]["value"] == null) {
|
|
||||||
throw Exception("Value of vout index ${e.vout} in transaction is null");
|
|
||||||
}
|
|
||||||
if (_tx["vout"][e.vout]["scriptPubKey"] == null) {
|
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"scriptPubKey of vout index ${e.vout} in transaction is null");
|
"Output value at index ${input.vout} in transaction ${input.txid} not found",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final scriptPubKeyHex =
|
||||||
|
_tx["vout"]?[input.vout]?["scriptPubKey"] as String?;
|
||||||
|
if (scriptPubKeyHex == null) {
|
||||||
|
throw Exception(
|
||||||
|
"scriptPubKey of vout index ${input.vout} in transaction is null",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// TODO replace with conditional chaining?
|
|
||||||
|
|
||||||
// Assign vout value to amount.
|
// Assign vout value to amount.
|
||||||
final value = Amount.fromDecimal(
|
final value = Amount.fromDecimal(
|
||||||
Decimal.parse(_tx["vout"][e.vout]["value"].toString()),
|
txVoutAmount,
|
||||||
fractionDigits: Coin.bitcoincash.decimals,
|
fractionDigits: Coin.bitcoincash.decimals,
|
||||||
);
|
);
|
||||||
|
|
||||||
return fusion_input.Input(
|
return fusion_input.Input(
|
||||||
prevTxid: utf8.encode(e.txid),
|
prevTxid: utf8.encode(input.txid),
|
||||||
prevIndex: e.vout,
|
prevIndex: input.vout,
|
||||||
pubKey: hex.decode("${_tx["vout"][e.vout]["scriptPubKey"]}"),
|
pubKey: scriptPubKeyHex.toUint8ListFromHex,
|
||||||
amount: value.raw.toInt(),
|
amount: value.raw.toInt(),
|
||||||
);
|
);
|
||||||
}).toList());
|
}).toList());
|
||||||
|
|
||||||
fusionTransaction.Outputs = outputs.map((e) {
|
fusionTransaction.Outputs = await Future.wait(outputs.map((output) async {
|
||||||
/*
|
// TODO: maybe only need one of these but IIRC scriptPubKeyAddress is required for bitcoincash transactions?
|
||||||
if (e.scriptPubKey == null) {
|
if (output.scriptPubKeyAddress.isEmpty) {
|
||||||
// TODO calculate scriptPubKey if it is null.
|
throw Exception("isar model output.scriptPubKeyAddress is empty!");
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fusion_address.DerivationPath? derivationPath;
|
|
||||||
List<int>? pubKey;
|
|
||||||
|
|
||||||
// Validate that we have all the required data.
|
|
||||||
if (address.value == null) {
|
|
||||||
// TODO calculate address if it is null.
|
|
||||||
throw Exception(
|
|
||||||
"address value is null for input: ${e.scriptPubKeyAddress}");
|
|
||||||
} else {
|
|
||||||
if (address.value!.publicKey.isEmpty || e.scriptPubKey != null) {
|
|
||||||
pubKey = utf8.encode(e.scriptPubKey!);
|
|
||||||
// TODO is this valid?
|
|
||||||
} else {
|
|
||||||
pubKey = address.value!
|
|
||||||
.publicKey; // TODO IMPORTANT: this address may not be *the* address in question :)
|
|
||||||
}
|
|
||||||
if (address.value!.derivationPath != null) {
|
|
||||||
derivationPath = fusion_address.DerivationPath(address
|
|
||||||
.value!.derivationPath!
|
|
||||||
.toString()); // TODO IMPORTANT: this address may not be *the* address in question :)
|
|
||||||
} else {
|
|
||||||
// TODO calculate derivation path if it is null.
|
|
||||||
/*
|
|
||||||
throw Exception(
|
|
||||||
"derivationPath is null for input: ${e.scriptPubKeyAddress}");
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
if (output.scriptPubKey == null || output.scriptPubKey!.isEmpty) {
|
||||||
|
throw Exception("isar model output.scriptPubKey is null or empty!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle case where address.value.publicKey is empty and e.scriptPubKey is null
|
final outputAddress = output.scriptPubKeyAddress;
|
||||||
|
final outputAddressScriptPubKey = output.scriptPubKey!.toUint8ListFromHex;
|
||||||
|
|
||||||
|
// fetch address derivation path
|
||||||
|
final derivationPathString = await _getAddressDerivationPathString(
|
||||||
|
address: outputAddress,
|
||||||
|
dbInstance: dbInstance,
|
||||||
|
);
|
||||||
|
fusion_address.DerivationPath derivationPath;
|
||||||
|
if (derivationPathString == null) {
|
||||||
|
// TODO: check on this:
|
||||||
|
// Either the address is not an address of this wallet
|
||||||
|
// or we need to find out what it is.
|
||||||
|
// If the former, then the issue cannot be easily solved as we will
|
||||||
|
// have no way of finding out what the derivation path is.
|
||||||
|
// Throw exception for now.
|
||||||
|
throw Exception("derivationPathString is null");
|
||||||
|
} else {
|
||||||
|
derivationPath = fusion_address.DerivationPath(
|
||||||
|
derivationPathString,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return fusion_output.Output(
|
return fusion_output.Output(
|
||||||
addr: fusion_address.Address(
|
addr: fusion_address.Address(
|
||||||
addr: e.scriptPubKeyAddress,
|
addr: output.scriptPubKeyAddress,
|
||||||
publicKey: pubKey,
|
publicKey: outputAddressScriptPubKey,
|
||||||
derivationPath: derivationPath,
|
derivationPath: derivationPath,
|
||||||
),
|
),
|
||||||
value: e.value,
|
value: output.value,
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList());
|
||||||
|
|
||||||
return fusionTransaction;
|
return fusionTransaction;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue