Remove Next Address button and logic for electrum wallets. Add disclaimer for electrum wallets on address screen. Change way for generation of current receive and change addresses.

This commit is contained in:
M 2022-01-24 14:04:23 +02:00
parent 2bbd413b12
commit 63a0395c2d
21 changed files with 142 additions and 142 deletions

View file

@ -116,7 +116,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
factory ElectrumTransactionInfo.fromElectrumBundle( factory ElectrumTransactionInfo.fromElectrumBundle(
ElectrumTransactionBundle bundle, WalletType type, ElectrumTransactionBundle bundle, WalletType type,
{@required Set<String> addresses, int height}) { {@required Set<String> addresses, int height}) {
final date = DateTime.fromMillisecondsSinceEpoch(bundle.time * 1000); final date = bundle.time != null
? DateTime.fromMillisecondsSinceEpoch(bundle.time * 1000)
: DateTime.now();
var direction = TransactionDirection.incoming; var direction = TransactionDirection.incoming;
var amount = 0; var amount = 0;
var inputAmount = 0; var inputAmount = 0;

View file

@ -484,7 +484,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
final original = bitcoin.Transaction.fromHex(transactionHex); final original = bitcoin.Transaction.fromHex(transactionHex);
final ins = <bitcoin.Transaction>[]; final ins = <bitcoin.Transaction>[];
final time = verboseTransaction['time'] as int; final time = verboseTransaction['time'] as int;
final confirmations = verboseTransaction['time'] as int; final confirmations = verboseTransaction['confirmations'] as int ?? 0;
for (final vin in original.ins) { for (final vin in original.ins) {
final id = HEX.encode(vin.hash.reversed.toList()); final id = HEX.encode(vin.hash.reversed.toList());
@ -510,13 +510,35 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
@override @override
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async { Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
final addressHashes = <String, BitcoinAddressRecord>{};
final normalizedHistories = <Map<String, dynamic>>[];
walletAddresses.addresses.forEach((addressRecord) {
if (addressRecord.isHidden) {
return;
}
final sh = scriptHash(addressRecord.address, networkType: networkType);
addressHashes[sh] = addressRecord;
});
final histories = final histories =
publicScriptHashes.map((scriptHash) => electrumClient.getHistory(scriptHash)); addressHashes.keys.map((scriptHash) => electrumClient
final _historiesWithDetails = await Future.wait(histories) .getHistory(scriptHash)
.then((histories) => histories.expand((i) => i).toList()) .then((history) => {scriptHash: history}));
.then((histories) => histories.map((tx) => fetchTransactionInfo( final historyResults = await Future.wait(histories);
hash: tx['tx_hash'] as String, height: tx['height'] as int))); historyResults.forEach((history) {
final historiesWithDetails = await Future.wait(_historiesWithDetails); history.entries.forEach((historyItem) {
if (historyItem.value.isNotEmpty) {
final address = addressHashes[historyItem.key];
address.setAsUsed();
normalizedHistories.addAll(historyItem.value);
}
});
});
final historiesWithDetails = await Future.wait(
normalizedHistories
.map((transaction) => fetchTransactionInfo(
hash: transaction['tx_hash'] as String,
height: transaction['height'] as int)));
return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>( return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>(
<String, ElectrumTransactionInfo>{}, (acc, tx) { <String, ElectrumTransactionInfo>{}, (acc, tx) {
@ -534,6 +556,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
_isTransactionUpdating = true; _isTransactionUpdating = true;
final transactions = await fetchTransactions(); final transactions = await fetchTransactions();
transactionHistory.addMany(transactions); transactionHistory.addMany(transactions);
walletAddresses.updateReceiveAddresses();
await transactionHistory.save(); await transactionHistory.save();
_isTransactionUpdating = false; _isTransactionUpdating = false;
} catch (e) { } catch (e) {

View file

@ -6,7 +6,6 @@ import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'dart:math';
part 'electrum_wallet_addresses.g.dart'; part 'electrum_wallet_addresses.g.dart';
@ -22,44 +21,60 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
this.sideHd, this.sideHd,
this.electrumClient, this.electrumClient,
this.networkType}) this.networkType})
: super(walletInfo) { : addresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? []).toSet()),
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? [])
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
.toSet()),
changeAddresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? [])
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
.toSet()),
super(walletInfo) {
currentReceiveAddressIndex = initialRegularAddressIndex; currentReceiveAddressIndex = initialRegularAddressIndex;
currentChangeAddressIndex = initialChangeAddressIndex; currentChangeAddressIndex = initialChangeAddressIndex;
addresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? []).toSet());
} }
static const defaultReceiveAddressesCount = 22; static const defaultReceiveAddressesCount = 22;
static const defaultChangeAddressesCount = 17; static const defaultChangeAddressesCount = 17;
static const gap = 20; static const gap = 20;
final ObservableList<BitcoinAddressRecord> addresses;
final ObservableList<BitcoinAddressRecord> receiveAddresses;
final ObservableList<BitcoinAddressRecord> changeAddresses;
final ElectrumClient electrumClient;
final bitcoin.NetworkType networkType;
final bitcoin.HDWallet mainHd;
final bitcoin.HDWallet sideHd;
@override @override
@observable @computed
String address; String get address => receiveAddresses.first.address;
@override
set address(String addr) => null;
int currentReceiveAddressIndex; int currentReceiveAddressIndex;
int currentChangeAddressIndex; int currentChangeAddressIndex;
ElectrumClient electrumClient;
bitcoin.NetworkType networkType;
bitcoin.HDWallet mainHd;
bitcoin.HDWallet sideHd;
ObservableList<BitcoinAddressRecord> addresses;
List<BitcoinAddressRecord> get receiveAddresses => addresses @computed
.where((addr) => !addr.isHidden && !addr.isUsed) int get totalCountOfReceiveAddresses =>
.toList(); addresses.fold(0, (acc, addressRecord) {
if (!addressRecord.isHidden) {
return acc + 1;
}
return acc;
});
List<BitcoinAddressRecord> get changeAddresses => addresses @computed
.where((addr) => addr.isHidden && !addr.isUsed) int get totalCountOfChangeAddresses =>
.toList(); addresses.fold(0, (acc, addressRecord) {
if (addressRecord.isHidden) {
List<BitcoinAddressRecord> get totalReceiveAddresses => addresses return acc + 1;
.where((addr) => !addr.isHidden) }
.toList(); return acc;
});
List<BitcoinAddressRecord> get totalChangeAddresses => addresses
.where((addr) => addr.isHidden)
.toList();
Future<void> discoverAddresses() async { Future<void> discoverAddresses() async {
await _discoverAddresses(mainHd, false); await _discoverAddresses(mainHd, false);
@ -70,60 +85,39 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@override @override
Future<void> init() async { Future<void> init() async {
await _generateInitialAddresses(); await _generateInitialAddresses();
updateReceiveAddresses();
updateChangeAddresses();
await updateAddressesInBox();
if (receiveAddresses.isEmpty) { if (currentReceiveAddressIndex >= receiveAddresses.length) {
final newAddresses = await _createNewAddresses(
gap,
hd: mainHd,
startIndex: totalReceiveAddresses.length > 0
? totalReceiveAddresses.length - 1
: 0,
isHidden: false);
_addAddresses(newAddresses);
} else if (currentReceiveAddressIndex >= receiveAddresses.length) {
currentReceiveAddressIndex = 0; currentReceiveAddressIndex = 0;
} }
address = receiveAddresses[currentReceiveAddressIndex].address; if (currentChangeAddressIndex >= changeAddresses.length) {
await updateAddressesInBox(); currentChangeAddressIndex = 0;
}
@action
Future<void> nextReceiveAddress() async {
if (receiveAddresses.isEmpty) {
final newAddresses = await _createNewAddresses(
gap,
hd: mainHd,
startIndex: totalReceiveAddresses.length > 0
? totalReceiveAddresses.length - 1
: 0,
isHidden: false);
_addAddresses(newAddresses);
} else if (currentReceiveAddressIndex >= receiveAddresses.length) {
currentReceiveAddressIndex = 0;
} }
address = receiveAddresses[currentReceiveAddressIndex].address;
currentReceiveAddressIndex += 1;
await updateAddressesInBox();
} }
@action @action
Future<String> getChangeAddress() async { Future<String> getChangeAddress() async {
updateChangeAddresses();
if (changeAddresses.isEmpty) { if (changeAddresses.isEmpty) {
final newAddresses = await _createNewAddresses( final newAddresses = await _createNewAddresses(
gap, gap,
hd: sideHd, hd: sideHd,
startIndex: totalChangeAddresses.length > 0 startIndex: totalCountOfChangeAddresses > 0
? totalChangeAddresses.length - 1 ? totalCountOfChangeAddresses - 1
: 0, : 0,
isHidden: true); isHidden: true);
_addAddresses(newAddresses); _addAddresses(newAddresses);
} else if (currentChangeAddressIndex >= changeAddresses.length) { }
if (currentChangeAddressIndex >= changeAddresses.length) {
currentChangeAddressIndex = 0; currentChangeAddressIndex = 0;
} }
updateChangeAddresses();
final address = changeAddresses[currentChangeAddressIndex].address; final address = changeAddresses[currentChangeAddressIndex].address;
currentChangeAddressIndex += 1; currentChangeAddressIndex += 1;
return address; return address;
@ -153,18 +147,20 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
} }
} }
void randomizeAddress() { @action
const minCountOfVisibleAddresses = 5; void updateReceiveAddresses() {
final random = Random(); receiveAddresses.removeRange(0, receiveAddresses.length);
var availableAddresses = addresses final newAdresses = addresses
.where((addr) => !addr.isHidden) .where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
.toList(); receiveAddresses.addAll(newAdresses);
}
if (availableAddresses.length < minCountOfVisibleAddresses) { @action
availableAddresses = addresses; void updateChangeAddresses() {
} changeAddresses.removeRange(0, changeAddresses.length);
final newAdresses = addresses
address = availableAddresses[random.nextInt(availableAddresses.length)].address; .where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
changeAddresses.addAll(newAdresses);
} }
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async { Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
@ -187,12 +183,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
while(hasAddrUse) { while(hasAddrUse) {
final addr = addrs.last.address; final addr = addrs.last.address;
hasAddrUse = await _validateAddressUsing(addr); hasAddrUse = await _hasAddressUsed(addr);
if (!hasAddrUse) { if (!hasAddrUse) {
break; break;
} }
final start = addrs.length; final start = addrs.length;
final count = start + gap; final count = start + gap;
final batch = await _createNewAddresses( final batch = await _createNewAddresses(
@ -264,9 +260,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
this.addresses.addAll(addressesSet); this.addresses.addAll(addressesSet);
} }
Future<bool> _validateAddressUsing(String address) async { Future<bool> _hasAddressUsed(String address) async {
final sh = scriptHash(address, networkType: networkType); final sh = scriptHash(address, networkType: networkType);
final balance = await electrumClient.getBalance(sh); final transactionHistory = await electrumClient.getHistory(sh);
return balance.isEmpty; return transactionHistory.isNotEmpty;
} }
} }

View file

@ -53,18 +53,6 @@ class CWBitcoin extends Bitcoin {
final bitcoinWallet = wallet as ElectrumWallet; final bitcoinWallet = wallet as ElectrumWallet;
await bitcoinWallet.walletAddresses.generateNewAddress(); await bitcoinWallet.walletAddresses.generateNewAddress();
} }
@override
Future<void> nextAddress(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
bitcoinWallet.walletAddresses.nextReceiveAddress();
}
@override
Future<void> randomAddress(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
bitcoinWallet.walletAddresses.randomizeAddress();
}
@override @override
Object createBitcoinTransactionCredentials(List<Output> outputs, TransactionPriority priority) Object createBitcoinTransactionCredentials(List<Output> outputs, TransactionPriority priority)

