mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-11-16 17:27:37 +00:00
feat: rescan a specific address
This commit is contained in:
parent
fc7bea6830
commit
bf0dcc8d7b
6 changed files with 85 additions and 10 deletions
|
@ -63,6 +63,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||||
super.isUsed = false,
|
super.isUsed = false,
|
||||||
required super.type,
|
required super.type,
|
||||||
String? scriptHash,
|
String? scriptHash,
|
||||||
|
this.spendKey,
|
||||||
required super.network,
|
required super.network,
|
||||||
}) : scriptHash = scriptHash ??
|
}) : scriptHash = scriptHash ??
|
||||||
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network) : null);
|
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network) : null);
|
||||||
|
@ -88,6 +89,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
String? scriptHash;
|
String? scriptHash;
|
||||||
|
ECPrivate? spendKey;
|
||||||
|
|
||||||
String getScriptHash(BasedUtxoNetwork network) {
|
String getScriptHash(BasedUtxoNetwork network) {
|
||||||
if (scriptHash != null) return scriptHash!;
|
if (scriptHash != null) return scriptHash!;
|
||||||
|
|
|
@ -602,7 +602,11 @@ abstract class ElectrumWalletBase
|
||||||
spendsSilentPayment = true;
|
spendsSilentPayment = true;
|
||||||
isSilentPayment = true;
|
isSilentPayment = true;
|
||||||
} else if (!isHardwareWallet) {
|
} else if (!isHardwareWallet) {
|
||||||
privkey =
|
final spendKey = (utx.bitcoinAddressRecord as BitcoinAddressRecord).spendKey;
|
||||||
|
// if spend key is present, this is needed to disable tweaking the key during signing
|
||||||
|
isSilentPayment = spendKey != null;
|
||||||
|
|
||||||
|
privkey = spendKey ??
|
||||||
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1266,14 +1270,22 @@ abstract class ElectrumWalletBase
|
||||||
if (hasSilentPaymentsScanning) {
|
if (hasSilentPaymentsScanning) {
|
||||||
// Update unspents stored from scanned silent payment transactions
|
// Update unspents stored from scanned silent payment transactions
|
||||||
transactionHistory.transactions.values.forEach((tx) {
|
transactionHistory.transactions.values.forEach((tx) {
|
||||||
if (tx.unspents != null) {
|
if (tx.unspents != null && tx.unspents!.isNotEmpty) {
|
||||||
updatedUnspentCoins.addAll(tx.unspents!);
|
updatedUnspentCoins.addAll(tx.unspents!);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(walletAddresses.allAddresses.map((address) async {
|
await Future.wait(walletAddresses.allAddresses.map((address) async {
|
||||||
updatedUnspentCoins.addAll(await fetchUnspent(address));
|
final unspentList = await fetchUnspent(address);
|
||||||
|
|
||||||
|
if (unspentList.isNotEmpty) {
|
||||||
|
for (final unspent in unspentList) {
|
||||||
|
if (!updatedUnspentCoins.any((element) => element.hash == unspent.hash)) {
|
||||||
|
updatedUnspentCoins.add(unspent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
unspentCoins = updatedUnspentCoins;
|
unspentCoins = updatedUnspentCoins;
|
||||||
|
@ -1860,6 +1872,12 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> fullAddressUpdate(BitcoinAddressRecord address) async {
|
||||||
|
await updateUnspents(address);
|
||||||
|
await _fetchBalance(address);
|
||||||
|
await _fetchAddressHistory(address, await getCurrentChainTip());
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _subscribeForUpdates() async {
|
Future<void> _subscribeForUpdates() async {
|
||||||
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
|
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
|
||||||
(address) => !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)),
|
(address) => !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)),
|
||||||
|
@ -1871,11 +1889,7 @@ abstract class ElectrumWalletBase
|
||||||
_scripthashesUpdateSubject[sh] = await electrumClient.scripthashUpdate(sh);
|
_scripthashesUpdateSubject[sh] = await electrumClient.scripthashUpdate(sh);
|
||||||
_scripthashesUpdateSubject[sh]?.listen((event) async {
|
_scripthashesUpdateSubject[sh]?.listen((event) async {
|
||||||
try {
|
try {
|
||||||
await updateUnspents(address);
|
await fullAddressUpdate(address);
|
||||||
|
|
||||||
await updateBalance();
|
|
||||||
|
|
||||||
await _fetchAddressHistory(address, await getCurrentChainTip());
|
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
_onError?.call(FlutterErrorDetails(
|
_onError?.call(FlutterErrorDetails(
|
||||||
|
@ -1888,6 +1902,20 @@ abstract class ElectrumWalletBase
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<ElectrumBalance> _fetchBalance(BitcoinAddressRecord address) async {
|
||||||
|
final sh = address.getScriptHash(network);
|
||||||
|
final balance = await electrumClient.getBalance(sh);
|
||||||
|
|
||||||
|
final totalConfirmed = balance['confirmed'] as int? ?? 0;
|
||||||
|
final totalUnconfirmed = balance['unconfirmed'] as int? ?? 0;
|
||||||
|
|
||||||
|
if (totalConfirmed > 0 || totalUnconfirmed > 0) {
|
||||||
|
address.setAsUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElectrumBalance(confirmed: totalConfirmed, unconfirmed: totalUnconfirmed, frozen: 0);
|
||||||
|
}
|
||||||
|
|
||||||
Future<ElectrumBalance> _fetchBalances() async {
|
Future<ElectrumBalance> _fetchBalances() async {
|
||||||
final addresses = walletAddresses.allAddresses.toList();
|
final addresses = walletAddresses.allAddresses.toList();
|
||||||
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
||||||
|
|
|
@ -588,7 +588,6 @@ class CWBitcoin extends Bitcoin {
|
||||||
}
|
}
|
||||||
|
|
||||||
final updatedOutputs = outputs.map((output) {
|
final updatedOutputs = outputs.map((output) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final pendingOut = pendingTx!.outputs[outputs.indexOf(output)];
|
final pendingOut = pendingTx!.outputs[outputs.indexOf(output)];
|
||||||
final updatedOutput = output;
|
final updatedOutput = output;
|
||||||
|
@ -609,4 +608,31 @@ class CWBitcoin extends Bitcoin {
|
||||||
final tx = txInfo as ElectrumTransactionInfo;
|
final tx = txInfo as ElectrumTransactionInfo;
|
||||||
return tx.isReceivedSilentPayment;
|
return tx.isReceivedSilentPayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> fullAddressUpdate(Object wallet, String address) async {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
final spAddressRecord =
|
||||||
|
bitcoinWallet.walletAddresses.silentAddresses.firstWhere((addr) => addr.address == address);
|
||||||
|
|
||||||
|
final bitcoinAddressRecord = BitcoinAddressRecord(
|
||||||
|
address,
|
||||||
|
index: spAddressRecord.index,
|
||||||
|
isHidden: spAddressRecord.isHidden,
|
||||||
|
txCount: spAddressRecord.txCount,
|
||||||
|
balance: spAddressRecord.balance,
|
||||||
|
name: spAddressRecord.name,
|
||||||
|
isUsed: spAddressRecord.isUsed,
|
||||||
|
type: spAddressRecord.type,
|
||||||
|
network: spAddressRecord.network,
|
||||||
|
spendKey: bitcoinWallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
|
||||||
|
BigintUtils.fromBytes(
|
||||||
|
BytesUtils.fromHexString(spAddressRecord.silentPaymentTweak!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
bitcoinWallet.walletAddresses.addAddresses([bitcoinAddressRecord]);
|
||||||
|
bitcoinWallet.fullAddressUpdate(bitcoinAddressRecord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ class AddressCell extends StatelessWidget {
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onEdit,
|
this.onEdit,
|
||||||
this.onDelete,
|
this.onDelete,
|
||||||
|
this.onRescan,
|
||||||
this.txCount,
|
this.txCount,
|
||||||
this.balance,
|
this.balance,
|
||||||
this.isChange = false,
|
this.isChange = false,
|
||||||
|
@ -30,6 +31,7 @@ class AddressCell extends StatelessWidget {
|
||||||
bool hasBalance = false,
|
bool hasBalance = false,
|
||||||
Function()? onEdit,
|
Function()? onEdit,
|
||||||
Function()? onDelete,
|
Function()? onDelete,
|
||||||
|
Function()? onRescan,
|
||||||
}) =>
|
}) =>
|
||||||
AddressCell(
|
AddressCell(
|
||||||
address: item.address,
|
address: item.address,
|
||||||
|
@ -41,6 +43,7 @@ class AddressCell extends StatelessWidget {
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onEdit: onEdit,
|
onEdit: onEdit,
|
||||||
onDelete: onDelete,
|
onDelete: onDelete,
|
||||||
|
onRescan: onRescan,
|
||||||
txCount: item.txCount,
|
txCount: item.txCount,
|
||||||
balance: item.balance,
|
balance: item.balance,
|
||||||
isChange: item.isChange,
|
isChange: item.isChange,
|
||||||
|
@ -55,6 +58,7 @@ class AddressCell extends StatelessWidget {
|
||||||
final Function(String)? onTap;
|
final Function(String)? onTap;
|
||||||
final Function()? onEdit;
|
final Function()? onEdit;
|
||||||
final Function()? onDelete;
|
final Function()? onDelete;
|
||||||
|
final Function()? onRescan;
|
||||||
final int? txCount;
|
final int? txCount;
|
||||||
final String? balance;
|
final String? balance;
|
||||||
final bool isChange;
|
final bool isChange;
|
||||||
|
@ -89,7 +93,9 @@ class AddressCell extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: name.isNotEmpty ? MainAxisAlignment.spaceBetween : MainAxisAlignment.center,
|
mainAxisAlignment: name.isNotEmpty
|
||||||
|
? MainAxisAlignment.spaceBetween
|
||||||
|
: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
|
@ -196,6 +202,14 @@ class AddressCell extends StatelessWidget {
|
||||||
icon: Icons.edit,
|
icon: Icons.edit,
|
||||||
label: S.of(context).edit,
|
label: S.of(context).edit,
|
||||||
),
|
),
|
||||||
|
if (onRescan != null)
|
||||||
|
SlidableAction(
|
||||||
|
onPressed: (_) => onRescan!.call(),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: Icons.refresh,
|
||||||
|
label: S.of(context).rescan,
|
||||||
|
),
|
||||||
if (onDelete != null)
|
if (onDelete != null)
|
||||||
SlidableAction(
|
SlidableAction(
|
||||||
onPressed: (_) => onDelete!.call(),
|
onPressed: (_) => onDelete!.call(),
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -102,6 +103,9 @@ class AddressList extends StatelessWidget {
|
||||||
onEdit: editable
|
onEdit: editable
|
||||||
? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item)
|
? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item)
|
||||||
: null,
|
: null,
|
||||||
|
onRescan: (item.isOneTimeReceiveAddress ?? false)
|
||||||
|
? () => bitcoin!.fullAddressUpdate(addressListViewModel.wallet, item.address)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,7 @@ abstract class Bitcoin {
|
||||||
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM, {int index = 0, int limit = 5});
|
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM, {int index = 0, int limit = 5});
|
||||||
List<Output> updateOutputs(PendingTransaction pendingTransaction, List<Output> outputs);
|
List<Output> updateOutputs(PendingTransaction pendingTransaction, List<Output> outputs);
|
||||||
bool txIsReceivedSilentPayment(TransactionInfo txInfo);
|
bool txIsReceivedSilentPayment(TransactionInfo txInfo);
|
||||||
|
Future<void> fullAddressUpdate(Object wallet, String address);
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue