Skip to content

Commit

Permalink
feat: scanning and adding addresses working with getTweaks, add btc S…
Browse files Browse the repository at this point in the history
…P address type
  • Loading branch information
rafael-xmr committed Feb 27, 2024
1 parent 2cbf1dc commit 3c88146
Show file tree
Hide file tree
Showing 42 changed files with 494 additions and 485 deletions.
134 changes: 100 additions & 34 deletions cw_bitcoin/lib/bitcoin_address_record.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/script_hash.dart' as sh;

class BitcoinAddressRecord {
BitcoinAddressRecord(
abstract class BaseBitcoinAddressRecord {
BaseBitcoinAddressRecord(
this.address, {
required this.index,
this.isHidden = false,
Expand All @@ -14,41 +14,14 @@ class BitcoinAddressRecord {
String name = '',
bool isUsed = false,
required this.type,
String? scriptHash,
required this.network,
this.silentPaymentTweak,
}) : _txCount = txCount,
_balance = balance,
_name = name,
_isUsed = isUsed,
scriptHash =
scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null);

factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) {
final decoded = json.decode(jsonSource) as Map;

return BitcoinAddressRecord(
decoded['address'] as String,
index: decoded['index'] as int,
isHidden: decoded['isHidden'] as bool? ?? false,
isUsed: decoded['isUsed'] as bool? ?? false,
txCount: decoded['txCount'] as int? ?? 0,
name: decoded['name'] as String? ?? '',
balance: decoded['balance'] as int? ?? 0,
type: decoded['type'] != null && decoded['type'] != ''
? BitcoinAddressType.values
.firstWhere((type) => type.toString() == decoded['type'] as String)
: SegwitAddresType.p2wpkh,
scriptHash: decoded['scriptHash'] as String?,
network: (decoded['network'] as String?) == null
? network
: BasedUtxoNetwork.fromName(decoded['network'] as String),
silentPaymentTweak: decoded['silentPaymentTweak'] as String?,
);
}
_isUsed = isUsed;

@override
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address;

final String address;
bool isHidden;
Expand All @@ -57,9 +30,7 @@ class BitcoinAddressRecord {
int _balance;
String _name;
bool _isUsed;
String? scriptHash;
BasedUtxoNetwork? network;
final String? silentPaymentTweak;

int get txCount => _txCount;

Expand All @@ -76,18 +47,60 @@ class BitcoinAddressRecord {
void setAsUsed() => _isUsed = true;
void setNewName(String label) => _name = label;

@override
int get hashCode => address.hashCode;

String get cashAddr => bitbox.Address.toCashAddress(address);

BitcoinAddressType type;

String toJSON();
}

class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
BitcoinAddressRecord(
super.address, {
required super.index,
super.isHidden = false,
super.txCount = 0,
super.balance = 0,
super.name = '',
super.isUsed = false,
required super.type,
String? scriptHash,
required super.network,
}) : scriptHash =
scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null);

factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) {
final decoded = json.decode(jsonSource) as Map;

return BitcoinAddressRecord(
decoded['address'] as String,
index: decoded['index'] as int,
isHidden: decoded['isHidden'] as bool? ?? false,
isUsed: decoded['isUsed'] as bool? ?? false,
txCount: decoded['txCount'] as int? ?? 0,
name: decoded['name'] as String? ?? '',
balance: decoded['balance'] as int? ?? 0,
type: decoded['type'] != null && decoded['type'] != ''
? BitcoinAddressType.values
.firstWhere((type) => type.toString() == decoded['type'] as String)
: SegwitAddresType.p2wpkh,
scriptHash: decoded['scriptHash'] as String?,
network: (decoded['network'] as String?) == null
? network
: BasedUtxoNetwork.fromName(decoded['network'] as String),
);
}

String? scriptHash;

String updateScriptHash(BasedUtxoNetwork network) {
scriptHash = sh.scriptHash(address, network: network);
return scriptHash!;
}

@override
String toJSON() => json.encode({
'address': address,
'index': index,
Expand All @@ -99,6 +112,59 @@ class BitcoinAddressRecord {
'type': type.toString(),
'scriptHash': scriptHash,
'network': network?.value,
});
}

class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
BitcoinSilentPaymentAddressRecord(
super.address, {
required super.index,
super.isHidden = false,
super.txCount = 0,
super.balance = 0,
super.name = '',
super.isUsed = false,
required super.type,
required this.silentPaymentTweak,
required super.network,
});

factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource,
{BasedUtxoNetwork? network}) {
final decoded = json.decode(jsonSource) as Map;

return BitcoinSilentPaymentAddressRecord(
decoded['address'] as String,
index: decoded['index'] as int,
isHidden: decoded['isHidden'] as bool? ?? false,
isUsed: decoded['isUsed'] as bool? ?? false,
txCount: decoded['txCount'] as int? ?? 0,
name: decoded['name'] as String? ?? '',
balance: decoded['balance'] as int? ?? 0,
type: decoded['type'] != null && decoded['type'] != ''
? BitcoinAddressType.values
.firstWhere((type) => type.toString() == decoded['type'] as String)
: SegwitAddresType.p2wpkh,
network: (decoded['network'] as String?) == null
? network
: BasedUtxoNetwork.fromName(decoded['network'] as String),
silentPaymentTweak: decoded['silentPaymentTweak'] as String,
);
}

