mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 03:59:23 +00:00
feat: derivationinfo to address records
This commit is contained in:
parent
64caf8479e
commit
433686bce3
16 changed files with 253 additions and 212 deletions
|
@ -6,7 +6,7 @@ abstract class BaseBitcoinAddressRecord {
|
|||
BaseBitcoinAddressRecord(
|
||||
this.address, {
|
||||
required this.index,
|
||||
this.isChange = false,
|
||||
bool isChange = false,
|
||||
int txCount = 0,
|
||||
int balance = 0,
|
||||
String name = '',
|
||||
|
@ -17,7 +17,8 @@ abstract class BaseBitcoinAddressRecord {
|
|||
_balance = balance,
|
||||
_name = name,
|
||||
_isUsed = isUsed,
|
||||
_isHidden = isHidden ?? isChange;
|
||||
_isHidden = isHidden ?? isChange,
|
||||
_isChange = isChange;
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address;
|
||||
|
@ -25,7 +26,8 @@ abstract class BaseBitcoinAddressRecord {
|
|||
final String address;
|
||||
final bool _isHidden;
|
||||
bool get isHidden => _isHidden;
|
||||
bool isChange;
|
||||
final bool _isChange;
|
||||
bool get isChange => _isChange;
|
||||
final int index;
|
||||
int _txCount;
|
||||
int _balance;
|
||||
|
@ -55,9 +57,12 @@ abstract class BaseBitcoinAddressRecord {
|
|||
}
|
||||
|
||||
class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||
final BitcoinDerivationInfo derivationInfo;
|
||||
|
||||
BitcoinAddressRecord(
|
||||
super.address, {
|
||||
required super.index,
|
||||
required this.derivationInfo,
|
||||
super.isHidden,
|
||||
super.isChange = false,
|
||||
super.txCount = 0,
|
||||
|
@ -81,6 +86,9 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
return BitcoinAddressRecord(
|
||||
decoded['address'] as String,
|
||||
index: decoded['index'] as int,
|
||||
derivationInfo: BitcoinDerivationInfo.fromJSON(
|
||||
decoded['derivationInfo'] as Map<String, dynamic>,
|
||||
),
|
||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||
isChange: decoded['isChange'] as bool? ?? false,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||
|
@ -101,6 +109,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
String toJSON() => json.encode({
|
||||
'address': address,
|
||||
'index': index,
|
||||
'derivationInfo': derivationInfo.toJSON(),
|
||||
'isHidden': isHidden,
|
||||
'isChange': isChange,
|
||||
'isUsed': isUsed,
|
||||
|
@ -116,6 +125,8 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
int get labelIndex => index;
|
||||
final String? labelHex;
|
||||
|
||||
static bool isChangeAddress(int labelIndex) => labelIndex == 0;
|
||||
|
||||
BitcoinSilentPaymentAddressRecord(
|
||||
super.address, {
|
||||
required int labelIndex,
|
||||
|
@ -126,9 +137,9 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
super.type = SilentPaymentsAddresType.p2sp,
|
||||
super.isHidden,
|
||||
this.labelHex,
|
||||
}) : super(index: labelIndex, isChange: labelIndex == 0) {
|
||||
}) : super(index: labelIndex, isChange: isChangeAddress(labelIndex)) {
|
||||
if (labelIndex != 1 && labelHex == null) {
|
||||
throw ArgumentError('label must be provided for silent address index > 0');
|
||||
throw ArgumentError('label must be provided for silent address index != 1');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,14 @@ class BitcoinHardwareWalletService {
|
|||
final xpub = await bitcoinLedgerApp.getXPubKey(derivationPath: derivationPath);
|
||||
final bip32 = Bip32Slip10Secp256k1.fromExtendedKey(xpub).childKey(Bip32KeyIndex(0));
|
||||
|
||||
final fullPath = Bip32PathParser.parse(derivationPath).addElem(Bip32KeyIndex(0));
|
||||
|
||||
final address = ECPublic.fromBip32(bip32.derive(fullPath).publicKey)
|
||||
.toP2wpkhAddress()
|
||||
.toAddress(BitcoinNetwork.mainnet);
|
||||
|
||||
accounts.add(HardwareAccountData(
|
||||
address: P2wpkhAddress.fromBip32(bip32: bip32, isChange: false, index: i)
|
||||
.toAddress(BitcoinNetwork.mainnet),
|
||||
address: address,
|
||||
accountIndex: i,
|
||||
derivationPath: derivationPath,
|
||||
masterFingerprint: masterFp,
|
||||
|
|
|
@ -32,7 +32,10 @@ class BitcoinUnspent extends Unspent {
|
|||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
print('BitcoinUnspent operator ==');
|
||||
if (identical(this, o)) return true;
|
||||
return o is BitcoinUnspent && hash == o.hash && vout == o.vout;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(hash, vout);
|
||||
}
|
||||
|
|
|
@ -360,7 +360,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
updatedUnspentCoins.addAll(await fetchUnspent(address));
|
||||
}));
|
||||
|
||||
unspentCoins = updatedUnspentCoins.toSet();
|
||||
unspentCoins.addAll(updatedUnspentCoins);
|
||||
|
||||
if (unspentCoinsInfo.length != updatedUnspentCoins.length) {
|
||||
unspentCoins.forEach((coin) => addCoinInfo(coin));
|
||||
|
@ -1033,3 +1033,58 @@ Future<void> delegatedScan(ScanData scanData) async {
|
|||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
class ScanNode {
|
||||
final Uri uri;
|
||||
final bool? useSSL;
|
||||
|
||||
ScanNode(this.uri, this.useSSL);
|
||||
}
|
||||
|
||||
class ScanData {
|
||||
final SendPort sendPort;
|
||||
final SilentPaymentOwner silentAddress;
|
||||
final int height;
|
||||
final ScanNode? node;
|
||||
final BasedUtxoNetwork network;
|
||||
final int chainTip;
|
||||
final List<String> transactionHistoryIds;
|
||||
final Map<String, String> labels;
|
||||
final List<int> labelIndexes;
|
||||
final bool isSingleScan;
|
||||
|
||||
ScanData({
|
||||
required this.sendPort,
|
||||
required this.silentAddress,
|
||||
required this.height,
|
||||
required this.node,
|
||||
required this.network,
|
||||
required this.chainTip,
|
||||
required this.transactionHistoryIds,
|
||||
required this.labels,
|
||||
required this.labelIndexes,
|
||||
required this.isSingleScan,
|
||||
});
|
||||
|
||||
factory ScanData.fromHeight(ScanData scanData, int newHeight) {
|
||||
return ScanData(
|
||||
sendPort: scanData.sendPort,
|
||||
silentAddress: scanData.silentAddress,
|
||||
height: newHeight,
|
||||
node: scanData.node,
|
||||
network: scanData.network,
|
||||
chainTip: scanData.chainTip,
|
||||
transactionHistoryIds: scanData.transactionHistoryIds,
|
||||
labels: scanData.labels,
|
||||
labelIndexes: scanData.labelIndexes,
|
||||
isSingleScan: scanData.isSingleScan,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SyncResponse {
|
||||
final int height;
|
||||
final SyncStatus syncStatus;
|
||||
|
||||
SyncResponse(this.height, this.syncStatus);
|
||||
}
|
||||
|
|
|
@ -39,27 +39,44 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
required bool isChange,
|
||||
required int index,
|
||||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) {
|
||||
switch (addressType) {
|
||||
case P2pkhAddressType.p2pkh:
|
||||
return P2pkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
case SegwitAddresType.p2tr:
|
||||
return P2trAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
case SegwitAddresType.p2wsh:
|
||||
return P2wshAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
case P2shAddressType.p2wpkhInP2sh:
|
||||
return P2shAddress.fromBip32(
|
||||
return P2pkhAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
);
|
||||
case SegwitAddresType.p2tr:
|
||||
return P2trAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
);
|
||||
case SegwitAddresType.p2wsh:
|
||||
return P2wshAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
);
|
||||
case P2shAddressType.p2wpkhInP2sh:
|
||||
return P2shAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
type: P2shAddressType.p2wpkhInP2sh,
|
||||
);
|
||||
case SegwitAddresType.p2wpkh:
|
||||
return P2wpkhAddress.fromBip32(
|
||||
return P2wpkhAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
isElectrum: false, // TODO:
|
||||
);
|
||||
default:
|
||||
throw ArgumentError('Invalid address type');
|
||||
|
|
|
@ -4,11 +4,8 @@ import 'package:cw_core/encryption_file_utils.dart';
|
|||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/utils/file.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
|
||||
part 'electrum_transaction_history.g.dart';
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
@ -38,7 +37,6 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
part 'electrum_wallet.g.dart';
|
||||
|
@ -69,7 +67,8 @@ abstract class ElectrumWalletBase
|
|||
_password = password,
|
||||
_isTransactionUpdating = false,
|
||||
isEnabledAutoGenerateSubaddress = true,
|
||||
unspentCoins = {},
|
||||
// TODO: inital unspent coins
|
||||
unspentCoins = ObservableSet(),
|
||||
scripthashesListening = {},
|
||||
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
|
||||
? {
|
||||
|
@ -221,8 +220,7 @@ abstract class ElectrumWalletBase
|
|||
);
|
||||
|
||||
String _password;
|
||||
@observable
|
||||
Set<BitcoinUnspent> unspentCoins;
|
||||
ObservableSet<BitcoinUnspent> unspentCoins;
|
||||
|
||||
@observable
|
||||
TransactionPriorities? feeRates;
|
||||
|
@ -404,7 +402,7 @@ abstract class ElectrumWalletBase
|
|||
|
||||
bool _isBelowDust(int amount) => amount <= _dustAmount && network != BitcoinNetwork.testnet;
|
||||
|
||||
UtxoDetails _createUTXOS({
|
||||
TxCreateUtxoDetails _createUTXOS({
|
||||
required bool sendAll,
|
||||
required int credentialsAmount,
|
||||
required bool paysToSilentPayment,
|
||||
|
@ -484,13 +482,13 @@ abstract class ElectrumWalletBase
|
|||
.toHex();
|
||||
}
|
||||
|
||||
// TODO: isElectrum
|
||||
final derivationPath = BitcoinAddressUtils.getDerivationPath(
|
||||
type: utx.bitcoinAddressRecord.type,
|
||||
account: utx.bitcoinAddressRecord.isChange ? 1 : 0,
|
||||
index: utx.bitcoinAddressRecord.index,
|
||||
);
|
||||
if (utx.bitcoinAddressRecord is BitcoinAddressRecord) {
|
||||
final derivationPath = (utx.bitcoinAddressRecord as BitcoinAddressRecord)
|
||||
.derivationInfo
|
||||
.derivationPath
|
||||
.toString();
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
}
|
||||
|
||||
utxos.add(
|
||||
UtxoWithAddress(
|
||||
|
@ -521,7 +519,7 @@ abstract class ElectrumWalletBase
|
|||
throw BitcoinTransactionNoInputsException();
|
||||
}
|
||||
|
||||
return UtxoDetails(
|
||||
return TxCreateUtxoDetails(
|
||||
availableInputs: availableInputs,
|
||||
unconfirmedCoins: unconfirmedCoins,
|
||||
utxos: utxos,
|
||||
|
@ -662,11 +660,7 @@ abstract class ElectrumWalletBase
|
|||
isChange: true,
|
||||
));
|
||||
|
||||
final changeDerivationPath = BitcoinAddressUtils.getDerivationPath(
|
||||
type: changeAddress.type,
|
||||
account: changeAddress.isChange ? 1 : 0,
|
||||
index: changeAddress.index,
|
||||
);
|
||||
final changeDerivationPath = changeAddress.derivationInfo.derivationPath.toString();
|
||||
utxoDetails.publicKeys[address.pubKeyHash()] =
|
||||
PublicKeyWithDerivationPath('', changeDerivationPath);
|
||||
|
||||
|
@ -1176,7 +1170,7 @@ abstract class ElectrumWalletBase
|
|||
updatedUnspentCoins.addAll(await fetchUnspent(address));
|
||||
}));
|
||||
|
||||
unspentCoins = updatedUnspentCoins.toSet();
|
||||
unspentCoins.addAll(updatedUnspentCoins);
|
||||
|
||||
if (unspentCoinsInfo.length != updatedUnspentCoins.length) {
|
||||
unspentCoins.forEach((coin) => addCoinInfo(coin));
|
||||
|
@ -1220,17 +1214,7 @@ abstract class ElectrumWalletBase
|
|||
final newUnspentCoins = (await fetchUnspent(addressRecord)).toSet();
|
||||
await updateCoins(newUnspentCoins);
|
||||
|
||||
print([1, unspentCoins.containsAll(newUnspentCoins)]);
|
||||
if (!unspentCoins.containsAll(newUnspentCoins)) {
|
||||
newUnspentCoins.forEach((coin) {
|
||||
print(unspentCoins.contains(coin));
|
||||
print([coin.vout, coin.hash]);
|
||||
print([unspentCoins.first.vout, unspentCoins.first.hash]);
|
||||
if (!unspentCoins.contains(coin)) {
|
||||
unspentCoins.add(coin);
|
||||
}
|
||||
});
|
||||
}
|
||||
unspentCoins.addAll(newUnspentCoins);
|
||||
|
||||
// if (unspentCoinsInfo.length != unspentCoins.length) {
|
||||
// unspentCoins.forEach(addCoinInfo);
|
||||
|
@ -1400,7 +1384,7 @@ abstract class ElectrumWalletBase
|
|||
if (index + 1 <= script.length) {
|
||||
try {
|
||||
final opReturnData = script[index + 1].toString();
|
||||
memo = utf8.decode(HEX.decode(opReturnData));
|
||||
memo = StringUtils.decode(BytesUtils.fromHexString(opReturnData));
|
||||
continue;
|
||||
} catch (_) {
|
||||
throw Exception('Cannot decode OP_RETURN data');
|
||||
|
@ -1708,6 +1692,7 @@ abstract class ElectrumWalletBase
|
|||
isChange: addressRecord.isChange,
|
||||
gap: gapLimit,
|
||||
type: addressRecord.type,
|
||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(addressRecord.type),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1805,21 +1790,17 @@ abstract class ElectrumWalletBase
|
|||
|
||||
@override
|
||||
Future<String> signMessage(String message, {String? address = null}) async {
|
||||
Bip32Slip10Secp256k1 HD = bip32;
|
||||
final record = walletAddresses.getFromAddresses(address!);
|
||||
|
||||
final record = walletAddresses.allAddresses.firstWhere((element) => element.address == address);
|
||||
final path = Bip32PathParser.parse(walletInfo.derivationInfo!.derivationPath!)
|
||||
.addElem(
|
||||
Bip32KeyIndex(BitcoinAddressUtils.getAccountFromChange(record.isChange)),
|
||||
)
|
||||
.addElem(Bip32KeyIndex(record.index));
|
||||
|
||||
if (record.isChange) {
|
||||
HD = HD.childKey(Bip32KeyIndex(1));
|
||||
} else {
|
||||
HD = HD.childKey(Bip32KeyIndex(0));
|
||||
}
|
||||
final priv = ECPrivate.fromHex(bip32.derive(path).privateKey.toHex());
|
||||
|
||||
HD = HD.childKey(Bip32KeyIndex(record.index));
|
||||
final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex());
|
||||
|
||||
String messagePrefix = '\x18Bitcoin Signed Message:\n';
|
||||
final hexEncoded = priv.signMessage(utf8.encode(message), messagePrefix: messagePrefix);
|
||||
final hexEncoded = priv.signMessage(StringUtils.encode(message));
|
||||
final decodedSig = hex.decode(hexEncoded);
|
||||
return base64Encode(decodedSig);
|
||||
}
|
||||
|
@ -1835,7 +1816,7 @@ abstract class ElectrumWalletBase
|
|||
if (signature.endsWith('=')) {
|
||||
sigDecodedBytes = base64.decode(signature);
|
||||
} else {
|
||||
sigDecodedBytes = hex.decode(signature);
|
||||
sigDecodedBytes = BytesUtils.fromHexString(signature);
|
||||
}
|
||||
|
||||
if (sigDecodedBytes.length != 64 && sigDecodedBytes.length != 65) {
|
||||
|
@ -1845,7 +1826,7 @@ abstract class ElectrumWalletBase
|
|||
|
||||
String messagePrefix = '\x18Bitcoin Signed Message:\n';
|
||||
final messageHash = QuickCrypto.sha256Hash(
|
||||
BitcoinSignerUtils.magicMessage(utf8.encode(message), messagePrefix));
|
||||
BitcoinSignerUtils.magicMessage(StringUtils.encode(message), messagePrefix));
|
||||
|
||||
List<int> correctSignature =
|
||||
sigDecodedBytes.length == 65 ? sigDecodedBytes.sublist(1) : List.from(sigDecodedBytes);
|
||||
|
@ -1911,14 +1892,14 @@ abstract class ElectrumWalletBase
|
|||
|
||||
break;
|
||||
case ConnectionStatus.disconnected:
|
||||
// if (syncStatus is! NotConnectedSyncStatus) {
|
||||
// syncStatus = NotConnectedSyncStatus();
|
||||
// }
|
||||
if (syncStatus is! NotConnectedSyncStatus) {
|
||||
syncStatus = NotConnectedSyncStatus();
|
||||
}
|
||||
break;
|
||||
case ConnectionStatus.failed:
|
||||
// if (syncStatus is! LostConnectionSyncStatus) {
|
||||
// syncStatus = LostConnectionSyncStatus();
|
||||
// }
|
||||
if (syncStatus is! LostConnectionSyncStatus) {
|
||||
syncStatus = LostConnectionSyncStatus();
|
||||
}
|
||||
break;
|
||||
case ConnectionStatus.connecting:
|
||||
if (syncStatus is! ConnectingSyncStatus) {
|
||||
|
@ -1989,7 +1970,7 @@ abstract class ElectrumWalletBase
|
|||
if (index + 1 <= script.length) {
|
||||
try {
|
||||
final opReturnData = script[index + 1].toString();
|
||||
final decodedString = utf8.decode(HEX.decode(opReturnData));
|
||||
final decodedString = StringUtils.decode(BytesUtils.fromHexString(opReturnData));
|
||||
outputAddresses.add('OP_RETURN:$decodedString');
|
||||
} catch (_) {
|
||||
outputAddresses.add('OP_RETURN:');
|
||||
|
@ -2005,61 +1986,6 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
class ScanNode {
|
||||
final Uri uri;
|
||||
final bool? useSSL;
|
||||
|
||||
ScanNode(this.uri, this.useSSL);
|
||||
}
|
||||
|
||||
class ScanData {
|
||||
final SendPort sendPort;
|
||||
final SilentPaymentOwner silentAddress;
|
||||
final int height;
|
||||
final ScanNode? node;
|
||||
final BasedUtxoNetwork network;
|
||||
final int chainTip;
|
||||
final List<String> transactionHistoryIds;
|
||||
final Map<String, String> labels;
|
||||
final List<int> labelIndexes;
|
||||
final bool isSingleScan;
|
||||
|
||||
ScanData({
|
||||
required this.sendPort,
|
||||
required this.silentAddress,
|
||||
required this.height,
|
||||
required this.node,
|
||||
required this.network,
|
||||
required this.chainTip,
|
||||
required this.transactionHistoryIds,
|
||||
required this.labels,
|
||||
required this.labelIndexes,
|
||||
required this.isSingleScan,
|
||||
});
|
||||
|
||||
factory ScanData.fromHeight(ScanData scanData, int newHeight) {
|
||||
return ScanData(
|
||||
sendPort: scanData.sendPort,
|
||||
silentAddress: scanData.silentAddress,
|
||||
height: newHeight,
|
||||
node: scanData.node,
|
||||
network: scanData.network,
|
||||
chainTip: scanData.chainTip,
|
||||
transactionHistoryIds: scanData.transactionHistoryIds,
|
||||
labels: scanData.labels,
|
||||
labelIndexes: scanData.labelIndexes,
|
||||
isSingleScan: scanData.isSingleScan,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SyncResponse {
|
||||
final int height;
|
||||
final SyncStatus syncStatus;
|
||||
|
||||
SyncResponse(this.height, this.syncStatus);
|
||||
}
|
||||
|
||||
class EstimatedTxResult {
|
||||
EstimatedTxResult({
|
||||
required this.utxos,
|
||||
|
@ -2095,7 +2021,7 @@ class PublicKeyWithDerivationPath {
|
|||
final String publicKey;
|
||||
}
|
||||
|
||||
class UtxoDetails {
|
||||
class TxCreateUtxoDetails {
|
||||
final List<BitcoinUnspent> availableInputs;
|
||||
final List<BitcoinUnspent> unconfirmedCoins;
|
||||
final List<UtxoWithAddress> utxos;
|
||||
|
@ -2106,7 +2032,7 @@ class UtxoDetails {
|
|||
final bool spendsSilentPayment;
|
||||
final bool spendsUnconfirmedTX;
|
||||
|
||||
UtxoDetails({
|
||||
TxCreateUtxoDetails({
|
||||
required this.availableInputs,
|
||||
required this.unconfirmedCoins,
|
||||
required this.utxos,
|
||||
|
|
|
@ -43,7 +43,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
int initialSilentAddressIndex = 0,
|
||||
List<BitcoinAddressRecord>? initialMwebAddresses,
|
||||
BitcoinAddressType? initialAddressPageType,
|
||||
}) : _allAddresses = (initialAddresses ?? []).toSet(),
|
||||
}) : _allAddresses = ObservableSet.of(initialAddresses ?? []),
|
||||
addressesByReceiveType =
|
||||
ObservableList<BaseBitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
|
@ -63,11 +63,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
mwebAddresses =
|
||||
ObservableList<BitcoinAddressRecord>.of((initialMwebAddresses ?? []).toSet()),
|
||||
super(walletInfo) {
|
||||
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,
|
||||
);
|
||||
// TODO: initial silent address, not every time
|
||||
silentAddress = SilentPaymentOwner.fromBip32(bip32);
|
||||
|
||||
if (silentAddresses.length == 0) {
|
||||
silentAddresses.add(BitcoinSilentPaymentAddressRecord(
|
||||
silentAddress.toString(),
|
||||
|
@ -91,8 +89,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
static const defaultChangeAddressesCount = 17;
|
||||
static const gap = 20;
|
||||
|
||||
@observable
|
||||
final Set<BitcoinAddressRecord> _allAddresses;
|
||||
final ObservableSet<BitcoinAddressRecord> _allAddresses;
|
||||
final ObservableList<BaseBitcoinAddressRecord> addressesByReceiveType;
|
||||
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
||||
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
||||
|
@ -119,6 +116,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@computed
|
||||
List<BitcoinAddressRecord> get allAddresses => _allAddresses.toList();
|
||||
|
||||
BitcoinAddressRecord getFromAddresses(String address) {
|
||||
return _allAddresses.firstWhere((element) => element.address == address);
|
||||
}
|
||||
|
||||
@override
|
||||
@computed
|
||||
String get address {
|
||||
|
@ -189,7 +190,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
@override
|
||||
String get primaryAddress => getAddress(isChange: false, index: 0, addressType: addressPageType);
|
||||
String get primaryAddress => _allAddresses.first.address;
|
||||
|
||||
Map<String, int> currentReceiveAddressIndexByType;
|
||||
|
||||
|
@ -250,7 +251,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
updateAddressesByMatch();
|
||||
updateReceiveAddresses();
|
||||
updateChangeAddresses();
|
||||
_validateAddresses();
|
||||
await updateAddressesInBox();
|
||||
|
||||
if (currentReceiveAddressIndex >= receiveAddresses.length) {
|
||||
|
@ -263,15 +263,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
Future<BitcoinAddressRecord> getChangeAddress(
|
||||
{List<BitcoinUnspent>? inputs, List<BitcoinOutput>? outputs, bool isPegIn = false}) async {
|
||||
Future<BitcoinAddressRecord> getChangeAddress({
|
||||
List<BitcoinUnspent>? inputs,
|
||||
List<BitcoinOutput>? outputs,
|
||||
bool isPegIn = false,
|
||||
}) async {
|
||||
updateChangeAddresses();
|
||||
|
||||
if (changeAddresses.isEmpty) {
|
||||
final newAddresses = await _createNewAddresses(gap, isChange: true);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
if (currentChangeAddressIndex >= changeAddresses.length) {
|
||||
currentChangeAddressIndex = 0;
|
||||
}
|
||||
|
@ -326,13 +324,20 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
final newAddressIndex = addressesByReceiveType.fold(
|
||||
0, (int acc, addressRecord) => addressRecord.isChange == false ? acc + 1 : acc);
|
||||
|
||||
final derivationInfo = BitcoinAddressUtils.getDerivationFromType(addressPageType);
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(isChange: false, index: newAddressIndex, addressType: addressPageType),
|
||||
getAddress(
|
||||
isChange: false,
|
||||
index: newAddressIndex,
|
||||
addressType: addressPageType,
|
||||
derivationInfo: derivationInfo,
|
||||
),
|
||||
index: newAddressIndex,
|
||||
isChange: false,
|
||||
name: label,
|
||||
type: addressPageType,
|
||||
network: network,
|
||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(addressPageType),
|
||||
);
|
||||
_allAddresses.add(address);
|
||||
Future.delayed(Duration.zero, () => updateAddressesByMatch());
|
||||
|
@ -343,6 +348,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
required bool isChange,
|
||||
required int index,
|
||||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
@ -351,17 +357,28 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
required bool isChange,
|
||||
required int index,
|
||||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) {
|
||||
return generateAddress(isChange: isChange, index: index, addressType: addressType)
|
||||
.toAddress(network);
|
||||
return generateAddress(
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
addressType: addressType,
|
||||
derivationInfo: derivationInfo,
|
||||
).toAddress(network);
|
||||
}
|
||||
|
||||
Future<String> getAddressAsync({
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) async =>
|
||||
getAddress(isChange: isChange, index: index, addressType: addressType);
|
||||
getAddress(
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
addressType: addressType,
|
||||
derivationInfo: derivationInfo,
|
||||
);
|
||||
|
||||
@action
|
||||
void addBitcoinAddressTypes() {
|
||||
|
@ -551,23 +568,41 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
required bool isChange,
|
||||
required int gap,
|
||||
required BitcoinAddressType type,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) async {
|
||||
print("_allAddresses: ${_allAddresses.length}");
|
||||
final newAddresses = await _createNewAddresses(gap, isChange: isChange, type: type);
|
||||
final newAddresses = await _createNewAddresses(
|
||||
gap,
|
||||
isChange: isChange,
|
||||
type: type,
|
||||
derivationInfo: derivationInfo,
|
||||
);
|
||||
addAddresses(newAddresses);
|
||||
print("_allAddresses: ${_allAddresses.length}");
|
||||
return newAddresses;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> generateInitialAddresses({required BitcoinAddressType type}) async {
|
||||
await discoverAddresses(isChange: false, gap: defaultReceiveAddressesCount, type: type);
|
||||
await discoverAddresses(isChange: true, gap: defaultChangeAddressesCount, type: type);
|
||||
// TODO: try all other derivations
|
||||
final derivationInfo = BitcoinAddressUtils.getDerivationFromType(type);
|
||||
|
||||
await discoverAddresses(
|
||||
isChange: false,
|
||||
gap: defaultReceiveAddressesCount,
|
||||
type: type,
|
||||
derivationInfo: derivationInfo,
|
||||
);
|
||||
await discoverAddresses(
|
||||
isChange: true,
|
||||
gap: defaultChangeAddressesCount,
|
||||
type: type,
|
||||
derivationInfo: derivationInfo,
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(
|
||||
int count, {
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
bool isChange = false,
|
||||
BitcoinAddressType? type,
|
||||
}) async {
|
||||
|
@ -580,11 +615,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
isChange: isChange,
|
||||
index: i,
|
||||
addressType: type ?? addressPageType,
|
||||
derivationInfo: derivationInfo,
|
||||
),
|
||||
index: i,
|
||||
isChange: isChange,
|
||||
type: type ?? addressPageType,
|
||||
network: network,
|
||||
derivationInfo: derivationInfo,
|
||||
);
|
||||
list.add(address);
|
||||
}
|
||||
|
@ -618,32 +655,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
updateAddressesByMatch();
|
||||
}
|
||||
|
||||
void _validateAddresses() {
|
||||
_allAddresses.forEach((element) async {
|
||||
if (element.type == SegwitAddresType.mweb) {
|
||||
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
|
||||
return;
|
||||
}
|
||||
if (!element.isChange &&
|
||||
element.address !=
|
||||
await getAddressAsync(
|
||||
isChange: false,
|
||||
index: element.index,
|
||||
addressType: element.type,
|
||||
)) {
|
||||
element.isChange = true;
|
||||
} else if (element.isChange &&
|
||||
element.address !=
|
||||
await getAddressAsync(
|
||||
isChange: true,
|
||||
index: element.index,
|
||||
addressType: element.type,
|
||||
)) {
|
||||
element.isChange = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> setAddressType(BitcoinAddressType type) async {
|
||||
_addressPageType = type;
|
||||
|
|
|
@ -3,7 +3,6 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
|||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
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/wallet_type.dart';
|
||||
|
@ -92,7 +91,7 @@ class ElectrumWalletSnapshot {
|
|||
|
||||
final derivationType = DerivationType
|
||||
.values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index];
|
||||
final derivationPath = data['derivationPath'] as String? ?? electrum_path;
|
||||
final derivationPath = data['derivationPath'] as String? ?? ELECTRUM_PATH;
|
||||
|
||||
try {
|
||||
regularAddressIndexByType = {
|
||||
|
|
|
@ -28,7 +28,12 @@ class LitecoinHardwareWalletService {
|
|||
final bip32 =
|
||||
Bip32Slip10Secp256k1.fromExtendedKey(xpub, xpubVersion).childKey(Bip32KeyIndex(0));
|
||||
|
||||
final address = P2wpkhAddress.fromBip32(bip32: bip32, isChange: false, index: 0);
|
||||
final address = P2wpkhAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: BitcoinDerivationInfos.LITECOIN,
|
||||
isChange: false,
|
||||
index: 0,
|
||||
);
|
||||
|
||||
accounts.add(HardwareAccountData(
|
||||
address: address.toAddress(LitecoinNetwork.mainnet),
|
||||
|
|
|
@ -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/electrum_derivations.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
|
@ -243,7 +242,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
walletInfo.derivationInfo ??= DerivationInfo();
|
||||
|
||||
// set the default if not present:
|
||||
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
|
||||
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? ELECTRUM_PATH;
|
||||
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
|
||||
|
||||
Uint8List? seedBytes = null;
|
||||
|
@ -435,13 +434,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
@action
|
||||
@override
|
||||
Future<void> rescan({
|
||||
required int height,
|
||||
int? chainTip,
|
||||
ScanData? scanData,
|
||||
bool? doSingleScan,
|
||||
bool? usingElectrs,
|
||||
}) async {
|
||||
Future<void> rescan({required int height}) async {
|
||||
_syncTimer?.cancel();
|
||||
await walletInfo.updateRestoreHeight(height);
|
||||
|
||||
|
|
|
@ -103,6 +103,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
index: e.key,
|
||||
type: SegwitAddresType.mweb,
|
||||
network: network,
|
||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(SegwitAddresType.p2wpkh),
|
||||
))
|
||||
.toList();
|
||||
addMwebAddresses(addressRecords);
|
||||
|
@ -121,12 +122,18 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
required bool isChange,
|
||||
required int index,
|
||||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) {
|
||||
if (addressType == SegwitAddresType.mweb) {
|
||||
return MwebAddress.fromAddress(address: mwebAddrs[0], network: network);
|
||||
}
|
||||
|
||||
return P2wpkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
return P2wpkhAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,12 +142,18 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
required bool isChange,
|
||||
required int index,
|
||||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) async {
|
||||
if (addressType == SegwitAddresType.mweb) {
|
||||
await ensureMwebAddressUpToIndexExists(index);
|
||||
}
|
||||
|
||||
return getAddress(isChange: isChange, index: index, addressType: addressType);
|
||||
return getAddress(
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
addressType: addressType,
|
||||
derivationInfo: derivationInfo,
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -194,6 +207,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
index: 0,
|
||||
type: SegwitAddresType.mweb,
|
||||
network: network,
|
||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(SegwitAddresType.p2wpkh),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -411,10 +411,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: google_identity_services_web
|
||||
sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6"
|
||||
sha256: "0c56c2c5d60d6dfaf9725f5ad4699f04749fb196ee5a70487a46ef184837ccf6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1+4"
|
||||
version: "0.3.0+2"
|
||||
googleapis_auth:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -467,10 +467,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
version: "1.2.0"
|
||||
http2:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -845,10 +845,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2"
|
||||
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.2.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1041,18 +1041,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.4.2"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
|
||||
sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.5"
|
||||
version: "2.4.3"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -153,6 +153,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
isChange: addr.isChange,
|
||||
type: P2pkhAddressType.p2pkh,
|
||||
network: BitcoinCashNetwork.mainnet,
|
||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(P2pkhAddressType.p2pkh),
|
||||
);
|
||||
} catch (_) {
|
||||
return BitcoinAddressRecord(
|
||||
|
@ -161,6 +162,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
isChange: addr.isChange,
|
||||
type: P2pkhAddressType.p2pkh,
|
||||
network: BitcoinCashNetwork.mainnet,
|
||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(P2pkhAddressType.p2pkh),
|
||||
);
|
||||
}
|
||||
}).toList(),
|
||||
|
|
|
@ -24,6 +24,12 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
|
|||
required bool isChange,
|
||||
required int index,
|
||||
required BitcoinAddressType addressType,
|
||||
required BitcoinDerivationInfo derivationInfo,
|
||||
}) =>
|
||||
P2pkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
P2pkhAddress.fromDerivation(
|
||||
bip32: bip32,
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -196,9 +196,6 @@ class WalletInfo extends HiveObject {
|
|||
@HiveField(24)
|
||||
List<String>? manualAddresses;
|
||||
|
||||
|
||||
|
||||
|
||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||
|
||||
set yatLastUsedAddress(String address) {
|
||||
|
|
Loading…
Reference in a new issue