View file

@ -125,14 +125,15 @@ class AddressPage extends StatelessWidget {
), ),
), ),
) )
: PrimaryButton( : Text(
onPressed: () => addressListViewModel.nextAddress(), S.of(context).electrum_address_disclaimer,
text: 'Next address', textAlign: TextAlign.center,
color: Theme.of(context).buttonColor, style: TextStyle(
textColor: Theme.of(context) fontSize: 15,
.accentTextTheme color: Theme.of(context)
.display3 .accentTextTheme
.backgroundColor); .display2
.backgroundColor));
}) })
], ],
), ),

View file

@ -212,26 +212,4 @@ abstract class WalletAddressListViewModelBase with Store {
_baseItems.add(WalletAddressListHeader()); _baseItems.add(WalletAddressListHeader());
} }
@action
void nextAddress() {
final wallet = _wallet;
if (wallet.type == WalletType.bitcoin
|| wallet.type == WalletType.litecoin) {
bitcoin.nextAddress(wallet);
wallet.save();
}
}
@action
void generateRandomAddress() {
final wallet = _wallet;
if (wallet.type == WalletType.bitcoin
|| wallet.type == WalletType.litecoin) {
bitcoin.randomAddress(wallet);
wallet.save();
}
}
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yats leben auch außerhalb von Cake Wallet. Jede Wallet-Adresse auf der Welt kann durch ein Yat ersetzt werden!", "third_intro_content" : "Yats leben auch außerhalb von Cake Wallet. Jede Wallet-Adresse auf der Welt kann durch ein Yat ersetzt werden!",
"learn_more" : "Erfahren Sie mehr", "learn_more" : "Erfahren Sie mehr",
"new_template" : "neue Vorlage" "new_template" : "neue Vorlage",
"electrum_address_disclaimer": "Wir generieren jedes Mal neue Adressen, wenn Sie eine verwenden, aber vorherige Adressen funktionieren weiterhin"
} }