final String silentPaymentTweak;

@override
String toJSON() => json.encode({
'address': address,
'index': index,
'isHidden': isHidden,
'isUsed': isUsed,
'txCount': txCount,
'name': name,
'balance': balance,
'type': type.toString(),
'network': network?.value,
'silentPaymentTweak': silentPaymentTweak,
});
}
2 changes: 1 addition & 1 deletion cw_bitcoin/lib/bitcoin_receive_page_option.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ class BitcoinReceivePageOption implements ReceivePageOption {
}

static const all = [
BitcoinReceivePageOption.silent_payments,
BitcoinReceivePageOption.p2wpkh,
BitcoinReceivePageOption.p2sh,
BitcoinReceivePageOption.p2tr,
BitcoinReceivePageOption.p2wsh,
BitcoinReceivePageOption.p2pkh,
BitcoinReceivePageOption.silent_payments,
];

BitcoinAddressType toType() {
Expand Down
6 changes: 3 additions & 3 deletions cw_bitcoin/lib/bitcoin_unspent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_core/unspent_transaction_output.dart';

class BitcoinUnspent extends Unspent {
BitcoinUnspent(BitcoinAddressRecord addressRecord, String hash, int value, int vout,
BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout,
{this.silentPaymentTweak, this.type})
: bitcoinAddressRecord = addressRecord,
super(addressRecord.address, hash, value, vout, null);

factory BitcoinUnspent.fromJSON(BitcoinAddressRecord address, Map<String, dynamic> json) =>
factory BitcoinUnspent.fromJSON(BaseBitcoinAddressRecord address, Map<String, dynamic> json) =>
BitcoinUnspent(
address,
json['tx_hash'] as String,
Expand All @@ -32,7 +32,7 @@ class BitcoinUnspent extends Unspent {
return json;
}

final BitcoinAddressRecord bitcoinAddressRecord;
final BaseBitcoinAddressRecord bitcoinAddressRecord;
String? silentPaymentTweak;
BitcoinAddressType? type;
}
22 changes: 16 additions & 6 deletions cw_bitcoin/lib/bitcoin_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex,
List<BitcoinAddressRecord>? initialSilentAddresses,
List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
int initialSilentAddressIndex = 0,
SilentPaymentOwner? silentAddress,
}) : super(
Expand Down Expand Up @@ -59,9 +59,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
network: networkParam ?? network,
);
hasSilentPaymentsScanning = addressPageType == SilentPaymentsAddresType.p2sp.toString();

autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});

reaction((_) => walletAddresses.addressPageType, (BitcoinAddressType addressPageType) {
final prev = hasSilentPaymentsScanning;
hasSilentPaymentsScanning = addressPageType == SilentPaymentsAddresType.p2sp;
if (prev != hasSilentPaymentsScanning) {
startSync();
}
});
}

static Future<BitcoinWallet> create({
Expand All @@ -72,7 +82,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
String? addressPageType,
BasedUtxoNetwork? network,
List<BitcoinAddressRecord>? initialAddresses,
List<BitcoinAddressRecord>? initialSilentAddresses,
List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex,
Expand All @@ -88,11 +98,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialSilentAddresses: initialSilentAddresses,
initialSilentAddressIndex: initialSilentAddressIndex,
silentAddress: await SilentPaymentOwner.fromPrivateKeys(
scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
b_scan: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
seedBytes,
network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
).derivePath(SCAN_PATH).privKey!),
spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
b_spend: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
seedBytes,
network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
).derivePath(SPEND_PATH).privKey!),
Expand Down Expand Up @@ -125,11 +135,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialSilentAddresses: snp.silentAddresses,
initialSilentAddressIndex: snp.silentAddressIndex,
silentAddress: await SilentPaymentOwner.fromPrivateKeys(
scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
b_scan: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
seedBytes,
network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
).derivePath(SCAN_PATH).privKey!),
spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
b_spend: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
seedBytes,
network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
).derivePath(SPEND_PATH).privKey!),
Expand Down
4 changes: 2 additions & 2 deletions cw_bitcoin/lib/electrum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ class ElectrumClient {
Future<Map<String, dynamic>> getHeader({required int height}) async =>
await call(method: 'blockchain.block.get_header', params: [height]) as Map<String, dynamic>;

Future<Map<String, dynamic>> getTweaks({required int height}) async =>
await call(method: 'blockchain.block.tweaks', params: [height]) as Map<String, dynamic>;
Future<List<dynamic>> getTweaks({required int height}) async =>
await callWithTimeout(method: 'blockchain.block.tweaks', params: [height]) as List<dynamic>;

Future<double> estimatefee({required int p}) =>
call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) {
Expand Down
Loading

0 comments on commit 3c88146

Please sign in to comment.