Change way for fetch electrum transactions.

This commit is contained in:
M 2022-01-17 17:33:51 +02:00
parent 22d285966d
commit 0ccc78ae70
3 changed files with 110 additions and 19 deletions

View file

@ -217,23 +217,16 @@ class ElectrumClient {
return <String, Object>{}; return <String, Object>{};
}); });
Future<Map<String, Object>> getTransactionExpanded( Future<String> getTransactionHex(
{@required String hash}) async { {@required String hash}) async =>
try { call(method: 'blockchain.transaction.get', params: [hash, false])
final originalTx = await getTransactionRaw(hash: hash); .then((dynamic result) {
final vins = originalTx['vin'] as List<Object>; if (result is String) {
return result;
for (dynamic vin in vins) {
if (vin is Map<String, Object>) {
vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String);
} }
}
return originalTx; return '';
} catch (_) { });
return {};
}
}
Future<String> broadcastTransaction( Future<String> broadcastTransaction(
{@required String transactionRaw}) async => {@required String transactionRaw}) async =>

View file

@ -1,3 +1,4 @@
import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData; import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
@ -8,6 +9,34 @@ import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/format_amount.dart'; import 'package:cw_core/format_amount.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
String addressFromOutput(Uint8List script) {
try {
return bitcoin.P2PKH(
data: PaymentData(output: script),
network: bitcoin.bitcoin)
.data
.address;
} catch (_) {}
try {
return bitcoin.P2WPKH(
data: PaymentData(output: script),
network: bitcoin.bitcoin)
.data
.address;
} catch(_) {}
return null;
}
class ElectrumTransactionBundle {
ElectrumTransactionBundle(this.originalTransaction, {this.ins, this.time, this.confirmations});
final bitcoin.Transaction originalTransaction;
final List<bitcoin.Transaction> ins;
final int time;
final int confirmations;
}
class ElectrumTransactionInfo extends TransactionInfo { class ElectrumTransactionInfo extends TransactionInfo {
ElectrumTransactionInfo(this.type, ElectrumTransactionInfo(this.type,
{@required String id, {@required String id,
@ -84,6 +113,49 @@ class ElectrumTransactionInfo extends TransactionInfo {
confirmations: confirmations); confirmations: confirmations);
} }
factory ElectrumTransactionInfo.fromElectrumBundle(
ElectrumTransactionBundle bundle, WalletType type,
{@required Set<String> addresses, int height}) {
final date = DateTime.fromMillisecondsSinceEpoch(bundle.time * 1000);
var direction = TransactionDirection.incoming;
var amount = 0;
var inputAmount = 0;
var totalOutAmount = 0;
for (var i = 0; i < bundle.originalTransaction.ins.length; i++) {
final input = bundle.originalTransaction.ins[i];
final inputTransaction = bundle.ins[i];
final vout = input.index;
final outTransaction = inputTransaction.outs[vout];
final address = addressFromOutput(outTransaction.script);
inputAmount += outTransaction.value;
if (addresses.contains(address)) {
direction = TransactionDirection.outgoing;
}
}
for (final out in bundle.originalTransaction.outs) {
totalOutAmount += out.value;
final address = addressFromOutput(out.script);
final addressExists = addresses.contains(address);
if ((direction == TransactionDirection.incoming && addressExists) ||
(direction == TransactionDirection.outgoing && !addressExists)) {
amount += out.value;
}
}
final fee = inputAmount - totalOutAmount;
return ElectrumTransactionInfo(type,
id: bundle.originalTransaction.getId(),
height: height,
isPending: false,
fee: fee,
direction: direction,
amount: amount,
date: date,
confirmations: bundle.confirmations);
}
factory ElectrumTransactionInfo.fromHexAndHeader(WalletType type, String hex, factory ElectrumTransactionInfo.fromHexAndHeader(WalletType type, String hex,
{List<String> addresses, int height, int timestamp, int confirmations}) { {List<String> addresses, int height, int timestamp, int confirmations}) {
final tx = bitcoin.Transaction.fromHex(hex); final tx = bitcoin.Transaction.fromHex(hex);

View file

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
@ -31,6 +32,7 @@ import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_bitcoin/electrum.dart';
import 'package:hex/hex.dart';
part 'electrum_wallet.g.dart'; part 'electrum_wallet.g.dart';
@ -479,11 +481,35 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
} }
} }
Future<ElectrumTransactionBundle> getTransactionExpanded(
{@required String hash, @required int height}) async {
final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
final transactionHex = verboseTransaction['hex'] as String;
final original = bitcoin.Transaction.fromHex(transactionHex);
final ins = <bitcoin.Transaction>[];
final time = verboseTransaction['time'] as int;
final confirmations = verboseTransaction['time'] as int;
for (final vin in original.ins) {
final id = HEX.encode(vin.hash.reversed.toList());
final txHex = await electrumClient.getTransactionHex(hash: id);
final tx = bitcoin.Transaction.fromHex(txHex);
ins.add(tx);
}
return ElectrumTransactionBundle(
original,
ins: ins,
time: time,
confirmations: confirmations);
}
Future<ElectrumTransactionInfo> fetchTransactionInfo( Future<ElectrumTransactionInfo> fetchTransactionInfo(
{@required String hash, @required int height}) async { {@required String hash, @required int height}) async {
final tx = await electrumClient.getTransactionExpanded(hash: hash); final tx = await getTransactionExpanded(hash: hash, height: height);
return ElectrumTransactionInfo.fromElectrumVerbose(tx, walletInfo.type, final addresses = walletAddresses.addresses.map((addr) => addr.address).toSet();
height: height, addresses: walletAddresses.addresses); return ElectrumTransactionInfo.fromElectrumBundle(
tx,walletInfo.type, addresses: addresses, height: height);
} }
@override @override
@ -524,7 +550,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
scriptHashes.forEach((sh) async { scriptHashes.forEach((sh) async {
await _scripthashesUpdateSubject[sh]?.close(); await _scripthashesUpdateSubject[sh]?.close();
_scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh); _scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh);
_scripthashesUpdateSubject[sh].listen((event) async { _scripthashesUpdateSubject[sh]?.listen((event) async {
try { try {
await _updateBalance(); await _updateBalance();
await updateUnspent(); await updateUnspent();