diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index 7e4b5f58f..2c3abad0f 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -6,13 +6,12 @@ abstract class BaseBitcoinAddressRecord { BaseBitcoinAddressRecord( this.address, { required this.index, - this.isHidden = false, + this.isChange = false, int txCount = 0, int balance = 0, String name = '', bool isUsed = false, required this.type, - required this.network, }) : _txCount = txCount, _balance = balance, _name = name, @@ -22,13 +21,12 @@ abstract class BaseBitcoinAddressRecord { bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address; final String address; - bool isHidden; + bool isChange; final int index; int _txCount; int _balance; String _name; bool _isUsed; - BasedUtxoNetwork? network; int get txCount => _txCount; @@ -56,24 +54,29 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { BitcoinAddressRecord( super.address, { required super.index, - super.isHidden = false, + super.isChange = false, super.txCount = 0, super.balance = 0, super.name = '', super.isUsed = false, required super.type, String? scriptHash, - required super.network, - }) : scriptHash = scriptHash ?? - (network != null ? BitcoinAddressUtils.scriptHash(address, network: network) : null); + BasedUtxoNetwork? network, + }) { + if (scriptHash == null && network == null) { + throw ArgumentError('either scriptHash or network must be provided'); + } - factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { + this.scriptHash = scriptHash ?? BitcoinAddressUtils.scriptHash(address, network: network!); + } + + factory BitcoinAddressRecord.fromJSON(String jsonSource) { final decoded = json.decode(jsonSource) as Map; return BitcoinAddressRecord( decoded['address'] as String, index: decoded['index'] as int, - isHidden: decoded['isHidden'] as bool? ?? false, + isChange: decoded['isChange'] as bool? ?? false, isUsed: decoded['isUsed'] as bool? ?? false, txCount: decoded['txCount'] as int? ?? 0, name: decoded['name'] as String? ?? '', @@ -83,23 +86,16 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { .firstWhere((type) => type.toString() == decoded['type'] as String) : SegwitAddresType.p2wpkh, scriptHash: decoded['scriptHash'] as String?, - network: network, ); } - String? scriptHash; - - String getScriptHash(BasedUtxoNetwork network) { - if (scriptHash != null) return scriptHash!; - scriptHash = BitcoinAddressUtils.scriptHash(address, network: network); - return scriptHash!; - } + late String scriptHash; @override String toJSON() => json.encode({ 'address': address, 'index': index, - 'isHidden': isHidden, + 'isChange': isChange, 'isUsed': isUsed, 'txCount': txCount, 'name': name, @@ -110,18 +106,23 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { } class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { + int get labelIndex => index; + final String? labelHex; + BitcoinSilentPaymentAddressRecord( super.address, { - required super.index, - super.isHidden = false, + required int labelIndex, super.txCount = 0, super.balance = 0, super.name = '', super.isUsed = false, - required this.silentPaymentTweak, - required super.network, - required super.type, - }) : super(); + super.type = SilentPaymentsAddresType.p2sp, + this.labelHex, + }) : super(index: labelIndex, isChange: labelIndex == 0) { + if (labelIndex != 1 && labelHex == null) { + throw ArgumentError('label must be provided for silent address index > 0'); + } + } factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { @@ -129,36 +130,68 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { return BitcoinSilentPaymentAddressRecord( decoded['address'] as String, - index: decoded['index'] as int, - isHidden: decoded['isHidden'] as bool? ?? false, + labelIndex: decoded['labelIndex'] as int, isUsed: decoded['isUsed'] as bool? ?? false, txCount: decoded['txCount'] as int? ?? 0, name: decoded['name'] as String? ?? '', balance: decoded['balance'] as int? ?? 0, - network: (decoded['network'] as String?) == null - ? network - : BasedUtxoNetwork.fromName(decoded['network'] as String), - silentPaymentTweak: decoded['silent_payment_tweak'] as String?, - type: decoded['type'] != null && decoded['type'] != '' - ? BitcoinAddressType.values - .firstWhere((type) => type.toString() == decoded['type'] as String) - : SilentPaymentsAddresType.p2sp, + labelHex: decoded['labelHex'] as String?, ); } - final String? silentPaymentTweak; - @override String toJSON() => json.encode({ 'address': address, - 'index': index, - 'isHidden': isHidden, + 'labelIndex': labelIndex, 'isUsed': isUsed, 'txCount': txCount, 'name': name, 'balance': balance, 'type': type.toString(), - 'network': network?.value, - 'silent_payment_tweak': silentPaymentTweak, + 'labelHex': labelHex, + }); +} + +class BitcoinReceivedSPAddressRecord extends BitcoinSilentPaymentAddressRecord { + final ECPrivate spendKey; + + BitcoinReceivedSPAddressRecord( + super.address, { + required super.labelIndex, + super.txCount = 0, + super.balance = 0, + super.name = '', + super.isUsed = false, + required this.spendKey, + super.type = SegwitAddresType.p2tr, + super.labelHex, + }); + + factory BitcoinReceivedSPAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { + final decoded = json.decode(jsonSource) as Map; + + return BitcoinReceivedSPAddressRecord( + decoded['address'] as String, + labelIndex: decoded['index'] as int, + isUsed: decoded['isUsed'] as bool? ?? false, + txCount: decoded['txCount'] as int? ?? 0, + name: decoded['name'] as String? ?? '', + balance: decoded['balance'] as int? ?? 0, + labelHex: decoded['label'] as String?, + spendKey: ECPrivate.fromHex(decoded['spendKey'] as String), + ); + } + + @override + String toJSON() => json.encode({ + 'address': address, + 'labelIndex': labelIndex, + 'isUsed': isUsed, + 'txCount': txCount, + 'name': name, + 'balance': balance, + 'type': type.toString(), + 'labelHex': labelHex, + 'spend_key': spendKey.toString(), }); } diff --git a/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart b/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart index a02c51c69..582147e3d 100644 --- a/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_core/hardware/hardware_account_data.dart'; import 'package:ledger_bitcoin/ledger_bitcoin.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus.dart'; @@ -12,8 +11,7 @@ class BitcoinHardwareWalletService { final LedgerConnection ledgerConnection; - Future> getAvailableAccounts( - {int index = 0, int limit = 5}) async { + Future> getAvailableAccounts({int index = 0, int limit = 5}) async { final bitcoinLedgerApp = BitcoinLedgerApp(ledgerConnection); final masterFp = await bitcoinLedgerApp.getMasterFingerprint(); @@ -23,16 +21,13 @@ class BitcoinHardwareWalletService { for (final i in indexRange) { final derivationPath = "m/84'/0'/$i'"; - final xpub = - await bitcoinLedgerApp.getXPubKey(derivationPath: derivationPath); + final xpub = await bitcoinLedgerApp.getXPubKey(derivationPath: derivationPath); Bip32Slip10Secp256k1 hd = Bip32Slip10Secp256k1.fromExtendedKey(xpub).childKey(Bip32KeyIndex(0)); - final address = generateP2WPKHAddress( - hd: hd, index: 0, network: BitcoinNetwork.mainnet); - accounts.add(HardwareAccountData( - address: address, + address: P2wpkhAddress.fromBip32(bip32: hd, account: i, index: 0) + .toAddress(BitcoinNetwork.mainnet), accountIndex: i, derivationPath: derivationPath, masterFingerprint: masterFp, diff --git a/cw_bitcoin/lib/bitcoin_unspent.dart b/cw_bitcoin/lib/bitcoin_unspent.dart index 3691a7a22..d3421980a 100644 --- a/cw_bitcoin/lib/bitcoin_unspent.dart +++ b/cw_bitcoin/lib/bitcoin_unspent.dart @@ -26,42 +26,3 @@ class BitcoinUnspent extends Unspent { final BaseBitcoinAddressRecord bitcoinAddressRecord; } - -class BitcoinSilentPaymentsUnspent extends BitcoinUnspent { - BitcoinSilentPaymentsUnspent( - BitcoinSilentPaymentAddressRecord addressRecord, - String hash, - int value, - int vout, { - required this.silentPaymentTweak, - required this.silentPaymentLabel, - }) : super(addressRecord, hash, value, vout); - - @override - factory BitcoinSilentPaymentsUnspent.fromJSON( - BitcoinSilentPaymentAddressRecord? address, Map json) => - BitcoinSilentPaymentsUnspent( - address ?? BitcoinSilentPaymentAddressRecord.fromJSON(json['address_record'].toString()), - json['tx_hash'] as String, - json['value'] as int, - json['tx_pos'] as int, - silentPaymentTweak: json['silent_payment_tweak'] as String?, - silentPaymentLabel: json['silent_payment_label'] as String?, - ); - - @override - Map toJson() { - final json = { - 'address_record': bitcoinAddressRecord.toJSON(), - 'tx_hash': hash, - 'value': value, - 'tx_pos': vout, - 'silent_payment_tweak': silentPaymentTweak, - 'silent_payment_label': silentPaymentLabel, - }; - return json; - } - - String? silentPaymentTweak; - String? silentPaymentLabel; -} diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 908897845..029b6f241 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -61,16 +61,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialBalance: initialBalance, seedBytes: seedBytes, encryptionFileUtils: encryptionFileUtils, - currency: networkParam == BitcoinNetwork.testnet - ? CryptoCurrency.tbtc - : CryptoCurrency.btc, + currency: + networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc, alwaysScan: alwaysScan, ) { - // in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here) - // the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here) - // String derivationPath = walletInfo.derivationInfo!.derivationPath!; - // String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1"; - // final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType); walletAddresses = BitcoinWalletAddresses( walletInfo, initialAddresses: initialAddresses, @@ -78,17 +72,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialChangeAddressIndex: initialChangeAddressIndex, initialSilentAddresses: initialSilentAddresses, initialSilentAddressIndex: initialSilentAddressIndex, - mainHd: hd, - sideHd: accountHD.childKey(Bip32KeyIndex(1)), + bip32: bip32, network: networkParam ?? network, - masterHd: - seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null, - isHardwareWallet: walletInfo.isHardwareWallet, ); autorun((_) { - this.walletAddresses.isEnabledAutoGenerateSubaddress = - this.isEnabledAutoGenerateSubaddress; + this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; }); } @@ -189,10 +178,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { walletInfo.derivationInfo ??= DerivationInfo(); // set the default if not present: - walletInfo.derivationInfo!.derivationPath ??= - snp?.derivationPath ?? electrum_path; - walletInfo.derivationInfo!.derivationType ??= - snp?.derivationType ?? DerivationType.electrum; + walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path; + walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum; Uint8List? seedBytes = null; final mnemonic = keysData.mnemonic; @@ -260,10 +247,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { final psbtReadyInputs = []; for (final utxo in utxos) { - final rawTx = - await electrumClient.getTransactionHex(hash: utxo.utxo.txHash); - final publicKeyAndDerivationPath = - publicKeys[utxo.ownerDetails.address.pubKeyHash()]!; + final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash); + final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!; psbtReadyInputs.add(PSBTReadyUtxoWithAddress( utxo: utxo.utxo, @@ -275,8 +260,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { )); } - final psbt = PSBTTransactionBuild( - inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF); + final psbt = + PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF); final rawHex = await _bitcoinLedgerApp!.signPsbt(psbt: psbt.psbt); return BtcTransaction.fromRaw(BytesUtils.toHexString(rawHex)); @@ -286,17 +271,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { Future signMessage(String message, {String? address = null}) async { if (walletInfo.isHardwareWallet) { final addressEntry = address != null - ? walletAddresses.allAddresses - .firstWhere((element) => element.address == address) + ? walletAddresses.allAddresses.firstWhere((element) => element.address == address) : null; final index = addressEntry?.index ?? 0; - final isChange = addressEntry?.isHidden == true ? 1 : 0; + final isChange = addressEntry?.isChange == true ? 1 : 0; final accountPath = walletInfo.derivationInfo?.derivationPath; - final derivationPath = - accountPath != null ? "$accountPath/$isChange/$index" : null; + final derivationPath = accountPath != null ? "$accountPath/$isChange/$index" : null; - final signature = await _bitcoinLedgerApp!.signMessage( - message: ascii.encode(message), signDerivationPath: derivationPath); + final signature = await _bitcoinLedgerApp! + .signMessage(message: ascii.encode(message), signDerivationPath: derivationPath); return base64Encode(signature); } diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index 04a3cae36..a6f047fa1 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -1,7 +1,6 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/bip/bip/bip32/bip32.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:mobx/mobx.dart'; @@ -12,8 +11,7 @@ class BitcoinWalletAddresses = BitcoinWalletAddressesBase with _$BitcoinWalletAd abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with Store { BitcoinWalletAddressesBase( WalletInfo walletInfo, { - required super.mainHd, - required super.sideHd, + required super.bip32, required super.network, required super.isHardwareWallet, super.initialAddresses, @@ -21,24 +19,33 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S super.initialChangeAddressIndex, super.initialSilentAddresses, super.initialSilentAddressIndex = 0, - super.masterHd, }) : super(walletInfo); @override - String getAddress( - {required int index, required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType}) { - if (addressType == P2pkhAddressType.p2pkh) - return generateP2PKHAddress(hd: hd, index: index, network: network); - - if (addressType == SegwitAddresType.p2tr) - return generateP2TRAddress(hd: hd, index: index, network: network); - - if (addressType == SegwitAddresType.p2wsh) - return generateP2WSHAddress(hd: hd, index: index, network: network); - - if (addressType == P2shAddressType.p2wpkhInP2sh) - return generateP2SHAddress(hd: hd, index: index, network: network); - - return generateP2WPKHAddress(hd: hd, index: index, network: network); + BitcoinBaseAddress generateAddress({ + required int account, + required int index, + required Bip32Slip10Secp256k1 hd, + required BitcoinAddressType addressType, + }) { + switch (addressType) { + case P2pkhAddressType.p2pkh: + return P2pkhAddress.fromBip32(account: account, bip32: hd, index: index); + case SegwitAddresType.p2tr: + return P2trAddress.fromBip32(account: account, bip32: hd, index: index); + case SegwitAddresType.p2wsh: + return P2wshAddress.fromBip32(account: account, bip32: hd, index: index); + case P2shAddressType.p2wpkhInP2sh: + return P2shAddress.fromBip32( + account: account, + bip32: hd, + index: index, + type: P2shAddressType.p2wpkhInP2sh, + ); + case SegwitAddresType.p2wpkh: + return P2wpkhAddress.fromBip32(account: account, bip32: hd, index: index); + default: + throw ArgumentError('Invalid address type'); + } } } diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index a18c038fa..0a963bd6f 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -317,13 +317,38 @@ class ElectrumClient { Future> getHeader({required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) as Map; - BehaviorSubject? tweaksSubscribe({required int height, required int count}) { - return subscribe( - id: 'blockchain.tweaks.subscribe', - method: 'blockchain.tweaks.subscribe', - params: [height, count, false], - ); - } + BehaviorSubject? tweaksSubscribe({required int height, required int count}) => + subscribe( + id: 'blockchain.tweaks.subscribe', + method: 'blockchain.tweaks.subscribe', + params: [height, count, false], + ); + + Future tweaksRegister({ + required String secViewKey, + required String pubSpendKey, + List labels = const [], + }) => + call( + method: 'blockchain.tweaks.subscribe', + params: [secViewKey, pubSpendKey, labels], + ); + + Future tweaksErase({required String pubSpendKey}) => call( + method: 'blockchain.tweaks.erase', + params: [pubSpendKey], + ); + + BehaviorSubject? tweaksScan({required String pubSpendKey}) => subscribe( + id: 'blockchain.tweaks.scan', + method: 'blockchain.tweaks.scan', + params: [pubSpendKey], + ); + + Future tweaksGet({required String pubSpendKey}) => call( + method: 'blockchain.tweaks.get', + params: [pubSpendKey], + ); Future getTweaks({required int height}) async => await callWithTimeout(method: 'blockchain.tweaks.subscribe', params: [height, 1, false]); @@ -527,6 +552,7 @@ class ElectrumClient { _tasks[method]?.subject?.add(params.last); break; case 'blockchain.tweaks.subscribe': + case 'blockchain.tweaks.scan': final params = request['params'] as List; _tasks[_tasks.keys.first]?.subject?.add(params.last); break; diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index 1ab7799e3..f5857437c 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -22,7 +22,7 @@ class ElectrumTransactionBundle { } class ElectrumTransactionInfo extends TransactionInfo { - List? unspents; + List? unspents; bool isReceivedSilentPayment; ElectrumTransactionInfo( @@ -208,8 +208,7 @@ class ElectrumTransactionInfo extends TransactionInfo { outputAddresses.isEmpty ? [] : outputAddresses.map((e) => e.toString()).toList(), to: data['to'] as String?, unspents: unspents - .map((unspent) => - BitcoinSilentPaymentsUnspent.fromJSON(null, unspent as Map)) + .map((unspent) => BitcoinUnspent.fromJSON(null, unspent as Map)) .toList(), isReceivedSilentPayment: data['isReceivedSilentPayment'] as bool? ?? false, ); diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index c05095cf1..ee4e7d7bb 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'dart:isolate'; import 'package:bitcoin_base/bitcoin_base.dart'; -import 'package:cw_bitcoin/bitcoin_wallet.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:collection/collection.dart'; @@ -23,7 +22,6 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_bitcoin/exceptions.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/get_height_by_date.dart'; @@ -52,10 +50,9 @@ part 'electrum_wallet.g.dart'; class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet; -abstract class ElectrumWalletBase extends WalletBase< - ElectrumBalance, - ElectrumTransactionHistory, - ElectrumTransactionInfo> with Store, WalletKeysFile { +abstract class ElectrumWalletBase + extends WalletBase + with Store, WalletKeysFile { ElectrumWalletBase({ required String password, required WalletInfo walletInfo, @@ -71,8 +68,7 @@ abstract class ElectrumWalletBase extends WalletBase< ElectrumBalance? initialBalance, CryptoCurrency? currency, this.alwaysScan, - }) : accountHD = getAccountHDWallet( - currency, network, seedBytes, xpub, walletInfo.derivationInfo), + }) : bip32 = getAccountHDWallet(currency, network, seedBytes, xpub, walletInfo.derivationInfo), syncStatus = NotConnectedSyncStatus(), _password = password, _feeRates = [], @@ -107,12 +103,8 @@ abstract class ElectrumWalletBase extends WalletBase< sharedPrefs.complete(SharedPreferences.getInstance()); } - static Bip32Slip10Secp256k1 getAccountHDWallet( - CryptoCurrency? currency, - BasedUtxoNetwork network, - Uint8List? seedBytes, - String? xpub, - DerivationInfo? derivationInfo) { + static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network, + Uint8List? seedBytes, String? xpub, DerivationInfo? derivationInfo) { if (seedBytes == null && xpub == null) { throw Exception( "To create a Wallet you need either a seed or an xpub. This should not happen"); @@ -123,10 +115,7 @@ abstract class ElectrumWalletBase extends WalletBase< case CryptoCurrency.btc: case CryptoCurrency.ltc: case CryptoCurrency.tbtc: - return Bip32Slip10Secp256k1.fromSeed(seedBytes, getKeyNetVersion(network)) - .derivePath(_hardenedDerivationPath( - derivationInfo?.derivationPath ?? electrum_path)) - as Bip32Slip10Secp256k1; + return Bip32Slip10Secp256k1.fromSeed(seedBytes); case CryptoCurrency.bch: return bitcoinCashHDWallet(seedBytes); default: @@ -134,13 +123,11 @@ abstract class ElectrumWalletBase extends WalletBase< } } - return Bip32Slip10Secp256k1.fromExtendedKey( - xpub!, getKeyNetVersion(network)); + return Bip32Slip10Secp256k1.fromExtendedKey(xpub!, getKeyNetVersion(network)); } static Bip32Slip10Secp256k1 bitcoinCashHDWallet(Uint8List seedBytes) => - Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'") - as Bip32Slip10Secp256k1; + Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'") as Bip32Slip10Secp256k1; static int estimatedTransactionSize(int inputsCount, int outputsCounts) => inputsCount * 68 + outputsCounts * 34 + 10; @@ -156,13 +143,9 @@ abstract class ElectrumWalletBase extends WalletBase< bool? alwaysScan; - final Bip32Slip10Secp256k1 accountHD; + final Bip32Slip10Secp256k1 bip32; final String? _mnemonic; - Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0)); - - Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1)); - final EncryptionFileUtils encryptionFileUtils; @override @@ -193,16 +176,16 @@ abstract class ElectrumWalletBase extends WalletBase< List get scriptHashes => walletAddresses.addressesByReceiveType .where((addr) => RegexUtils.addressTypeFromStr(addr.address, network) is! MwebAddress) - .map((addr) => (addr as BitcoinAddressRecord).getScriptHash(network)) + .map((addr) => (addr as BitcoinAddressRecord).scriptHash) .toList(); List get publicScriptHashes => walletAddresses.allAddresses - .where((addr) => !addr.isHidden) + .where((addr) => !addr.isChange) .where((addr) => RegexUtils.addressTypeFromStr(addr.address, network) is! MwebAddress) - .map((addr) => addr.getScriptHash(network)) + .map((addr) => addr.scriptHash) .toList(); - String get xpub => accountHD.publicKey.toExtended; + String get xpub => bip32.publicKey.toExtended; @override String? get seed => _mnemonic; @@ -235,6 +218,36 @@ abstract class ElectrumWalletBase extends WalletBase< return isMempoolAPIEnabled; } + // @action + // Future registerSilentPaymentsKey(bool register) async { + // silentPaymentsScanningActive = active; + + // if (active) { + // syncStatus = AttemptingScanSyncStatus(); + + // final tip = await getUpdatedChainTip(); + + // if (tip == walletInfo.restoreHeight) { + // syncStatus = SyncedTipSyncStatus(tip); + // return; + // } + + // if (tip > walletInfo.restoreHeight) { + // _setListeners(walletInfo.restoreHeight, chainTipParam: _currentChainTip); + // } + // } else { + // alwaysScan = false; + + // _isolate?.then((value) => value.kill(priority: Isolate.immediate)); + + // if (electrumClient.isConnected) { + // syncStatus = SyncedSyncStatus(); + // } else { + // syncStatus = NotConnectedSyncStatus(); + // } + // } + // } + @action Future setSilentPaymentsScanning(bool active) async { silentPaymentsScanningActive = active; @@ -286,9 +299,9 @@ abstract class ElectrumWalletBase extends WalletBase< @override BitcoinWalletKeys get keys => BitcoinWalletKeys( - wif: WifEncoder.encode(hd.privateKey.raw, netVer: network.wifNetVer), - privateKey: hd.privateKey.toHex(), - publicKey: hd.publicKey.toHex(), + wif: WifEncoder.encode(bip32.privateKey.raw, netVer: network.wifNetVer), + privateKey: bip32.privateKey.toHex(), + publicKey: bip32.publicKey.toHex(), ); String _password; @@ -337,7 +350,7 @@ abstract class ElectrumWalletBase extends WalletBase< final receivePort = ReceivePort(); _isolate = Isolate.spawn( - startRefresh, + delegatedScan, ScanData( sendPort: receivePort.sendPort, silentAddress: walletAddresses.silentAddress!, @@ -351,8 +364,8 @@ abstract class ElectrumWalletBase extends WalletBase< : null, labels: walletAddresses.labels, labelIndexes: walletAddresses.silentAddresses - .where((addr) => addr.type == SilentPaymentsAddresType.p2sp && addr.index >= 1) - .map((addr) => addr.index) + .where((addr) => addr.type == SilentPaymentsAddresType.p2sp && addr.labelIndex >= 1) + .map((addr) => addr.labelIndex) .toList(), isSingleScan: doSingleScan ?? false, )); @@ -433,17 +446,17 @@ abstract class ElectrumWalletBase extends WalletBase< }); } - void _updateSilentAddressRecord(BitcoinSilentPaymentsUnspent unspent) { + void _updateSilentAddressRecord(BitcoinUnspent unspent) { + final receiveAddressRecord = unspent.bitcoinAddressRecord as BitcoinReceivedSPAddressRecord; final silentAddress = walletAddresses.silentAddress!; final silentPaymentAddress = SilentPaymentAddress( version: silentAddress.version, B_scan: silentAddress.B_scan, - B_spend: unspent.silentPaymentLabel != null + B_spend: receiveAddressRecord.labelHex != null ? silentAddress.B_spend.tweakAdd( - BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)), + BigintUtils.fromBytes(BytesUtils.fromHexString(receiveAddressRecord.labelHex!)), ) : silentAddress.B_spend, - network: network, ); final addressRecord = walletAddresses.silentAddresses @@ -652,22 +665,16 @@ abstract class ElectrumWalletBase extends WalletBase< ECPrivate? privkey; bool? isSilentPayment = false; - final hd = utx.bitcoinAddressRecord.isHidden - ? walletAddresses.sideHd - : walletAddresses.mainHd; - - if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { - final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord; - privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( - BigintUtils.fromBytes( - BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!), - ), - ); + if (utx.bitcoinAddressRecord is BitcoinReceivedSPAddressRecord) { + privkey = (utx.bitcoinAddressRecord as BitcoinReceivedSPAddressRecord).spendKey; spendsSilentPayment = true; isSilentPayment = true; } else if (!isHardwareWallet) { - privkey = - generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network); + privkey = ECPrivate.fromBip32( + bip32: walletAddresses.bip32, + account: utx.bitcoinAddressRecord.isChange ? 1 : 0, + index: utx.bitcoinAddressRecord.index, + ); } vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); @@ -682,12 +689,15 @@ abstract class ElectrumWalletBase extends WalletBase< pubKeyHex = privkey.getPublic().toHex(); } else { - pubKeyHex = hd.childKey(Bip32KeyIndex(utx.bitcoinAddressRecord.index)).publicKey.toHex(); + pubKeyHex = walletAddresses.bip32 + .childKey(Bip32KeyIndex(utx.bitcoinAddressRecord.index)) + .publicKey + .toHex(); } final derivationPath = "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? electrum_path)}" - "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}" + "/${utx.bitcoinAddressRecord.isChange ? "1" : "0"}" "/${utx.bitcoinAddressRecord.index}"; publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath); @@ -1233,8 +1243,7 @@ abstract class ElectrumWalletBase extends WalletBase< } } - void setLedgerConnection(ledger.LedgerConnection connection) => - throw UnimplementedError(); + void setLedgerConnection(ledger.LedgerConnection connection) => throw UnimplementedError(); Future buildHardwareWalletTransaction({ required List outputs, @@ -1467,13 +1476,13 @@ abstract class ElectrumWalletBase extends WalletBase< List> unspents = []; List updatedUnspentCoins = []; - unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); + unspents = await electrumClient.getListUnspent(address.scriptHash); await Future.wait(unspents.map((unspent) async { try { final coin = BitcoinUnspent.fromJSON(address, unspent); final tx = await fetchTransactionInfo(hash: coin.hash); - coin.isChange = address.isHidden; + coin.isChange = address.isChange; coin.confirmations = tx?.confirmations; updatedUnspentCoins.add(coin); @@ -1495,7 +1504,7 @@ abstract class ElectrumWalletBase extends WalletBase< value: coin.value, vout: coin.vout, isChange: coin.isChange, - isSilentPayment: coin is BitcoinSilentPaymentsUnspent, + isSilentPayment: coin.bitcoinAddressRecord is BitcoinReceivedSPAddressRecord, ); await unspentCoinsInfo.add(newInfo); @@ -1543,7 +1552,7 @@ abstract class ElectrumWalletBase extends WalletBase< final bundle = await getTransactionExpanded(hash: txId); final outputs = bundle.originalTransaction.outputs; - final changeAddresses = walletAddresses.allAddresses.where((element) => element.isHidden); + final changeAddresses = walletAddresses.allAddresses.where((element) => element.isChange); // look for a change address in the outputs final changeOutput = outputs.firstWhereOrNull((output) => changeAddresses.any( @@ -1592,12 +1601,11 @@ abstract class ElectrumWalletBase extends WalletBase< walletAddresses.allAddresses.firstWhere((element) => element.address == address); final btcAddress = RegexUtils.addressTypeFromStr(addressRecord.address, network); - final privkey = generateECPrivate( - hd: addressRecord.isHidden - ? walletAddresses.sideHd - : walletAddresses.mainHd, - index: addressRecord.index, - network: network); + final privkey = ECPrivate.fromBip32( + bip32: walletAddresses.bip32, + account: addressRecord.isChange ? 1 : 0, + index: addressRecord.index, + ); privateKeys.add(privkey); @@ -1672,7 +1680,7 @@ abstract class ElectrumWalletBase extends WalletBase< } // Identify all change outputs - final changeAddresses = walletAddresses.allAddresses.where((element) => element.isHidden); + final changeAddresses = walletAddresses.allAddresses.where((element) => element.isChange); final List changeOutputs = outputs .where((output) => changeAddresses .any((element) => element.address == output.address.toAddress(network))) @@ -1777,8 +1785,7 @@ abstract class ElectrumWalletBase extends WalletBase< if (height != null) { if (time == null && height > 0) { - time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000) - .round(); + time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000).round(); } if (confirmations == null) { @@ -1879,8 +1886,8 @@ abstract class ElectrumWalletBase extends WalletBase< BitcoinAddressType type, ) async { final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type); - final hiddenAddresses = addressesByType.where((addr) => addr.isHidden == true); - final receiveAddresses = addressesByType.where((addr) => addr.isHidden == false); + final hiddenAddresses = addressesByType.where((addr) => addr.isChange == true); + final receiveAddresses = addressesByType.where((addr) => addr.isChange == false); walletAddresses.hiddenAddresses.addAll(hiddenAddresses.map((e) => e.address)); await walletAddresses.saveAddressesInBox(); await Future.wait(addressesByType.map((addressRecord) async { @@ -1890,10 +1897,10 @@ abstract class ElectrumWalletBase extends WalletBase< addressRecord.txCount = history.length; historiesWithDetails.addAll(history); - final matchedAddresses = addressRecord.isHidden ? hiddenAddresses : receiveAddresses; + final matchedAddresses = addressRecord.isChange ? hiddenAddresses : receiveAddresses; final isUsedAddressUnderGap = matchedAddresses.toList().indexOf(addressRecord) >= matchedAddresses.length - - (addressRecord.isHidden + (addressRecord.isChange ? ElectrumWalletAddressesBase.defaultChangeAddressesCount : ElectrumWalletAddressesBase.defaultReceiveAddressesCount); @@ -1903,7 +1910,7 @@ abstract class ElectrumWalletBase extends WalletBase< // Discover new addresses for the same address type until the gap limit is respected await walletAddresses.discoverAddresses( matchedAddresses.toList(), - addressRecord.isHidden, + addressRecord.isChange, (address) async { await subscribeForUpdates(); return _fetchAddressHistory(address, await getCurrentChainTip()) @@ -1929,7 +1936,7 @@ abstract class ElectrumWalletBase extends WalletBase< try { final Map historiesWithDetails = {}; - final history = await electrumClient.getHistory(addressRecord.getScriptHash(network)); + final history = await electrumClient.getHistory(addressRecord.scriptHash); if (history.isNotEmpty) { addressRecord.setAsUsed(); @@ -2010,12 +2017,12 @@ abstract class ElectrumWalletBase extends WalletBase< Future subscribeForUpdates() async { final unsubscribedScriptHashes = walletAddresses.allAddresses.where( (address) => - !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) && + !_scripthashesUpdateSubject.containsKey(address.scriptHash) && address.type != SegwitAddresType.mweb, ); await Future.wait(unsubscribedScriptHashes.map((address) async { - final sh = address.getScriptHash(network); + final sh = address.scriptHash; if (!(_scripthashesUpdateSubject[sh]?.isClosed ?? true)) { try { await _scripthashesUpdateSubject[sh]?.close(); @@ -2054,7 +2061,7 @@ abstract class ElectrumWalletBase extends WalletBase< final balanceFutures = >>[]; for (var i = 0; i < addresses.length; i++) { final addressRecord = addresses[i]; - final sh = addressRecord.getScriptHash(network); + final sh = addressRecord.scriptHash; final balanceFuture = electrumClient.getBalance(sh); balanceFutures.add(balanceFuture); } @@ -2095,15 +2102,17 @@ abstract class ElectrumWalletBase extends WalletBase< for (var i = 0; i < balances.length; i++) { final addressRecord = addresses[i]; final balance = balances[i]; - final confirmed = balance['confirmed'] as int? ?? 0; - final unconfirmed = balance['unconfirmed'] as int? ?? 0; - totalConfirmed += confirmed; - totalUnconfirmed += unconfirmed; + try { + final confirmed = balance['confirmed'] as int? ?? 0; + final unconfirmed = balance['unconfirmed'] as int? ?? 0; + totalConfirmed += confirmed; + totalUnconfirmed += unconfirmed; - addressRecord.balance = confirmed + unconfirmed; - if (confirmed > 0 || unconfirmed > 0) { - addressRecord.setAsUsed(); - } + addressRecord.balance = confirmed + unconfirmed; + if (confirmed > 0 || unconfirmed > 0) { + addressRecord.setAsUsed(); + } + } catch (_) {} } return ElectrumBalance( @@ -2124,10 +2133,17 @@ abstract class ElectrumWalletBase extends WalletBase< @override Future signMessage(String message, {String? address = null}) async { - final index = address != null - ? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index - : null; - final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index)); + Bip32Slip10Secp256k1 HD = bip32; + + final record = walletAddresses.allAddresses.firstWhere((element) => element.address == address); + + if (record.isChange) { + HD = HD.childKey(Bip32KeyIndex(1)); + } else { + HD = HD.childKey(Bip32KeyIndex(0)); + } + + HD = HD.childKey(Bip32KeyIndex(record.index)); final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex()); String messagePrefix = '\x18Bitcoin Signed Message:\n'; @@ -2223,7 +2239,6 @@ abstract class ElectrumWalletBase extends WalletBase< @action void _onConnectionStatusChange(ConnectionStatus status) { - switch (status) { case ConnectionStatus.connected: if (syncStatus is NotConnectedSyncStatus || @@ -2396,6 +2411,137 @@ class SyncResponse { SyncResponse(this.height, this.syncStatus); } +Future delegatedScan(ScanData scanData) async { + int syncHeight = scanData.height; + int initialSyncHeight = syncHeight; + + BehaviorSubject? tweaksSubscription = null; + + final electrumClient = scanData.electrumClient; + await electrumClient.connectToUri( + scanData.node?.uri ?? Uri.parse("tcp://electrs.cakewallet.com:50001"), + useSSL: scanData.node?.useSSL ?? false, + ); + + if (tweaksSubscription == null) { + scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight))); + + tweaksSubscription = await electrumClient.tweaksScan( + pubSpendKey: scanData.silentAddress.B_spend.toHex(), + ); + + Future listenFn(t) async { + final tweaks = t as Map; + final msg = tweaks["message"]; + + // success or error msg + final noData = msg != null; + if (noData) { + return; + } + + // Continuous status UI update, send how many blocks left to scan + final syncingStatus = scanData.isSingleScan + ? SyncingSyncStatus(1, 0) + : SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight); + scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); + + final blockHeight = tweaks.keys.first; + final tweakHeight = int.parse(blockHeight); + + try { + final blockTweaks = tweaks[blockHeight] as Map; + + for (var j = 0; j < blockTweaks.keys.length; j++) { + final txid = blockTweaks.keys.elementAt(j); + final details = blockTweaks[txid] as Map; + final outputPubkeys = (details["output_pubkeys"] as Map); + final spendingKey = details["spending_key"].toString(); + + try { + // placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s) + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: txid, + height: tweakHeight, + amount: 0, + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + isReplaced: false, + date: scanData.network == BitcoinNetwork.mainnet + ? getDateByBitcoinHeight(tweakHeight) + : DateTime.now(), + confirmations: scanData.chainTip - tweakHeight + 1, + unspents: [], + isReceivedSilentPayment: true, + ); + + outputPubkeys.forEach((pos, value) { + final secKey = ECPrivate.fromHex(spendingKey); + final receivingOutputAddress = + secKey.getPublic().toTaprootAddress(tweak: false).toAddress(scanData.network); + + late int amount; + try { + amount = int.parse(value[1].toString()); + } catch (_) { + return; + } + + final receivedAddressRecord = BitcoinReceivedSPAddressRecord( + receivingOutputAddress, + labelIndex: 0, + isUsed: true, + spendKey: secKey, + txCount: 1, + balance: amount, + ); + + final unspent = BitcoinUnspent( + receivedAddressRecord, + txid, + amount, + int.parse(pos.toString()), + ); + + txInfo.unspents!.add(unspent); + txInfo.amount += unspent.value; + }); + + scanData.sendPort.send({txInfo.id: txInfo}); + } catch (_) {} + } + } catch (_) {} + + syncHeight = tweakHeight; + + if (tweakHeight >= scanData.chainTip || scanData.isSingleScan) { + if (tweakHeight >= scanData.chainTip) + scanData.sendPort.send(SyncResponse( + syncHeight, + SyncedTipSyncStatus(scanData.chainTip), + )); + + if (scanData.isSingleScan) { + scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus())); + } + + await tweaksSubscription!.close(); + await electrumClient.close(); + } + } + + tweaksSubscription?.listen(listenFn); + } + + if (tweaksSubscription == null) { + return scanData.sendPort.send( + SyncResponse(syncHeight, UnsupportedSyncStatus()), + ); + } +} + Future startRefresh(ScanData scanData) async { int syncHeight = scanData.height; int initialSyncHeight = syncHeight; @@ -2528,26 +2674,18 @@ Future startRefresh(ScanData scanData) async { return false; }); - final receivedAddressRecord = BitcoinSilentPaymentAddressRecord( + final receivedAddressRecord = BitcoinReceivedSPAddressRecord( receivingOutputAddress, - index: 0, - isHidden: false, + labelIndex: 0, isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k, - type: SegwitAddresType.p2tr, + spendKey: scanData.silentAddress.b_spend.tweakAdd( + BigintUtils.fromBytes(BytesUtils.fromHexString(t_k)), + ), txCount: 1, balance: amount!, ); - final unspent = BitcoinSilentPaymentsUnspent( - receivedAddressRecord, - txid, - amount!, - pos!, - silentPaymentTweak: t_k, - silentPaymentLabel: label == "None" ? null : label, - ); + final unspent = BitcoinUnspent(receivedAddressRecord, txid, amount!, pos!); txInfo.unspents!.add(unspent); txInfo.amount += unspent.value; diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index c29579436..68de355bd 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -33,8 +33,7 @@ const List BITCOIN_CASH_ADDRESS_TYPES = [ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { ElectrumWalletAddressesBase( WalletInfo walletInfo, { - required this.mainHd, - required this.sideHd, + required this.bip32, required this.network, required this.isHardwareWallet, List? initialAddresses, @@ -43,17 +42,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { List? initialSilentAddresses, int initialSilentAddressIndex = 0, List? initialMwebAddresses, - Bip32Slip10Secp256k1? masterHd, BitcoinAddressType? initialAddressPageType, - }) : _addresses = ObservableList.of((initialAddresses ?? []).toSet()), addressesByReceiveType = ObservableList.of(([]).toSet()), receiveAddresses = ObservableList.of((initialAddresses ?? []) - .where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed) + .where((addressRecord) => !addressRecord.isChange && !addressRecord.isUsed) .toSet()), changeAddresses = ObservableList.of((initialAddresses ?? []) - .where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed) + .where((addressRecord) => addressRecord.isChange && !addressRecord.isUsed) .toSet()), currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {}, currentChangeAddressIndexByType = initialChangeAddressIndex ?? {}, @@ -67,33 +64,25 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { mwebAddresses = ObservableList.of((initialMwebAddresses ?? []).toSet()), super(walletInfo) { - if (masterHd != null) { - silentAddress = SilentPaymentOwner.fromPrivateKeys( - b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privateKey.toHex()), - b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privateKey.toHex()), - network: network, - ); - - if (silentAddresses.length == 0) { - silentAddresses.add(BitcoinSilentPaymentAddressRecord( - silentAddress.toString(), - index: 0, - isHidden: false, - name: "", - silentPaymentTweak: null, - network: network, - type: SilentPaymentsAddresType.p2sp, - )); - silentAddresses.add(BitcoinSilentPaymentAddressRecord( - silentAddress!.toLabeledSilentPaymentAddress(0).toString(), - index: 0, - isHidden: true, - name: "", - silentPaymentTweak: BytesUtils.toHexString(silentAddress!.generateLabel(0)), - network: network, - type: SilentPaymentsAddresType.p2sp, - )); - } + silentAddress = SilentPaymentOwner.fromPrivateKeys( + b_scan: ECPrivate.fromHex(bip32.derive(SCAN_PATH).privateKey.toHex()), + b_spend: ECPrivate.fromHex(bip32.derive(SPEND_PATH).privateKey.toHex()), + network: network, + ); + if (silentAddresses.length == 0) { + silentAddresses.add(BitcoinSilentPaymentAddressRecord( + silentAddress.toString(), + labelIndex: 1, + name: "", + type: SilentPaymentsAddresType.p2sp, + )); + silentAddresses.add(BitcoinSilentPaymentAddressRecord( + silentAddress!.toLabeledSilentPaymentAddress(0).toString(), + name: "", + labelIndex: 0, + labelHex: BytesUtils.toHexString(silentAddress!.generateLabel(0)), + type: SilentPaymentsAddresType.p2sp, + )); } updateAddressesByMatch(); @@ -112,9 +101,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { // TODO: add this variable in `litecoin_wallet_addresses` and just add a cast in cw_bitcoin to use it final ObservableList mwebAddresses; final BasedUtxoNetwork network; - final Bip32Slip10Secp256k1 mainHd; - final Bip32Slip10Secp256k1 sideHd; - final bool isHardwareWallet; + final Bip32Slip10Secp256k1 bip32; @observable SilentPaymentOwner? silentAddress; @@ -174,31 +161,37 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return; } if (addressPageType == SilentPaymentsAddresType.p2sp) { - final selected = silentAddresses.firstWhere((addressRecord) => addressRecord.address == addr); + late BitcoinSilentPaymentAddressRecord selected; + try { + selected = silentAddresses.firstWhere((addressRecord) => addressRecord.address == addr); + } catch (_) { + selected = silentAddresses[0]; + } - if (selected.silentPaymentTweak != null && silentAddress != null) { + if (selected.labelHex != null && silentAddress != null) { activeSilentAddress = - silentAddress!.toLabeledSilentPaymentAddress(selected.index).toString(); + silentAddress!.toLabeledSilentPaymentAddress(selected.labelIndex).toString(); } else { activeSilentAddress = silentAddress!.toString(); } return; } try { - final addressRecord = _addresses.firstWhere( - (addressRecord) => addressRecord.address == addr, - ); + final addressRecord = _addresses.firstWhere( + (addressRecord) => addressRecord.address == addr, + ); - previousAddressRecord = addressRecord; - receiveAddresses.remove(addressRecord); - receiveAddresses.insert(0, addressRecord); + previousAddressRecord = addressRecord; + receiveAddresses.remove(addressRecord); + receiveAddresses.insert(0, addressRecord); } catch (e) { print("ElectrumWalletAddressBase: set address ($addr): $e"); } } @override - String get primaryAddress => getAddress(index: 0, hd: mainHd, addressType: addressPageType); + String get primaryAddress => + getAddress(account: 0, index: 0, hd: bip32, addressType: addressPageType); Map currentReceiveAddressIndexByType; @@ -223,7 +216,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @computed int get totalCountOfReceiveAddresses => addressesByReceiveType.fold(0, (acc, addressRecord) { - if (!addressRecord.isHidden) { + if (!addressRecord.isChange) { return acc + 1; } return acc; @@ -231,7 +224,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @computed int get totalCountOfChangeAddresses => addressesByReceiveType.fold(0, (acc, addressRecord) { - if (addressRecord.isHidden) { + if (addressRecord.isChange) { return acc + 1; } return acc; @@ -272,7 +265,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } @action - Future getChangeAddress({List? inputs, List? outputs, bool isPegIn = false}) async { + Future getChangeAddress( + {List? inputs, List? outputs, bool isPegIn = false}) async { updateChangeAddresses(); if (changeAddresses.isEmpty) { @@ -297,7 +291,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final labels = {}; for (int i = 0; i < silentAddresses.length; i++) { final silentAddressRecord = silentAddresses[i]; - final silentPaymentTweak = silentAddressRecord.silentPaymentTweak; + final silentPaymentTweak = silentAddressRecord.labelHex; if (silentPaymentTweak != null && SilentPaymentAddress.regex.hasMatch(silentAddressRecord.address)) { @@ -321,12 +315,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final address = BitcoinSilentPaymentAddressRecord( silentAddress!.toLabeledSilentPaymentAddress(currentSilentAddressIndex).toString(), - index: currentSilentAddressIndex, - isHidden: false, + labelIndex: currentSilentAddressIndex, name: label, - silentPaymentTweak: - BytesUtils.toHexString(silentAddress!.generateLabel(currentSilentAddressIndex)), - network: network, + labelHex: BytesUtils.toHexString(silentAddress!.generateLabel(currentSilentAddressIndex)), type: SilentPaymentsAddresType.p2sp, ); @@ -337,12 +328,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } final newAddressIndex = addressesByReceiveType.fold( - 0, (int acc, addressRecord) => addressRecord.isHidden == false ? acc + 1 : acc); + 0, (int acc, addressRecord) => addressRecord.isChange == false ? acc + 1 : acc); final address = BitcoinAddressRecord( - getAddress(index: newAddressIndex, hd: mainHd, addressType: addressPageType), + getAddress(account: 0, index: newAddressIndex, hd: bip32, addressType: addressPageType), index: newAddressIndex, - isHidden: false, + isChange: false, name: label, type: addressPageType, network: network, @@ -352,19 +343,32 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return address; } - String getAddress({ + BitcoinBaseAddress generateAddress({ + required int account, required int index, required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType, - }) => - ''; + required BitcoinAddressType addressType, + }) { + throw UnimplementedError(); + } + + String getAddress({ + required int account, + required int index, + required Bip32Slip10Secp256k1 hd, + required BitcoinAddressType addressType, + }) { + return generateAddress(account: account, index: index, hd: hd, addressType: addressType) + .toAddress(network); + } Future getAddressAsync({ + required int account, required int index, required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType, + required BitcoinAddressType addressType, }) async => - getAddress(index: index, hd: hd, addressType: addressType); + getAddress(account: account, index: index, hd: hd, addressType: addressType); void addBitcoinAddressTypes() { final lastP2wpkh = _addresses @@ -411,7 +415,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } silentAddresses.forEach((addressRecord) { - if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isHidden) { + if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isChange) { return; } @@ -537,7 +541,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void updateReceiveAddresses() { receiveAddresses.removeRange(0, receiveAddresses.length); final newAddresses = - _addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed); + _addresses.where((addressRecord) => !addressRecord.isChange && !addressRecord.isUsed); receiveAddresses.addAll(newAddresses); } @@ -545,7 +549,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void updateChangeAddresses() { changeAddresses.removeRange(0, changeAddresses.length); final newAddresses = _addresses.where((addressRecord) => - addressRecord.isHidden && + addressRecord.isChange && !addressRecord.isUsed && // TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh)); @@ -575,7 +579,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { _addresses.forEach((addr) { if (addr.type == type) { - if (addr.isHidden) { + if (addr.isChange) { countOfHiddenAddresses += 1; return; } @@ -605,9 +609,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { for (var i = startIndex; i < count + startIndex; i++) { final address = BitcoinAddressRecord( - await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType), + await getAddressAsync( + account: _getAccount(isHidden), + index: i, + hd: bip32, + addressType: type ?? addressPageType), index: i, - isHidden: isHidden, + isChange: isHidden, type: type ?? addressPageType, network: network, ); @@ -650,14 +658,24 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { // this would add a ton of startup lag for mweb addresses since we have 1000 of them return; } - if (!element.isHidden && + if (!element.isChange && element.address != - await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) { - element.isHidden = true; - } else if (element.isHidden && + await getAddressAsync( + account: 0, + index: element.index, + hd: bip32, + addressType: element.type, + )) { + element.isChange = true; + } else if (element.isChange && element.address != - await getAddressAsync(index: element.index, hd: sideHd, addressType: element.type)) { - element.isHidden = false; + await getAddressAsync( + account: 1, + index: element.index, + hd: bip32, + addressType: element.type, + )) { + element.isChange = false; } }); } @@ -674,12 +692,11 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return _isAddressByType(addressRecord, addressPageType); } - Bip32Slip10Secp256k1 _getHd(bool isHidden) => isHidden ? sideHd : mainHd; - + int _getAccount(bool isHidden) => isHidden ? 1 : 0; bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type; bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => - !addr.isHidden && !addr.isUsed && addr.type == type; + !addr.isChange && !addr.isUsed && addr.type == type; @action void deleteSilentPaymentAddress(String address) { diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 990719089..f7c2e1a28 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -6,7 +6,6 @@ import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_type.dart'; class ElectrumWalletSnapshot { @@ -68,7 +67,7 @@ class ElectrumWalletSnapshot { final addressesTmp = data['addresses'] as List? ?? []; final addresses = addressesTmp .whereType() - .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network)) + .map((addr) => BitcoinAddressRecord.fromJSON(addr)) .toList(); final silentAddressesTmp = data['silent_addresses'] as List? ?? []; @@ -80,7 +79,7 @@ class ElectrumWalletSnapshot { final mwebAddressTmp = data['mweb_addresses'] as List? ?? []; final mwebAddresses = mwebAddressTmp .whereType() - .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network)) + .map((addr) => BitcoinAddressRecord.fromJSON(addr)) .toList(); final alwaysScan = data['alwaysScan'] as bool? ?? false; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 1fb39c878..1ca89e00a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -21,7 +21,6 @@ import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -98,8 +97,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, initialMwebAddresses: initialMwebAddresses, - mainHd: hd, - sideHd: accountHD.childKey(Bip32KeyIndex(1)), + bip32: bip32, network: network, mwebHd: mwebHd, mwebEnabled: mwebEnabled, @@ -1025,12 +1023,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { witnesses: tx2.inputs.asMap().entries.map((e) { final utxo = unspentCoins .firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); - final key = generateECPrivate( - hd: utxo.bitcoinAddressRecord.isHidden - ? walletAddresses.sideHd - : walletAddresses.mainHd, - index: utxo.bitcoinAddressRecord.index, - network: network); + final key = ECPrivate.fromBip32( + bip32: walletAddresses.bip32, + account: utxo.bitcoinAddressRecord.isChange ? 1 : 0, + index: utxo.bitcoinAddressRecord.index, + ); final digest = tx2.getTransactionSegwitDigit( txInIndex: e.key, script: key.getPublic().toP2pkhAddress().toScriptPubKey(), @@ -1113,10 +1110,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override Future signMessage(String message, {String? address = null}) async { - final index = address != null - ? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index - : null; - final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index)); + Bip32Slip10Secp256k1 HD = bip32; + + final record = walletAddresses.allAddresses.firstWhere((element) => element.address == address); + + if (record.isChange) { + HD = HD.childKey(Bip32KeyIndex(1)); + } else { + HD = HD.childKey(Bip32KeyIndex(0)); + } + + HD = HD.childKey(Bip32KeyIndex(record.index)); final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex()); final privateKey = ECDSAPrivateKey.fromBytes( diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index c55f5fc76..b6e7b4428 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -7,7 +7,6 @@ import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_mweb/cw_mweb.dart'; @@ -23,8 +22,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store { LitecoinWalletAddressesBase( WalletInfo walletInfo, { - required super.mainHd, - required super.sideHd, + required super.bip32, required super.network, required super.isHardwareWallet, required this.mwebHd, @@ -121,30 +119,34 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses await ensureMwebAddressUpToIndexExists(20); return; } - } - @override - String getAddress({ - required int index, - required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType, - }) { - if (addressType == SegwitAddresType.mweb) { - return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; + @override + BitcoinBaseAddress generateAddress({ + required int account, + required int index, + required Bip32Slip10Secp256k1 hd, + required BitcoinAddressType addressType, + }) { + if (addressType == SegwitAddresType.mweb) { + return MwebAddress.fromAddress(address: mwebAddrs[0], network: network); + } + + return P2wpkhAddress.fromBip32(account: account, bip32: hd, index: index); } - return generateP2WPKHAddress(hd: hd, index: index, network: network); } @override Future getAddressAsync({ + required int account, required int index, required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType, + required BitcoinAddressType addressType, }) async { if (addressType == SegwitAddresType.mweb) { await ensureMwebAddressUpToIndexExists(index); } - return getAddress(index: index, hd: hd, addressType: addressType); + + return getAddress(account: account, index: index, hd: hd, addressType: addressType); } @action diff --git a/cw_bitcoin/lib/utils.dart b/cw_bitcoin/lib/utils.dart deleted file mode 100644 index a7435bed1..000000000 --- a/cw_bitcoin/lib/utils.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:bitcoin_base/bitcoin_base.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; - -ECPrivate generateECPrivate({ - required Bip32Slip10Secp256k1 hd, - required BasedUtxoNetwork network, - required int index, -}) => - ECPrivate(hd.childKey(Bip32KeyIndex(index)).privateKey); - -String generateP2WPKHAddress({ - required Bip32Slip10Secp256k1 hd, - required BasedUtxoNetwork network, - required int index, -}) => - ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey) - .toP2wpkhAddress() - .toAddress(network); - -String generateP2SHAddress({ - required Bip32Slip10Secp256k1 hd, - required BasedUtxoNetwork network, - required int index, -}) => - ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey) - .toP2wpkhInP2sh() - .toAddress(network); - -String generateP2WSHAddress({ - required Bip32Slip10Secp256k1 hd, - required BasedUtxoNetwork network, - required int index, -}) => - ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey) - .toP2wshAddress() - .toAddress(network); - -String generateP2PKHAddress({ - required Bip32Slip10Secp256k1 hd, - required BasedUtxoNetwork network, - required int index, -}) => - ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey) - .toP2pkhAddress() - .toAddress(network); - -String generateP2TRAddress({ - required Bip32Slip10Secp256k1 hd, - required BasedUtxoNetwork network, - required int index, -}) => - ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey) - .toTaprootAddress() - .toAddress(network); diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart index d55914dcd..768c3fb4b 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart @@ -54,8 +54,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, - mainHd: hd, - sideHd: accountHD.childKey(Bip32KeyIndex(1)), + bip32: bip32, network: network, initialAddressPageType: addressPageType, isHardwareWallet: walletInfo.isHardwareWallet, @@ -141,7 +140,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { return BitcoinAddressRecord( addr.address, index: addr.index, - isHidden: addr.isHidden, + isChange: addr.isChange, type: P2pkhAddressType.p2pkh, network: BitcoinCashNetwork.mainnet, ); @@ -149,7 +148,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { return BitcoinAddressRecord( AddressUtils.getCashAddrFormat(addr.address), index: addr.index, - isHidden: addr.isHidden, + isChange: addr.isChange, type: P2pkhAddressType.p2pkh, network: BitcoinCashNetwork.mainnet, ); @@ -209,13 +208,17 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { @override Future signMessage(String message, {String? address = null}) async { - int? index; - try { - index = address != null - ? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index - : null; - } catch (_) {} - final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index)); + Bip32Slip10Secp256k1 HD = bip32; + + final record = walletAddresses.allAddresses.firstWhere((element) => element.address == address); + + if (record.isChange) { + HD = HD.childKey(Bip32KeyIndex(1)); + } else { + HD = HD.childKey(Bip32KeyIndex(0)); + } + + HD = HD.childKey(Bip32KeyIndex(record.index)); final priv = ECPrivate.fromWif( WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer), netVersion: network.wifNetVer, diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart index fe0ebc828..ae195bf6b 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart @@ -1,7 +1,6 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:mobx/mobx.dart'; @@ -12,8 +11,7 @@ class BitcoinCashWalletAddresses = BitcoinCashWalletAddressesBase with _$Bitcoin abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses with Store { BitcoinCashWalletAddressesBase( WalletInfo walletInfo, { - required super.mainHd, - required super.sideHd, + required super.bip32, required super.network, required super.isHardwareWallet, super.initialAddresses, @@ -23,9 +21,11 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi }) : super(walletInfo); @override - String getAddress( - {required int index, - required Bip32Slip10Secp256k1 hd, - BitcoinAddressType? addressType}) => - generateP2PKHAddress(hd: hd, index: index, network: network); + BitcoinBaseAddress generateAddress({ + required int account, + required int index, + required Bip32Slip10Secp256k1 hd, + required BitcoinAddressType addressType, + }) => + P2pkhAddress.fromBip32(account: account, bip32: hd, index: index); } diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 60364c289..cc7e97cd9 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -144,7 +144,7 @@ class CWBitcoin extends Bitcoin { address: addr.address, txCount: addr.txCount, balance: addr.balance, - isChange: addr.isHidden)) + isChange: addr.isChange)) .toList(); } @@ -379,16 +379,16 @@ class CWBitcoin extends Bitcoin { String? address; switch (dInfoCopy.scriptType) { case "p2wpkh": - address = generateP2WPKHAddress(hd: hd, network: network, index: 0); + address = P2wpkhAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network); break; case "p2pkh": - address = generateP2PKHAddress(hd: hd, network: network, index: 0); + address = P2pkhAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network); break; case "p2wpkh-p2sh": - address = generateP2SHAddress(hd: hd, network: network, index: 0); + address = P2shAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network); break; case "p2tr": - address = generateP2TRAddress(hd: hd, network: network, index: 0); + address = P2trAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network); break; default: continue; @@ -526,7 +526,7 @@ class CWBitcoin extends Bitcoin { address: addr.address, txCount: addr.txCount, balance: addr.balance, - isChange: addr.isHidden)) + isChange: addr.isChange)) .toList(); } @@ -541,7 +541,7 @@ class CWBitcoin extends Bitcoin { address: addr.address, txCount: addr.txCount, balance: addr.balance, - isChange: addr.isHidden)) + isChange: addr.isChange)) .toList(); } @@ -574,6 +574,11 @@ class CWBitcoin extends Bitcoin { return bitcoinWallet.isTestnet; } + @override + Future registerSilentPaymentsKey(Object wallet, bool active) async { + return; + } + @override Future checkIfMempoolAPIIsEnabled(Object wallet) async { final bitcoinWallet = wallet as ElectrumWallet; diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 4fbe358e5..b1aa33a10 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -48,6 +48,7 @@ class PreferencesKey { static const customBitcoinFeeRate = 'custom_electrum_fee_rate'; static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay'; static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan'; + static const silentPaymentsKeyRegistered = 'silentPaymentsKeyRegistered'; static const mwebCardDisplay = 'mwebCardDisplay'; static const mwebEnabled = 'mwebEnabled'; static const hasEnabledMwebBefore = 'hasEnabledMwebBefore'; diff --git a/lib/src/screens/receive/widgets/address_list.dart b/lib/src/screens/receive/widgets/address_list.dart index 9f15018d0..0d5805e52 100644 --- a/lib/src/screens/receive/widgets/address_list.dart +++ b/lib/src/screens/receive/widgets/address_list.dart @@ -1,6 +1,3 @@ - -import 'dart:math'; - import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; @@ -37,7 +34,6 @@ class AddressList extends StatefulWidget { } class _AddressListState extends State { - bool showHiddenAddresses = false; void _toggleHiddenAddresses() { @@ -131,9 +127,10 @@ class _AddressListState extends State { showTrailingButton: widget.addressListViewModel.showAddManualAddresses, showSearchButton: true, onSearchCallback: updateItems, - trailingButtonTap: () => Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) { - updateItems(); // refresh the new address - }), + trailingButtonTap: () => + Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) { + updateItems(); // refresh the new address + }), trailingIcon: Icon( Icons.add, size: 20, @@ -148,7 +145,8 @@ class _AddressListState extends State { cell = Container(); } else { cell = Observer(builder: (_) { - final isCurrent = item.address == widget.addressListViewModel.address.address && editable; + final isCurrent = + item.address == widget.addressListViewModel.address.address && editable; final backgroundColor = isCurrent ? Theme.of(context).extension()!.currentTileBackgroundColor : Theme.of(context).extension()!.tilesBackgroundColor; @@ -156,17 +154,17 @@ class _AddressListState extends State { ? Theme.of(context).extension()!.currentTileTextColor : Theme.of(context).extension()!.tilesTextColor; - return AddressCell.fromItem( item, isCurrent: isCurrent, hasBalance: widget.addressListViewModel.isBalanceAvailable, hasReceived: widget.addressListViewModel.isReceivedAvailable, - // hasReceived: - backgroundColor: (kDebugMode && item.isHidden) ? - Theme.of(context).colorScheme.error : - (kDebugMode && item.isManual) ? Theme.of(context).colorScheme.error.withBlue(255) : - backgroundColor, + // hasReceived: + backgroundColor: (kDebugMode && item.isHidden) + ? Theme.of(context).colorScheme.error + : (kDebugMode && item.isManual) + ? Theme.of(context).colorScheme.error.withBlue(255) + : backgroundColor, textColor: textColor, onTap: (_) { if (widget.onSelect != null) { @@ -176,9 +174,11 @@ class _AddressListState extends State { widget.addressListViewModel.setAddress(item); }, onEdit: editable - ? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item).then((value) { - updateItems(); // refresh the new address - }) + ? () => Navigator.of(context) + .pushNamed(Routes.newSubaddress, arguments: item) + .then((value) { + updateItems(); // refresh the new address + }) : null, isHidden: item.isHidden, onHide: () => _hideAddress(item), @@ -190,8 +190,8 @@ class _AddressListState extends State { return index != 0 ? cell : ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(30), topRight: Radius.circular(30)), + borderRadius: + BorderRadius.only(topLeft: Radius.circular(30), topRight: Radius.circular(30)), child: cell, ); }, @@ -202,5 +202,4 @@ class _AddressListState extends State { await widget.addressListViewModel.toggleHideAddress(item); updateItems(); } - } diff --git a/lib/src/screens/settings/silent_payments_settings.dart b/lib/src/screens/settings/silent_payments_settings.dart index bc0ecece1..d2a4f3600 100644 --- a/lib/src/screens/settings/silent_payments_settings.dart +++ b/lib/src/screens/settings/silent_payments_settings.dart @@ -37,6 +37,13 @@ class SilentPaymentsSettingsPage extends BasePage { _silentPaymentsSettingsViewModel.setSilentPaymentsAlwaysScan(value); }, ), + SettingsSwitcherCell( + title: S.current.silent_payments_register_key, + value: _silentPaymentsSettingsViewModel.silentPaymentsAlwaysScan, + onValueChange: (_, bool value) { + _silentPaymentsSettingsViewModel.setSilentPaymentsAlwaysScan(value); + }, + ), SettingsCellWithArrow( title: S.current.silent_payments_scanning, handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan), diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 9f03c95c3..cd39318f4 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -114,6 +114,7 @@ abstract class SettingsStoreBase with Store { required this.customBitcoinFeeRate, required this.silentPaymentsCardDisplay, required this.silentPaymentsAlwaysScan, + required this.silentPaymentsKeyRegistered, required this.mwebAlwaysScan, required this.mwebCardDisplay, required this.mwebEnabled, @@ -344,8 +345,8 @@ abstract class SettingsStoreBase with Store { reaction( (_) => bitcoinSeedType, - (BitcoinSeedType bitcoinSeedType) => sharedPreferences.setInt( - PreferencesKey.bitcoinSeedType, bitcoinSeedType.raw)); + (BitcoinSeedType bitcoinSeedType) => + sharedPreferences.setInt(PreferencesKey.bitcoinSeedType, bitcoinSeedType.raw)); reaction( (_) => nanoSeedType, @@ -428,8 +429,10 @@ abstract class SettingsStoreBase with Store { reaction((_) => useTronGrid, (bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid)); - reaction((_) => useMempoolFeeAPI, - (bool useMempoolFeeAPI) => _sharedPreferences.setBool(PreferencesKey.useMempoolFeeAPI, useMempoolFeeAPI)); + reaction( + (_) => useMempoolFeeAPI, + (bool useMempoolFeeAPI) => + _sharedPreferences.setBool(PreferencesKey.useMempoolFeeAPI, useMempoolFeeAPI)); reaction((_) => defaultNanoRep, (String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep)); @@ -559,6 +562,11 @@ abstract class SettingsStoreBase with Store { (bool silentPaymentsAlwaysScan) => _sharedPreferences.setBool( PreferencesKey.silentPaymentsAlwaysScan, silentPaymentsAlwaysScan)); + reaction( + (_) => silentPaymentsKeyRegistered, + (bool silentPaymentsKeyRegistered) => _sharedPreferences.setBool( + PreferencesKey.silentPaymentsKeyRegistered, silentPaymentsKeyRegistered)); + reaction( (_) => mwebAlwaysScan, (bool mwebAlwaysScan) => @@ -790,6 +798,9 @@ abstract class SettingsStoreBase with Store { @observable bool silentPaymentsAlwaysScan; + @observable + bool silentPaymentsKeyRegistered; + @observable bool mwebAlwaysScan; @@ -959,6 +970,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; final silentPaymentsAlwaysScan = sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; + final silentPaymentsKeyRegistered = + sharedPreferences.getBool(PreferencesKey.silentPaymentsKeyRegistered) ?? false; final mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; final mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; final mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; @@ -1230,6 +1243,7 @@ abstract class SettingsStoreBase with Store { customBitcoinFeeRate: customBitcoinFeeRate, silentPaymentsCardDisplay: silentPaymentsCardDisplay, silentPaymentsAlwaysScan: silentPaymentsAlwaysScan, + silentPaymentsKeyRegistered: silentPaymentsKeyRegistered, mwebAlwaysScan: mwebAlwaysScan, mwebCardDisplay: mwebCardDisplay, mwebEnabled: mwebEnabled, @@ -1396,6 +1410,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; silentPaymentsAlwaysScan = sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false; + silentPaymentsKeyRegistered = + sharedPreferences.getBool(PreferencesKey.silentPaymentsKeyRegistered) ?? false; mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; @@ -1658,7 +1674,8 @@ abstract class SettingsStoreBase with Store { deviceName = windowsInfo.productName; } catch (e) { print(e); - print('likely digitalProductId is null wait till https://github.com/fluttercommunity/plus_plugins/pull/3188 is merged'); + print( + 'likely digitalProductId is null wait till https://github.com/fluttercommunity/plus_plugins/pull/3188 is merged'); deviceName = "Windows Device"; } } diff --git a/lib/view_model/settings/silent_payments_settings_view_model.dart b/lib/view_model/settings/silent_payments_settings_view_model.dart index 5d20230d2..37c2f6486 100644 --- a/lib/view_model/settings/silent_payments_settings_view_model.dart +++ b/lib/view_model/settings/silent_payments_settings_view_model.dart @@ -30,4 +30,10 @@ abstract class SilentPaymentsSettingsViewModelBase with Store { _settingsStore.silentPaymentsAlwaysScan = value; if (value) bitcoin!.setScanningActive(_wallet, true); } + + @action + void registerSilentPaymentsKey(bool value) { + _settingsStore.silentPaymentsKeyRegistered = value; + bitcoin!.registerSilentPaymentsKey(_wallet, true); + } } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 81fe3cc2c..abcb892e1 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "حدد المدفوعات الصامتة دائمًا المسح الضوئي", "silent_payments_disclaimer": "العناوين الجديدة ليست هويات جديدة. إنها إعادة استخدام هوية موجودة مع ملصق مختلف.", "silent_payments_display_card": "عرض بطاقة المدفوعات الصامتة", + "silent_payments_register_key": "سجل عرض مفتاح المسح الأسرع", "silent_payments_scan_from_date": "فحص من التاريخ", "silent_payments_scan_from_date_or_blockheight": "يرجى إدخال ارتفاع الكتلة الذي تريد بدء المسح الضوئي للمدفوعات الصامتة الواردة ، أو استخدام التاريخ بدلاً من ذلك. يمكنك اختيار ما إذا كانت المحفظة تواصل مسح كل كتلة ، أو تتحقق فقط من الارتفاع المحدد.", "silent_payments_scan_from_height": "فحص من ارتفاع الكتلة", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 50db1610a..2060711c5 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Задайте мълчаливи плащания винаги сканиране", "silent_payments_disclaimer": "Новите адреси не са нови идентичности. Това е повторна употреба на съществуваща идентичност с различен етикет.", "silent_payments_display_card": "Показване на безшумни плащания карта", + "silent_payments_register_key": "Регистрирайте ключа за преглед на по -бързото сканиране", "silent_payments_scan_from_date": "Сканиране от дата", "silent_payments_scan_from_date_or_blockheight": "Моля, въведете височината на блока, която искате да започнете да сканирате за входящи безшумни плащания, или вместо това използвайте датата. Можете да изберете дали портфейлът продължава да сканира всеки блок или проверява само определената височина.", "silent_payments_scan_from_height": "Сканиране от височината на блока", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index ddc91340b..f06fe6885 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Nastavit tiché platby vždy skenování", "silent_payments_disclaimer": "Nové adresy nejsou nové identity. Je to opětovné použití existující identity s jiným štítkem.", "silent_payments_display_card": "Zobrazit kartu Silent Payments", + "silent_payments_register_key": "Zobrazení zaregistrujte klíč pro rychlejší skenování", "silent_payments_scan_from_date": "Skenovat od data", "silent_payments_scan_from_date_or_blockheight": "Zadejte výšku bloku, kterou chcete začít skenovat, zda jsou přicházející tiché platby, nebo místo toho použijte datum. Můžete si vybrat, zda peněženka pokračuje v skenování každého bloku nebo zkontroluje pouze zadanou výšku.", "silent_payments_scan_from_height": "Skenování z výšky bloku", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 2ec59f349..c094e838a 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -710,6 +710,7 @@ "silent_payments_always_scan": "Setzen Sie stille Zahlungen immer scannen", "silent_payments_disclaimer": "Neue Adressen sind keine neuen Identitäten. Es ist eine Wiederverwendung einer bestehenden Identität mit einem anderen Etikett.", "silent_payments_display_card": "Zeigen Sie stille Zahlungskarte", + "silent_payments_register_key": "Registrieren Sie die Ansichtsschlüssel für schnelleres Scannen", "silent_payments_scan_from_date": "Scan ab Datum", "silent_payments_scan_from_date_or_blockheight": "Bitte geben Sie die Blockhöhe ein, die Sie für eingehende stille Zahlungen scannen möchten, oder verwenden Sie stattdessen das Datum. Sie können wählen, ob die Wallet jeden Block scannt oder nur die angegebene Höhe überprüft.", "silent_payments_scan_from_height": "Scan aus der Blockhöhe scannen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index d6a0ee9af..b3c2f4720 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -712,6 +712,7 @@ "silent_payments_always_scan": "Set Silent Payments always scanning", "silent_payments_disclaimer": "New addresses are not new identities. It is a re-use of an existing identity with a different label.", "silent_payments_display_card": "Show Silent Payments card", + "silent_payments_register_key": "Register view key for faster scanning", "silent_payments_scan_from_date": "Scan from date", "silent_payments_scan_from_date_or_blockheight": "Please enter the block height you want to start scanning for incoming Silent Payments or use the date instead. You can choose if the wallet continues scanning every block, or checks only the specified height.", "silent_payments_scan_from_height": "Scan from block height", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 25c9f95c1..ecc2356bb 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -710,6 +710,7 @@ "silent_payments_always_scan": "Establecer pagos silenciosos siempre escaneando", "silent_payments_disclaimer": "Las nuevas direcciones no son nuevas identidades. Es una reutilización de una identidad existente con una etiqueta diferente.", "silent_payments_display_card": "Mostrar tarjeta de pagos silenciosos", + "silent_payments_register_key": "Clave de vista de registro para escaneo más rápido", "silent_payments_scan_from_date": "Escanear desde la fecha", "silent_payments_scan_from_date_or_blockheight": "Ingresa la altura de bloque que desea comenzar a escanear para pagos silenciosos entrantes, o usa la fecha en su lugar. Puedes elegir si la billetera continúa escaneando cada bloque, o verifica solo la altura especificada.", "silent_payments_scan_from_height": "Escanear desde la altura de bloque específico", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index be5b48dd8..a68eecc40 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Définir les paiements silencieux toujours à la scanne", "silent_payments_disclaimer": "Les nouvelles adresses ne sont pas de nouvelles identités. Il s'agit d'une réutilisation d'une identité existante avec une étiquette différente.", "silent_payments_display_card": "Afficher la carte de paiement silencieuse", + "silent_payments_register_key": "Enregistrez la touche Afficher pour une analyse plus rapide", "silent_payments_scan_from_date": "Analyser à partir de la date", "silent_payments_scan_from_date_or_blockheight": "Veuillez saisir la hauteur du bloc que vous souhaitez commencer à scanner pour les paiements silencieux entrants, ou utilisez la date à la place. Vous pouvez choisir si le portefeuille continue de numériser chaque bloc ou ne vérifie que la hauteur spécifiée.", "silent_payments_scan_from_height": "Scan à partir de la hauteur du bloc", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 4deb0df1d..809db1727 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -711,6 +711,7 @@ "silent_payments_always_scan": "Saita biya na shiru koyaushe", "silent_payments_disclaimer": "Sabbin adiresoshin ba sabon tsari bane. Wannan shine sake amfani da asalin asalin tare da wata alama daban.", "silent_payments_display_card": "Nuna katin silent", + "silent_payments_register_key": "Yi rijista mabuɗin don bincika sauri", "silent_payments_scan_from_date": "Scan daga kwanan wata", "silent_payments_scan_from_date_or_blockheight": "Da fatan za a shigar da toshe wurin da kake son fara bincika don biyan silins mai shigowa, ko, yi amfani da kwanan wata. Zaka iya zabar idan walat ɗin ya ci gaba da bincika kowane toshe, ko duba tsinkaye da aka ƙayyade.", "silent_payments_scan_from_height": "Scan daga tsayin daka", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 5161250fc..84c1afd6d 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -711,6 +711,7 @@ "silent_payments_always_scan": "मूक भुगतान हमेशा स्कैनिंग सेट करें", "silent_payments_disclaimer": "नए पते नई पहचान नहीं हैं। यह एक अलग लेबल के साथ एक मौजूदा पहचान का पुन: उपयोग है।", "silent_payments_display_card": "मूक भुगतान कार्ड दिखाएं", + "silent_payments_register_key": "तेजी से स्कैनिंग के लिए रजिस्टर व्यू कुंजी", "silent_payments_scan_from_date": "तिथि से स्कैन करना", "silent_payments_scan_from_date_or_blockheight": "कृपया उस ब्लॉक ऊंचाई दर्ज करें जिसे आप आने वाले मूक भुगतान के लिए स्कैन करना शुरू करना चाहते हैं, या, इसके बजाय तारीख का उपयोग करें। आप चुन सकते हैं कि क्या वॉलेट हर ब्लॉक को स्कैन करना जारी रखता है, या केवल निर्दिष्ट ऊंचाई की जांच करता है।", "silent_payments_scan_from_height": "ब्लॉक ऊंचाई से स्कैन करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 8ef92aaf0..6c08955a8 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Postavite tiho plaćanje uvijek skeniranje", "silent_payments_disclaimer": "Nove adrese nisu novi identiteti. To je ponovna upotreba postojećeg identiteta s drugom oznakom.", "silent_payments_display_card": "Prikaži karticu tihih plaćanja", + "silent_payments_register_key": "Registrirajte ključ za brže skeniranje", "silent_payments_scan_from_date": "Skeniranje iz datuma", "silent_payments_scan_from_date_or_blockheight": "Unesite visinu bloka koju želite započeti skeniranje za dolazna tiha plaćanja ili umjesto toga upotrijebite datum. Možete odabrati da li novčanik nastavlja skenirati svaki blok ili provjerava samo navedenu visinu.", "silent_payments_scan_from_height": "Skeniranje s visine bloka", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index 40ed1e116..f3f29721e 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -701,6 +701,7 @@ "silent_payments_always_scan": "Միացնել Լուռ Վճարումներ մշտական սկանավորումը", "silent_payments_disclaimer": "Նոր հասցեները նոր ինքնություն չեն։ Դա այլ պիտակով գոյություն ունեցող ինքնության վերագործածում է", "silent_payments_display_card": "Ցուցադրել Լուռ Վճարումներ քարտը", + "silent_payments_register_key": "Գրանցեք Դիտել ստեղնը `ավելի արագ սկանավորման համար", "silent_payments_scan_from_date": "Սկանավորել ամսաթվից", "silent_payments_scan_from_date_or_blockheight": "Խնդրում ենք մուտքագրել բլոկի բարձրությունը, որտեղից դուք ցանկանում եք սկսել սկանավորել մուտքային Լուռ Վճարումները կամ տեղափոխել ամսաթվի փոխարեն։ Դուք կարող եք ընտրել, արդյոք դրամապանակը շարունակելու է սկանավորել ամեն բլոկ կամ ստուգել միայն սահմանված բարձրությունը", "silent_payments_scan_from_height": "Բլոկի բարձրությունից սկանավորել", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 5f93082ec..1aa071753 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -712,6 +712,7 @@ "silent_payments_always_scan": "Tetapkan pembayaran diam selalu pemindaian", "silent_payments_disclaimer": "Alamat baru bukanlah identitas baru. Ini adalah penggunaan kembali identitas yang ada dengan label yang berbeda.", "silent_payments_display_card": "Tunjukkan kartu pembayaran diam", + "silent_payments_register_key": "Daftar Kunci Lihat untuk pemindaian yang lebih cepat", "silent_payments_scan_from_date": "Pindai dari tanggal", "silent_payments_scan_from_date_or_blockheight": "Harap masukkan ketinggian blok yang ingin Anda mulai pemindaian untuk pembayaran diam yang masuk, atau, gunakan tanggal sebagai gantinya. Anda dapat memilih jika dompet terus memindai setiap blok, atau memeriksa hanya ketinggian yang ditentukan.", "silent_payments_scan_from_height": "Pindai dari Tinggi Blok", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 08ae928af..13133c297 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -711,6 +711,7 @@ "silent_payments_always_scan": "Impostare i pagamenti silenziosi che scansionano sempre", "silent_payments_disclaimer": "I nuovi indirizzi non sono nuove identità. È un riutilizzo di un'identità esistente con un'etichetta diversa.", "silent_payments_display_card": "Mostra la carta di pagamenti silenziosi", + "silent_payments_register_key": "Registra la chiave di visualizzazione per una scansione più veloce", "silent_payments_scan_from_date": "Scansionare dalla data", "silent_payments_scan_from_date_or_blockheight": "Inserisci l'altezza del blocco che si desidera iniziare la scansione per i pagamenti silenziosi in arrivo o, utilizza invece la data. Puoi scegliere se il portafoglio continua a scansionare ogni blocco o controlla solo l'altezza specificata.", "silent_payments_scan_from_height": "Scansione dall'altezza del blocco", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index d70eca31b..331057e23 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -710,6 +710,7 @@ "silent_payments_always_scan": "サイレント決済を常にスキャンします", "silent_payments_disclaimer": "新しいアドレスは新しいアイデンティティではありません。これは、異なるラベルを持つ既存のアイデンティティの再利用です。", "silent_payments_display_card": "サイレントペイメントカードを表示します", + "silent_payments_register_key": "登録キーを登録して、より速いスキャンを行います", "silent_payments_scan_from_date": "日付からスキャンします", "silent_payments_scan_from_date_or_blockheight": "着信のサイレント決済のためにスキャンを開始するブロックの高さを入力するか、代わりに日付を使用してください。ウォレットがすべてのブロックをスキャンし続けるか、指定された高さのみをチェックするかどうかを選択できます。", "silent_payments_scan_from_height": "ブロックの高さからスキャンします", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 133ca1838..542998ebe 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -710,6 +710,7 @@ "silent_payments_always_scan": "무음금을 항상 스캔합니다", "silent_payments_disclaimer": "새로운 주소는 새로운 정체성이 아닙니다. 다른 레이블로 기존 신원을 재사용하는 것입니다.", "silent_payments_display_card": "사일런트 지불 카드 표시", + "silent_payments_register_key": "더 빠른 스캔을 위해보기 키 등록 키", "silent_payments_scan_from_date": "날짜부터 스캔하십시오", "silent_payments_scan_from_date_or_blockheight": "들어오는 사일런트 결제를 위해 스캔을 시작하려는 블록 높이를 입력하거나 대신 날짜를 사용하십시오. 지갑이 모든 블록을 계속 스캔하는지 여부를 선택하거나 지정된 높이 만 확인할 수 있습니다.", "silent_payments_scan_from_height": "블록 높이에서 스캔하십시오", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 1727f0d71..b64615a56 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "အမြဲတမ်း scanning အမြဲ scanning", "silent_payments_disclaimer": "လိပ်စာအသစ်များသည်အထောက်အထားအသစ်များမဟုတ်ပါ။ ၎င်းသည်ကွဲပြားခြားနားသောတံဆိပ်ဖြင့်ရှိပြီးသားဝိသေသလက်ခဏာကိုပြန်လည်အသုံးပြုခြင်းဖြစ်သည်။", "silent_payments_display_card": "အသံတိတ်ငွေပေးချေမှုကဒ်ကိုပြပါ", + "silent_payments_register_key": "ပိုမိုမြန်ဆန်သောစကင်ဖတ်စစ်ဆေးရန်အတွက်ကြည့်ပါ", "silent_payments_scan_from_date": "ရက်စွဲမှစကင်ဖတ်ပါ", "silent_payments_scan_from_date_or_blockheight": "ကျေးဇူးပြု. သင်ဝင်လာသောအသံတိတ်ငွေပေးချေမှုအတွက်သင်စကင်ဖတ်စစ်ဆေးလိုသည့်အမြင့်ကိုဖြည့်ပါ။ သို့မဟုတ်နေ့စွဲကိုသုံးပါ။ Wallet သည်လုပ်ကွက်တိုင်းကိုဆက်လက်စကင်ဖတ်စစ်ဆေးပါကသို့မဟုတ်သတ်မှတ်ထားသောအမြင့်ကိုသာစစ်ဆေးပါကသင်ရွေးချယ်နိုင်သည်။", "silent_payments_scan_from_height": "ပိတ်ပင်တားဆီးမှုအမြင့်ကနေ scan", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 3f2df531b..d732ac410 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Stel stille betalingen in het scannen", "silent_payments_disclaimer": "Nieuwe adressen zijn geen nieuwe identiteiten. Het is een hergebruik van een bestaande identiteit met een ander label.", "silent_payments_display_card": "Toon stille betalingskaart", + "silent_payments_register_key": "Registerweergave Key voor sneller scannen", "silent_payments_scan_from_date": "Scan vanaf datum", "silent_payments_scan_from_date_or_blockheight": "Voer de blokhoogte in die u wilt beginnen met scannen op inkomende stille betalingen, of gebruik in plaats daarvan de datum. U kunt kiezen of de portemonnee elk blok blijft scannen of alleen de opgegeven hoogte controleert.", "silent_payments_scan_from_height": "Scan van blokhoogte", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 91b265144..5c82dcdc7 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Ustaw ciche płatności zawsze skanowanie", "silent_payments_disclaimer": "Nowe adresy nie są nową tożsamością. Jest to ponowne wykorzystanie istniejącej tożsamości z inną etykietą.", "silent_payments_display_card": "Pokaż kartę Silent Payments", + "silent_payments_register_key": "Zarejestruj się Wyświetl Klucz do szybszego skanowania", "silent_payments_scan_from_date": "Skanuj z daty", "silent_payments_scan_from_date_or_blockheight": "Wprowadź wysokość bloku, którą chcesz rozpocząć skanowanie w poszukiwaniu cichej płatności lub zamiast tego skorzystaj z daty. Możesz wybrać, czy portfel kontynuuje skanowanie każdego bloku, lub sprawdza tylko określoną wysokość.", "silent_payments_scan_from_height": "Skanuj z wysokości bloku", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 524dbcace..3d6b0c8bb 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -711,6 +711,7 @@ "silent_payments_always_scan": "Defina pagamentos silenciosos sempre escaneando", "silent_payments_disclaimer": "Novos endereços não são novas identidades. É uma reutilização de uma identidade existente com um rótulo diferente.", "silent_payments_display_card": "Mostrar cartão de pagamento silencioso", + "silent_payments_register_key": "Chave de exibição de registro para digitalização mais rápida", "silent_payments_scan_from_date": "Escanear a partir da data", "silent_payments_scan_from_date_or_blockheight": "Por favor, insira a altura do bloco que deseja iniciar o escaneamento para obter pagamentos silenciosos ou use a data. Você pode escolher se a carteira continua escaneando cada bloco ou verifica apenas a altura especificada.", "silent_payments_scan_from_height": "Escanear a partir da altura do bloco", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 1a8c2447f..f172d4390 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -710,6 +710,7 @@ "silent_payments_always_scan": "Установить молчаливые платежи всегда сканирование", "silent_payments_disclaimer": "Новые адреса не являются новыми личностями. Это повторное использование существующей идентичности с другой этикеткой.", "silent_payments_display_card": "Показать бесшумную платежную карту", + "silent_payments_register_key": "Зарегистрируйте ключ просмотра для более быстрого сканирования", "silent_payments_scan_from_date": "Сканирование с даты", "silent_payments_scan_from_date_or_blockheight": "Пожалуйста, введите высоту блока, которую вы хотите начать сканирование для входящих молчаливых платежей, или вместо этого используйте дату. Вы можете выбрать, продолжает ли кошелек сканировать каждый блок или проверять только указанную высоту.", "silent_payments_scan_from_height": "Сканирование с высоты блока", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 213f74530..a93411085 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "ตั้งค่าการชำระเงินแบบเงียบเสมอ", "silent_payments_disclaimer": "ที่อยู่ใหม่ไม่ใช่ตัวตนใหม่ มันเป็นการใช้ซ้ำของตัวตนที่มีอยู่ด้วยฉลากที่แตกต่างกัน", "silent_payments_display_card": "แสดงบัตร Silent Payments", + "silent_payments_register_key": "ลงทะเบียนคีย์มุมมองสำหรับการสแกนที่เร็วขึ้น", "silent_payments_scan_from_date": "สแกนตั้งแต่วันที่", "silent_payments_scan_from_date_or_blockheight": "โปรดป้อนความสูงของบล็อกที่คุณต้องการเริ่มการสแกนสำหรับการชำระเงินแบบเงียบ ๆ หรือใช้วันที่แทน คุณสามารถเลือกได้ว่ากระเป๋าเงินยังคงสแกนทุกบล็อกหรือตรวจสอบความสูงที่ระบุเท่านั้น", "silent_payments_scan_from_height": "สแกนจากความสูงของบล็อก", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 0ca8ee665..b8c4af9d2 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Itakda ang mga tahimik na pagbabayad na laging nag-scan", "silent_payments_disclaimer": "Ang mga bagong address ay hindi mga bagong pagkakakilanlan. Ito ay isang muling paggamit ng isang umiiral na pagkakakilanlan na may ibang label.", "silent_payments_display_card": "Ipakita ang Silent Payment Card", + "silent_payments_register_key": "Magrehistro ng View Key para sa mas mabilis na pag -scan", "silent_payments_scan_from_date": "I-scan mula sa petsa", "silent_payments_scan_from_date_or_blockheight": "Mangyaring ipasok ang block height na gusto mong simulan ang pag-scan para sa papasok na tahimik na pagbabayad, o, gamitin ang petsa sa halip. Maaari kang pumili kung ang wallet ay patuloy na pag-scan sa bawat bloke, o suriin lamang ang tinukoy na taas.", "silent_payments_scan_from_height": "I-scan mula sa block height", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index b23f64d60..2b44b6306 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "Sessiz ödemeleri her zaman tarama ayarlayın", "silent_payments_disclaimer": "Yeni adresler yeni kimlikler değildir. Farklı bir etikete sahip mevcut bir kimliğin yeniden kullanımıdır.", "silent_payments_display_card": "Sessiz Ödeme Kartı Göster", + "silent_payments_register_key": "Daha hızlı tarama için tuşunu kaydet", "silent_payments_scan_from_date": "Tarihten tarama", "silent_payments_scan_from_date_or_blockheight": "Lütfen gelen sessiz ödemeler için taramaya başlamak istediğiniz blok yüksekliğini girin veya bunun yerine tarihi kullanın. Cüzdanın her bloğu taramaya devam edip etmediğini veya yalnızca belirtilen yüksekliği kontrol edip etmediğini seçebilirsiniz.", "silent_payments_scan_from_height": "Blok yüksekliğinden tarama", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 79dc0543f..d5a82293e 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -710,6 +710,7 @@ "silent_payments_always_scan": "Встановити мовчазні платежі завжди сканувати", "silent_payments_disclaimer": "Нові адреси - це не нові ідентичності. Це повторне використання існуючої ідентичності з іншою етикеткою.", "silent_payments_display_card": "Покажіть безшумну карту платежів", + "silent_payments_register_key": "Зареєструйтесь ключ для більш швидкого сканування", "silent_payments_scan_from_date": "Сканувати з дати", "silent_payments_scan_from_date_or_blockheight": "Введіть висоту блоку, яку ви хочете почати сканувати для вхідних мовчазних платежів, або скористайтеся датою замість цього. Ви можете вибрати, якщо гаманець продовжує сканувати кожен блок, або перевіряє лише вказану висоту.", "silent_payments_scan_from_height": "Сканування від висоти блоку", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 0a136d140..84a8bb355 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -711,6 +711,7 @@ "silent_payments_always_scan": "خاموش ادائیگی ہمیشہ اسکیننگ کریں", "silent_payments_disclaimer": "نئے پتے نئی شناخت نہیں ہیں۔ یہ ایک مختلف لیبل کے ساتھ موجودہ شناخت کا دوبارہ استعمال ہے۔", "silent_payments_display_card": "خاموش ادائیگی کارڈ دکھائیں", + "silent_payments_register_key": "تیزی سے اسکیننگ کے لئے کلید کو رجسٹر کریں", "silent_payments_scan_from_date": "تاریخ سے اسکین کریں", "silent_payments_scan_from_date_or_blockheight": "براہ کرم بلاک اونچائی میں داخل ہوں جس سے آپ آنے والی خاموش ادائیگیوں کے لئے اسکیننگ شروع کرنا چاہتے ہیں ، یا اس کے بجائے تاریخ کا استعمال کریں۔ آپ یہ منتخب کرسکتے ہیں کہ اگر پرس ہر بلاک کو اسکیننگ جاری رکھے ہوئے ہے ، یا صرف مخصوص اونچائی کی جانچ پڑتال کرتا ہے۔", "silent_payments_scan_from_height": "بلاک اونچائی سے اسکین کریں", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index 8d28d48a2..6b6f0dd50 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -702,6 +702,7 @@ "silent_payments_always_scan": "Đặt Thanh toán im lặng luôn quét", "silent_payments_disclaimer": "Địa chỉ mới không phải là danh tính mới. Đây là việc tái sử dụng một danh tính hiện có với nhãn khác.", "silent_payments_display_card": "Hiển thị thẻ Thanh toán im lặng", + "silent_payments_register_key": "Đăng ký khóa xem để quét nhanh hơn", "silent_payments_scan_from_date": "Quét từ ngày", "silent_payments_scan_from_date_or_blockheight": "Vui lòng nhập chiều cao khối bạn muốn bắt đầu quét cho các thanh toán im lặng đến, hoặc, sử dụng ngày thay thế. Bạn có thể chọn nếu ví tiếp tục quét mỗi khối, hoặc chỉ kiểm tra chiều cao đã chỉ định.", "silent_payments_scan_from_height": "Quét từ chiều cao khối", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 14270120c..c45acfe7f 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -710,6 +710,7 @@ "silent_payments_always_scan": "Ṣeto awọn sisanwo ipalọlọ nigbagbogbo n ṣatunṣe", "silent_payments_disclaimer": "Awọn adirẹsi tuntun kii ṣe awọn idanimọ tuntun. O jẹ yiyan ti idanimọ ti o wa pẹlu aami oriṣiriṣi.", "silent_payments_display_card": "Ṣafihan kaadi isanwo ti o dakẹ", + "silent_payments_register_key": "Forukọsilẹ Wo bọtini Window fun Cranding yiyara", "silent_payments_scan_from_date": "Scan lati ọjọ", "silent_payments_scan_from_date_or_blockheight": "Jọwọ tẹ giga idibo ti o fẹ bẹrẹ ọlọjẹ fun awọn sisanwo ipalọlọ, tabi, lo ọjọ dipo. O le yan ti apamọwọ naa tẹsiwaju nṣapẹẹrẹ gbogbo bulọọki, tabi ṣayẹwo nikan giga ti o sọ tẹlẹ.", "silent_payments_scan_from_height": "Scan lati Iga Iga", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 65047b4fe..cee24ba1b 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -709,6 +709,7 @@ "silent_payments_always_scan": "设置无声付款总是扫描", "silent_payments_disclaimer": "新地址不是新的身份。这是重复使用具有不同标签的现有身份。", "silent_payments_display_card": "显示无声支付卡", + "silent_payments_register_key": "注册查看密钥以进行更快的扫描", "silent_payments_scan_from_date": "从日期开始扫描", "silent_payments_scan_from_date_or_blockheight": "请输入您要开始扫描输入静音付款的块高度,或者使用日期。您可以选择钱包是否继续扫描每个块,或仅检查指定的高度。", "silent_payments_scan_from_height": "从块高度扫描", diff --git a/tool/configure.dart b/tool/configure.dart index 97541c2fa..16370e977 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -102,7 +102,6 @@ import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:bip39/bip39.dart' as bip39; """; const bitcoinCWHeaders = """ -import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; @@ -216,6 +215,7 @@ abstract class Bitcoin { int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount, {int? outputsCount, int? size}); int feeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); + Future registerSilentPaymentsKey(Object wallet, bool active); Future checkIfMempoolAPIIsEnabled(Object wallet); Future getHeightByDate({required DateTime date, bool? bitcoinMempoolAPIEnabled}); int getLitecoinHeightByDate({required DateTime date});