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();