View file

@ -523,5 +523,6 @@
"third_intro_content" : "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!", "third_intro_content" : "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!",
"learn_more" : "Learn More", "learn_more" : "Learn More",
"new_template" : "New Template" "new_template" : "New Template",
"electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Los Yats también viven fuera de Cake Wallet. Cualquier dirección de billetera en la tierra se puede reemplazar con un Yat!", "third_intro_content" : "Los Yats también viven fuera de Cake Wallet. Cualquier dirección de billetera en la tierra se puede reemplazar con un Yat!",
"learn_more" : "Aprende más", "learn_more" : "Aprende más",
"new_template" : "Nueva plantilla" "new_template" : "Nueva plantilla",
"electrum_address_disclaimer": "Generamos nuevas direcciones cada vez que usa una, pero las direcciones anteriores siguen funcionando"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yats Cake Wallet के बाहर भी रहता है। धरती पर किसी भी वॉलेट पते को Yat से बदला जा सकता है!", "third_intro_content" : "Yats Cake Wallet के बाहर भी रहता है। धरती पर किसी भी वॉलेट पते को Yat से बदला जा सकता है!",
"learn_more" : "और अधिक जानें", "learn_more" : "और अधिक जानें",
"new_template" : "नया टेम्पलेट" "new_template" : "नया टेम्पलेट",
"electrum_address_disclaimer": "हर बार जब आप एक का उपयोग करते हैं तो हम नए पते उत्पन्न करते हैं, लेकिन पिछले पते काम करना जारी रखते हैं"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yats žive i izvan Cake Wallet -a. Bilo koja adresa novčanika na svijetu može se zamijeniti Yat!", "third_intro_content" : "Yats žive i izvan Cake Wallet -a. Bilo koja adresa novčanika na svijetu može se zamijeniti Yat!",
"learn_more" : "Saznajte više", "learn_more" : "Saznajte više",
"new_template" : "novi predložak" "new_template" : "novi predložak",
"electrum_address_disclaimer": "Minden egyes alkalommal új címeket generálunk, de a korábbi címek továbbra is működnek"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Anche Yats vive fuori da Cake Wallet. Qualsiasi indirizzo di portafoglio sulla terra può essere sostituito con un Yat!", "third_intro_content" : "Anche Yats vive fuori da Cake Wallet. Qualsiasi indirizzo di portafoglio sulla terra può essere sostituito con un Yat!",
"learn_more" : "Impara di più", "learn_more" : "Impara di più",
"new_template" : "Nuovo modello" "new_template" : "Nuovo modello",
"electrum_address_disclaimer": "Generiamo nuovi indirizzi ogni volta che ne utilizzi uno, ma gli indirizzi precedenti continuano a funzionare"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "YatsはCakeWalletの外にも住んでいます。 地球上のどのウォレットアドレスもYatに置き換えることができます", "third_intro_content" : "YatsはCakeWalletの外にも住んでいます。 地球上のどのウォレットアドレスもYatに置き換えることができます",
"learn_more" : "もっと詳しく知る", "learn_more" : "もっと詳しく知る",
"new_template" : "新しいテンプレート" "new_template" : "新しいテンプレート",
"electrum_address_disclaimer": "使用するたびに新しいアドレスが生成されますが、以前のアドレスは引き続き機能します"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yats는 Cake Wallet 밖에서도 살고 있습니다. 지구상의 모든 지갑 주소는 Yat!", "third_intro_content" : "Yats는 Cake Wallet 밖에서도 살고 있습니다. 지구상의 모든 지갑 주소는 Yat!",
"learn_more" : "더 알아보기", "learn_more" : "더 알아보기",
"new_template" : "새 템플릿" "new_template" : "새 템플릿",
"electrum_address_disclaimer": "사용할 때마다 새 주소가 생성되지만 이전 주소는 계속 작동합니다."
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yats wonen ook buiten Cake Wallet. Elk portemonnee-adres op aarde kan worden vervangen door een Yat!", "third_intro_content" : "Yats wonen ook buiten Cake Wallet. Elk portemonnee-adres op aarde kan worden vervangen door een Yat!",
"learn_more" : "Kom meer te weten", "learn_more" : "Kom meer te weten",
"new_template" : "Nieuwe sjabloon" "new_template" : "Nieuwe sjabloon",
"electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yats mieszkają również poza Cake Wallet. Każdy adres portfela na ziemi można zastąpić Yat!", "third_intro_content" : "Yats mieszkają również poza Cake Wallet. Każdy adres portfela na ziemi można zastąpić Yat!",
"learn_more" : "Ucz się więcej", "learn_more" : "Ucz się więcej",
"new_template" : "Nowy szablon" "new_template" : "Nowy szablon",
"electrum_address_disclaimer": "Za każdym razem, gdy korzystasz z jednego z nich, generujemy nowe adresy, ale poprzednie adresy nadal działają"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yats também mora fora da Cake Wallet. Qualquer endereço de carteira na Terra pode ser substituído por um Yat!", "third_intro_content" : "Yats também mora fora da Cake Wallet. Qualquer endereço de carteira na Terra pode ser substituído por um Yat!",
"learn_more" : "Saber mais", "learn_more" : "Saber mais",
"new_template" : "Novo modelo" "new_template" : "Novo modelo",
"electrum_address_disclaimer": "Geramos novos endereços cada vez que você usa um, mas os endereços anteriores continuam funcionando"
} }

