Change way for fetch electrum transactions.

M committed Jan 17, 2022 at 17:33 UTC 0ccc78ae70a6d1a224c9c0265fc077707d4f9dbf
3 files changed +110 -19
cw_bitcoin/lib/electrum.dart
+8 -15
@@ -217,23 +217,16 @@ class ElectrumClient {
217 return <String, Object>{};
218 });
219
220 - Future<Map<String, Object>> getTransactionExpanded(
221 - {@required String hash}) async {
222 - try {
223 - final originalTx = await getTransactionRaw(hash: hash);
224 - final vins = originalTx['vin'] as List<Object>;
225 -
226 - for (dynamic vin in vins) {
227 - if (vin is Map<String, Object>) {
228 - vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String);
220 + Future<String> getTransactionHex(
221 + {@required String hash}) async =>
222 + call(method: 'blockchain.transaction.get', params: [hash, false])
223 + .then((dynamic result) {
224 + if (result is String) {
225 + return result;
226 }
230 - }
227
232 - return originalTx;
233 - } catch (_) {
234 - return {};
235 - }
236 - }
228 + return '';
229 + });
230
231 Future<String> broadcastTransaction(
232 {@required String transactionRaw}) async =>
cw_bitcoin/lib/electrum_transaction_info.dart
+72
@@ -1,3 +1,4 @@
1 +import 'dart:typed_data';
2 import 'package:flutter/foundation.dart';
3 import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
4 import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
@@ -8,6 +9,34 @@ import 'package:cw_core/transaction_info.dart';
9 import 'package:cw_core/format_amount.dart';
10 import 'package:cw_core/wallet_type.dart';
11
12 +String addressFromOutput(Uint8List script) {
13 + try {
14 + return bitcoin.P2PKH(
15 + data: PaymentData(output: script),
16 + network: bitcoin.bitcoin)
17 + .data
18 + .address;
19 + } catch (_) {}
20 +
21 + try {
22 + return bitcoin.P2WPKH(
23 + data: PaymentData(output: script),
24 + network: bitcoin.bitcoin)
25 + .data
26 + .address;
27 + } catch(_) {}
28 +
29 + return null;
30 +}
31 +
32 +class ElectrumTransactionBundle {
33 + ElectrumTransactionBundle(this.originalTransaction, {this.ins, this.time, this.confirmations});
34 + final bitcoin.Transaction originalTransaction;
35 + final List<bitcoin.Transaction> ins;
36 + final int time;
37 + final int confirmations;
38 +}
39 +
40 class ElectrumTransactionInfo extends TransactionInfo {
41 ElectrumTransactionInfo(this.type,
42 {@required String id,
@@ -84,6 +113,49 @@ class ElectrumTransactionInfo extends TransactionInfo {
113 confirmations: confirmations);
114 }
115
116 + factory ElectrumTransactionInfo.fromElectrumBundle(
117 + ElectrumTransactionBundle bundle, WalletType type,
118 + {@required Set<String> addresses, int height}) {
119 + final date = DateTime.fromMillisecondsSinceEpoch(bundle.time * 1000);
120 + var direction = TransactionDirection.incoming;
121 + var amount = 0;
122 + var inputAmount = 0;
123 + var totalOutAmount = 0;
124 +
125 + for (var i = 0; i < bundle.originalTransaction.ins.length; i++) {
126 + final input = bundle.originalTransaction.ins[i];
127 + final inputTransaction = bundle.ins[i];
128 + final vout = input.index;
129 + final outTransaction = inputTransaction.outs[vout];
130 + final address = addressFromOutput(outTransaction.script);
131 + inputAmount += outTransaction.value;
132 + if (addresses.contains(address)) {
133 + direction = TransactionDirection.outgoing;
134 + }
135 + }
136 +
137 + for (final out in bundle.originalTransaction.outs) {
138 + totalOutAmount += out.value;
139 + final address = addressFromOutput(out.script);
140 + final addressExists = addresses.contains(address);
141 + if ((direction == TransactionDirection.incoming && addressExists) ||
142 + (direction == TransactionDirection.outgoing && !addressExists)) {
143 + amount += out.value;
144 + }
145 + }
146 +
147 + final fee = inputAmount - totalOutAmount;
148 + return ElectrumTransactionInfo(type,
149 + id: bundle.originalTransaction.getId(),
150 + height: height,
151 + isPending: false,
152 + fee: fee,
153 + direction: direction,
154 + amount: amount,
155 + date: date,
156 + confirmations: bundle.confirmations);
157 + }
158 +
159 factory ElectrumTransactionInfo.fromHexAndHeader(WalletType type, String hex,
160 {List<String> addresses, int height, int timestamp, int confirmations}) {
161 final tx = bitcoin.Transaction.fromHex(hex);
cw_bitcoin/lib/electrum_wallet.dart
+30 -4
@@ -1,6 +1,7 @@
1 import 'dart:async';
2 import 'dart:convert';
3 import 'dart:math';
4 +import 'dart:typed_data';
5 import 'package:cw_core/unspent_coins_info.dart';
6 import 'package:hive/hive.dart';
7 import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
@@ -31,6 +32,7 @@ import 'package:cw_core/sync_status.dart';
32 import 'package:cw_core/transaction_priority.dart';
33 import 'package:cw_core/wallet_info.dart';
34 import 'package:cw_bitcoin/electrum.dart';
35 +import 'package:hex/hex.dart';
36
37 part 'electrum_wallet.g.dart';
38
@@ -479,11 +481,35 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
481 }
482 }
483
484 + Future<ElectrumTransactionBundle> getTransactionExpanded(
485 + {@required String hash, @required int height}) async {
486 + final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
487 + final transactionHex = verboseTransaction['hex'] as String;
488 + final original = bitcoin.Transaction.fromHex(transactionHex);
489 + final ins = <bitcoin.Transaction>[];
490 + final time = verboseTransaction['time'] as int;
491 + final confirmations = verboseTransaction['time'] as int;
492 +
493 + for (final vin in original.ins) {
494 + final id = HEX.encode(vin.hash.reversed.toList());
495 + final txHex = await electrumClient.getTransactionHex(hash: id);
496 + final tx = bitcoin.Transaction.fromHex(txHex);
497 + ins.add(tx);
498 + }
499 +
500 + return ElectrumTransactionBundle(
501 + original,
502 + ins: ins,
503 + time: time,
504 + confirmations: confirmations);
505 + }
506 +
507 Future<ElectrumTransactionInfo> fetchTransactionInfo(
508 {@required String hash, @required int height}) async {
484 - final tx = await electrumClient.getTransactionExpanded(hash: hash);
485 - return ElectrumTransactionInfo.fromElectrumVerbose(tx, walletInfo.type,
486 - height: height, addresses: walletAddresses.addresses);
509 + final tx = await getTransactionExpanded(hash: hash, height: height);
510 + final addresses = walletAddresses.addresses.map((addr) => addr.address).toSet();
511 + return ElectrumTransactionInfo.fromElectrumBundle(
512 + tx,walletInfo.type, addresses: addresses, height: height);
513 }
514
515 @override
@@ -524,7 +550,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
550 scriptHashes.forEach((sh) async {
551 await _scripthashesUpdateSubject[sh]?.close();
552 _scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh);
527 - _scripthashesUpdateSubject[sh].listen((event) async {
553 + _scripthashesUpdateSubject[sh]?.listen((event) async {
554 try {
555 await _updateBalance();
556 await updateUnspent();