View file

@ -522,5 +522,6 @@
"third_intro_content" : "Yat находятся за пределами Cake Wallet. Любой адрес кошелька на земле можно заменить на Yat!", "third_intro_content" : "Yat находятся за пределами Cake Wallet. Любой адрес кошелька на земле можно заменить на Yat!",
"learn_more" : "Узнать больше", "learn_more" : "Узнать больше",
"new_template" : "Новый шаблон" "new_template" : "Новый шаблон",
"electrum_address_disclaimer": "Мы генерируем новые адреса каждый раз, когда вы их используете, но предыдущие адреса продолжают работать."
} }

View file

@ -521,5 +521,6 @@
"third_intro_content" : "Yat знаходиться за межами Cake Wallet. Будь-яку адресу гаманця на землі можна замінити на Yat!", "third_intro_content" : "Yat знаходиться за межами Cake Wallet. Будь-яку адресу гаманця на землі можна замінити на Yat!",
"learn_more" : "Дізнатися більше", "learn_more" : "Дізнатися більше",
"new_template" : "Новий шаблон" "new_template" : "Новий шаблон",
"electrum_address_disclaimer": "Ми створюємо нові адреси щоразу, коли ви використовуєте їх, але попередні адреси продовжують працювати"
} }

View file

@ -520,5 +520,6 @@
"third_intro_content" : "Yats 也住在 Cake Wallet 之外。 地球上任何一個錢包地址都可以用一個Yat來代替", "third_intro_content" : "Yats 也住在 Cake Wallet 之外。 地球上任何一個錢包地址都可以用一個Yat來代替",
"learn_more" : "了解更多", "learn_more" : "了解更多",
"new_template" : "新模板" "new_template" : "新模板",
"electrum_address_disclaimer": "每次您使用一个地址时,我们都会生成新地址,但之前的地址仍然有效"
} }

View file

@ -74,8 +74,6 @@ abstract class Bitcoin {
TransactionPriority deserializeBitcoinTransactionPriority(int raw); TransactionPriority deserializeBitcoinTransactionPriority(int raw);
int getFeeRate(Object wallet, TransactionPriority priority); int getFeeRate(Object wallet, TransactionPriority priority);
Future<void> generateNewAddress(Object wallet); Future<void> generateNewAddress(Object wallet);
Future<void> nextAddress(Object wallet);
Future<void> randomAddress(Object wallet);
Object createBitcoinTransactionCredentials(List<Output> outputs, TransactionPriority priority); Object createBitcoinTransactionCredentials(List<Output> outputs, TransactionPriority priority);
List<String> getAddresses(Object wallet); List<String> getAddresses(Object wallet);