Nuke Zcash from build infra (chain already disabled at config layer)

Removed:
- cw_zcash/ directory (Dart wrapper, no longer needed)
- assets/zcash_node_list.yml
- scripts/prepare_zcash.sh
- scripts/{android,ios,linux,macos}/build_zcash.sh
- scripts/android/docker/Dockerfile.zcash
- scripts/zcash_lib/ if cloned (rebuildable from prepare_zcash.sh which is also gone)

Edited:
- scripts/lint.sh: replaced cw_zcash loop target with the actual modules we
  ship (cw_core, cw_monero, cw_wownero, cw_bitcoin, cw_bitcoin_cash,
  cw_dogecoin, cw_evm, cw_nano).
- hashwallet.bat: trimmed mobx codegen loop to enabled chains.
- pubspec_base.yaml: dropped zcash_node_list.yml + zcash_lib sapling
  asset references.
- scripts/android/docker/{build.sh,Dockerfile.final}: removed zcash image
  build + COPY stage.

tool/configure.dart still has the zcash codegen functions intact (they
just don't fire because we don't pass --zcash); leaving them as harmless
dead code rather than risking a regression in the codegen pipeline.
This commit is contained in:
jwinterm
2026-05-14 17:51:19 -04:00
parent 4fe96feb1b
commit c64c740ba3
31 changed files with 3 additions and 3180 deletions
-12
View File
@@ -1,12 +0,0 @@
- uri: zec.rocks:443
is_default: false
useSSL: true
isEnabledForAutoSwitching: true
- uri: zec-node.cakewallet.com:443
is_default: true
useSSL: true
isEnabledForAutoSwitching: true
- uri: 2c4whzg26j6hgjh22rxynj3oig4gm22ga7x74weclujywxye23v3u5id.onion:9067
useSSL: false
- uri: bvp2l442g5ogma7rywzahm7eqyhpp3g26n3gvvxeioqn5csio2ir6myd.onion:9067
useSSL: false
-7
View File
@@ -1,7 +0,0 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
-3
View File
@@ -1,3 +0,0 @@
## 1.0.0
- Initial version.
-1
View File
@@ -1 +0,0 @@
# cw_zcash
-31
View File
@@ -1,31 +0,0 @@
analyzer:
errors:
implementation_imports: ignore
must_be_immutable: ignore
overridden_fields: ignore
exclude:
- "**.g.dart"
- "external/**"
linter:
rules:
unawaited_futures: true
avoid_print: false
parameter_assignments: true
prefer_const_declarations: true
prefer_final_fields: true
prefer_final_in_for_each: true
prefer_final_locals: true
prefer_final_parameters: true
avoid_void_async: true
require_trailing_commas: true
sort_child_properties_last: true
sort_constructors_first: true
sort_unnamed_constructors_first: true
unnecessary_async: true
unnecessary_await_in_return: true
unnecessary_string_interpolations: true
always_declare_return_types: true
formatter:
page_width: 100
-15
View File
@@ -1,15 +0,0 @@
/// Support for doing something awesome.
///
/// More dartdocs go here.
library;
export 'src/zcash_balance.dart';
export 'src/zcash_mnemonic_is_incorrect_exception.dart';
export 'src/zcash_transaction_info.dart';
export 'src/zcash_transactions.dart';
export 'src/zcash_wallet.dart';
export 'src/zcash_wallet_service.dart';
export 'src/models.dart';
export 'src/zcash_transaction_credentials.dart';
export 'src/pending_zcash_transaction.dart';
export 'src/zcash_receive_page_options.dart';
-196
View File
@@ -1,196 +0,0 @@
import 'dart:io';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:path/path.dart' as p;
import 'package:zkool/src/rust/api/account.dart' as zkool_account;
import 'package:zkool/src/rust/api/coin.dart' as zkool_coin;
import 'package:zkool/src/rust/api/sync.dart' as zkool_sync;
import 'package:zkool/src/rust/api/pay.dart' as zkool_pay;
import 'package:zkool/src/rust/api/key.dart' as zkool_key;
import 'package:zkool/src/rust/api/network.dart' as zkool_network;
import 'package:zkool/src/rust/pay.dart' as zkool_paydart;
import 'package:zkool/src/rust/frb_generated.dart' as zkool_frb;
class ZkoolSweep {
ZkoolSweep({
required this.currentBalance,
required this.cacheDir,
required this.seed,
required this.passphrase,
required this.address,
required this.url,
required this.height,
});
final int currentBalance;
final String seed;
final String cacheDir;
final String passphrase;
final String address;
final String url;
final int height;
static SweepUpdate msg = SweepUpdate(message: 'Idling', currentHeight: 0, networkHeight: 0);
Future<void> start() async {
try {
await _init();
if (!zkool_key.isValidAddress(address: address)) {
msg.message = "$address is not valid";
return;
}
msg = SweepUpdate(message: 'Starting import', currentHeight: 0, networkHeight: 0);
await _newAccount();
msg.message = "Starting sync";
await warpSync();
msg.message = "Waiting";
while (msg.blocksLeft != 0 && msg.networkHeight != 0) {
await Future.delayed(Duration(seconds: 1));
}
await Future.delayed(Duration(seconds: 5));
final balance = await getBalance();
if (balance.toInt() == balance) {
msg.message = "No need to sweep, balance is the same";
return;
}
msg.message = "Sweeping";
await sweep(balance);
} catch (e) {
msg.message = e.toString();
printV("zkool_sweep: $e");
rethrow;
}
}
Future<BigInt> getBalance() async {
final b = await zkool_sync.balance(c: c);
return b.field0.reduce((final a, final b) => a + b);
}
Future<void> sweep(final BigInt amount) async {
msg.message = "prepare";
final tx = await zkool_pay.prepare(
recipients: [zkool_paydart.Recipient(address: address, amount: amount)],
options: zkool_pay.PaymentOptions(
srcPools: 7,
recipientPaysFee: true,
smartTransparent: false,
),
c: c,
);
msg.message = "getLatestHeight";
final height = await getLatestHeight();
msg.message = "signTransaction";
final signTx = await zkool_pay.signTransaction(pczt: tx, c: c);
msg.message = "extractTransaction";
final txBytes = await zkool_pay.extractTransaction(package: signTx);
msg.message = "broadcastTransaction";
final result = await zkool_pay.broadcastTransaction(height: height, txBytes: txBytes, c: c);
msg.message = "result";
if (result.isEmpty) {
msg.message = "Unknown error";
throw Exception(result);
}
}
static var c = zkool_coin.Coin();
static bool _isInit = false;
File get dbFile => File(p.join(cacheDir, "zkool-tmp-mirror.db"));
Future<int> warpSync() async {
final currentHeight = await getLatestHeight();
msg.networkHeight = currentHeight;
final sync = zkool_sync.synchronize(
accounts: [currentAccount],
currentHeight: currentHeight,
actionsPerSync: 10000,
transparentLimit: 100,
checkpointAge: 200,
c: c,
);
bool success = false;
await sync.listen(
(final syncProgress) {
msg.currentHeight = syncProgress.height;
msg.message = "Sync ${syncProgress.height} / ${syncProgress.time}";
},
onError: (final e) {
msg.message = e.toString();
},
onDone: () {
msg.message = "Sync done!";
success = true;
},
);
if (msg.currentHeight != currentHeight && msg.currentHeight != 0) {
msg.message = "Canceling sync";
await zkool_sync.cancelSync();
await Future.delayed(Duration(seconds: 1));
return warpSync();
}
return 0;
}
Future<int> getLatestHeight() async {
try {
return await zkool_network.getCurrentHeight(c: c);
} catch (e) {
printV("getLatestHeight: $e");
}
return 0;
}
Future<void> _newAccount() async {
final id = await zkool_account.newAccount(
na: zkool_account.NewAccount(
name: 'restoretmp' + DateTime.now().microsecondsSinceEpoch.toString(),
restore: true,
passphrase: "",
key: seed,
aindex: 0,
birth: height,
folder: '',
useInternal: true,
internal: false,
ledger: false,
),
c: c,
);
currentAccount = id;
c = await c.setAccount(account: id);
}
int currentAccount = 0;
Future<void> _init() async {
if (_isInit) return;
_isInit = true;
printV(".init()");
await zkool_frb.RustLib.init();
await zkool_network.initDatadir(directory: dbFile.parent.path);
c = await c.openDatabase(dbFilepath: dbFile.path, password: 'cw_zcash_migration');
c = c.setLwd(url: url, serverType: 0);
printV(".init(): Done");
}
}
class SweepUpdate {
SweepUpdate({required this.message, required this.currentHeight, required this.networkHeight});
String message;
int currentHeight;
int networkHeight;
double get progress {
if (networkHeight == 0) return 0.01;
return currentHeight / networkHeight;
}
int get blocksLeft => networkHeight - currentHeight;
Map<String, dynamic> toJson() => {
"message": message,
"currentHeight": currentHeight,
"networkHeight": networkHeight,
"progress": progress,
};
}
-41
View File
@@ -1,41 +0,0 @@
import 'package:cw_core/wallet_credentials.dart';
class ZcashNewWalletCredentials extends WalletCredentials {
ZcashNewWalletCredentials({
required final String name,
final String? password,
required final String? passphrase,
final String? mnemonic,
final int? seedPhraseLength,
}) : super(
name: name,
password: password,
passphrase: passphrase,
seedPhraseLength: seedPhraseLength,
) {
this.mnemonic = mnemonic;
}
String? mnemonic;
}
class ZcashFromSeedWalletCredentials extends WalletCredentials {
ZcashFromSeedWalletCredentials({
required final String name,
final String? password,
required final String? passphrase,
required this.seed,
required super.height,
}) : super(name: name, password: password, passphrase: passphrase);
final String? seed;
}
class ZcashFromKeysWalletCredentials extends WalletCredentials {
ZcashFromKeysWalletCredentials({
required final String name,
final String? password,
required final int? height,
required this.privateKey,
}) : super(name: name, password: password, height: height);
final String? privateKey;
}
@@ -1,103 +0,0 @@
import 'dart:convert';
import 'dart:math';
import 'package:cw_core/output_info.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_zcash/cw_zcash.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_zcash/src/zcash_taddress_rotation.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'package:warp_api/warp_api.dart';
class PendingZcashTransaction with PendingTransaction {
PendingZcashTransaction({
required this.zcashWallet,
required this.credentials,
required this.txPlan,
required this.fee,
required this.availableBalance,
});
final ZcashWallet zcashWallet;
final ZcashTransactionCredentials credentials;
final String txPlan;
String? _txId;
final int availableBalance;
@override
String get id => _txId ?? '';
@override
String get hex => '';
@override
String get amountFormatted {
return walletTypeToCryptoCurrency(WalletType.zcash).formatAmount(BigInt.from(totalAmount));
}
int get totalAmount {
final isAll = credentials.outputs.fold<bool>(false, (final a, final b) => a || (b.sendAll));
if (isAll) {
return availableBalance;
}
return credentials.outputs.fold<int>(
0,
(final a, final b) => a + (b.formattedCryptoAmount ?? 0),
);
}
@override
String get feeFormatted =>
'$feeFormattedValue ${walletTypeToCryptoCurrency(WalletType.zcash).title}';
@override
late String feeFormattedValue = walletTypeToCryptoCurrency(
WalletType.zcash,
).formatAmount(BigInt.from(fee));
int fee;
@override
Future<void> commit() async {
_txId = await ZcashWalletService.runInDbMutex(
() => WarpApi.signAndBroadcast(ZcashWalletBase.coin, zcashWallet.accountId, txPlan),
);
ZcashWalletBase.temporarySentTx[zcashWallet.accountId] ??= [];
ZcashWalletBase.temporarySentTx[zcashWallet.accountId]?.add(
ShieldedTx(
base64.decode(
ZcashTaddressRotation.flatBuffersPack(
ShieldedTxT(
id: Random().nextInt(pow(2, 32).toInt()),
txId: _txId,
height: 0,
timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000,
value: -totalAmount,
).pack,
),
),
),
);
await ZcashTransactionInfo.addCachedDestinationAddress(
_txId ?? '',
credentials.outputs.reduce((final o1, final o2) {
return OutputInfo(
address: o1.address + "," + o2.address,
sendAll: false,
isParsedAddress: false,
);
}).address,
);
await zcashWallet.updateTransactions();
await zcashWallet.updateBalance();
}
@override
Future<Map<String, String>> commitUR() {
throw UnimplementedError('UR not supported for Zcash');
}
@override
bool shouldCommitUR() => false;
}
-296
View File
@@ -1,296 +0,0 @@
/// CRC32
/// Copyright (C) 2012, Kai Sellgren
/// Licensed under the MIT License.
/// http://www.opensource.org/licenses/mit-license.php
/// source: https://raw.githubusercontent.com/kaisellgren/CRC32/refs/heads/master/lib/crc32.dart
// ignore_for_file: parameter_assignments
import 'dart:convert';
/// Computes Cyclic Redundancy Check values.
class CRC32 {
/// Computes a CRC32 value for the given input.
///
/// The return value is an unsigned integer.
///
/// You may optionally specify the beginning CRC value.
static int compute(dynamic input, [int crc = 0]) {
if (input == null) throw new ArgumentError.notNull('input');
if (input is String) input = utf8.encode(input);
crc = crc ^ (0xffffffff);
for (final byte in input) {
if (!(byte >= -128 && byte <= 255))
throw new FormatException("Invalid value in input: $byte");
final x = CRC32._table[(crc ^ byte) & 0xff];
crc = (crc & 0xffffffff) >> 8; // crc >>> 8 (32-bit unsigned integer)
crc ^= x;
}
crc = crc ^ (0xffffffff);
// Turns the signed integer into an unsigned integer.
return crc & 0xffffffff;
}
static const _table = const [
0x00000000,
0x77073096,
0xEE0E612C,
0x990951BA,
0x076DC419,
0x706AF48F,
0xE963A535,
0x9E6495A3,
0x0EDB8832,
0x79DCB8A4,
0xE0D5E91E,
0x97D2D988,
0x09B64C2B,
0x7EB17CBD,
0xE7B82D07,
0x90BF1D91,
0x1DB71064,
0x6AB020F2,
0xF3B97148,
0x84BE41DE,
0x1ADAD47D,
0x6DDDE4EB,
0xF4D4B551,
0x83D385C7,
0x136C9856,
0x646BA8C0,
0xFD62F97A,
0x8A65C9EC,
0x14015C4F,
0x63066CD9,
0xFA0F3D63,
0x8D080DF5,
0x3B6E20C8,
0x4C69105E,
0xD56041E4,
0xA2677172,
0x3C03E4D1,
0x4B04D447,
0xD20D85FD,
0xA50AB56B,
0x35B5A8FA,
0x42B2986C,
0xDBBBC9D6,
0xACBCF940,
0x32D86CE3,
0x45DF5C75,
0xDCD60DCF,
0xABD13D59,
0x26D930AC,
0x51DE003A,
0xC8D75180,
0xBFD06116,
0x21B4F4B5,
0x56B3C423,
0xCFBA9599,
0xB8BDA50F,
0x2802B89E,
0x5F058808,
0xC60CD9B2,
0xB10BE924,
0x2F6F7C87,
0x58684C11,
0xC1611DAB,
0xB6662D3D,
0x76DC4190,
0x01DB7106,
0x98D220BC,
0xEFD5102A,
0x71B18589,
0x06B6B51F,
0x9FBFE4A5,
0xE8B8D433,
0x7807C9A2,
0x0F00F934,
0x9609A88E,
0xE10E9818,
0x7F6A0DBB,
0x086D3D2D,
0x91646C97,
0xE6635C01,
0x6B6B51F4,
0x1C6C6162,
0x856530D8,
0xF262004E,
0x6C0695ED,
0x1B01A57B,
0x8208F4C1,
0xF50FC457,
0x65B0D9C6,
0x12B7E950,
0x8BBEB8EA,
0xFCB9887C,
0x62DD1DDF,
0x15DA2D49,
0x8CD37CF3,
0xFBD44C65,
0x4DB26158,
0x3AB551CE,
0xA3BC0074,
0xD4BB30E2,
0x4ADFA541,
0x3DD895D7,
0xA4D1C46D,
0xD3D6F4FB,
0x4369E96A,
0x346ED9FC,
0xAD678846,
0xDA60B8D0,
0x44042D73,
0x33031DE5,
0xAA0A4C5F,
0xDD0D7CC9,
0x5005713C,
0x270241AA,
0xBE0B1010,
0xC90C2086,
0x5768B525,
0x206F85B3,
0xB966D409,
0xCE61E49F,
0x5EDEF90E,
0x29D9C998,
0xB0D09822,
0xC7D7A8B4,
0x59B33D17,
0x2EB40D81,
0xB7BD5C3B,
0xC0BA6CAD,
0xEDB88320,
0x9ABFB3B6,
0x03B6E20C,
0x74B1D29A,
0xEAD54739,
0x9DD277AF,
0x04DB2615,
0x73DC1683,
0xE3630B12,
0x94643B84,
0x0D6D6A3E,
0x7A6A5AA8,
0xE40ECF0B,
0x9309FF9D,
0x0A00AE27,
0x7D079EB1,
0xF00F9344,
0x8708A3D2,
0x1E01F268,
0x6906C2FE,
0xF762575D,
0x806567CB,
0x196C3671,
0x6E6B06E7,
0xFED41B76,
0x89D32BE0,
0x10DA7A5A,
0x67DD4ACC,
0xF9B9DF6F,
0x8EBEEFF9,
0x17B7BE43,
0x60B08ED5,
0xD6D6A3E8,
0xA1D1937E,
0x38D8C2C4,
0x4FDFF252,
0xD1BB67F1,
0xA6BC5767,
0x3FB506DD,
0x48B2364B,
0xD80D2BDA,
0xAF0A1B4C,
0x36034AF6,
0x41047A60,
0xDF60EFC3,
0xA867DF55,
0x316E8EEF,
0x4669BE79,
0xCB61B38C,
0xBC66831A,
0x256FD2A0,
0x5268E236,
0xCC0C7795,
0xBB0B4703,
0x220216B9,
0x5505262F,
0xC5BA3BBE,
0xB2BD0B28,
0x2BB45A92,
0x5CB36A04,
0xC2D7FFA7,
0xB5D0CF31,
0x2CD99E8B,
0x5BDEAE1D,
0x9B64C2B0,
0xEC63F226,
0x756AA39C,
0x026D930A,
0x9C0906A9,
0xEB0E363F,
0x72076785,
0x05005713,
0x95BF4A82,
0xE2B87A14,
0x7BB12BAE,
0x0CB61B38,
0x92D28E9B,
0xE5D5BE0D,
0x7CDCEFB7,
0x0BDBDF21,
0x86D3D2D4,
0xF1D4E242,
0x68DDB3F8,
0x1FDA836E,
0x81BE16CD,
0xF6B9265B,
0x6FB077E1,
0x18B74777,
0x88085AE6,
0xFF0F6A70,
0x66063BCA,
0x11010B5C,
0x8F659EFF,
0xF862AE69,
0x616BFFD3,
0x166CCF45,
0xA00AE278,
0xD70DD2EE,
0x4E048354,
0x3903B3C2,
0xA7672661,
0xD06016F7,
0x4969474D,
0x3E6E77DB,
0xAED16A4A,
0xD9D65ADC,
0x40DF0B66,
0x37D83BF0,
0xA9BCAE53,
0xDEBB9EC5,
0x47B2CF7F,
0x30B5FFE9,
0xBDBDF21C,
0xCABAC28A,
0x53B39330,
0x24B4A3A6,
0xBAD03605,
0xCDD70693,
0x54DE5729,
0x23D967BF,
0xB3667A2E,
0xC4614AB8,
0x5D681B02,
0x2A6F2B94,
0xB40BBE37,
0xC30C8EA1,
0x5A05DF1B,
0x2D02EF8D,
];
}
-32
View File
@@ -1,32 +0,0 @@
import 'package:cw_core/balance.dart';
import 'package:cw_core/crypto_currency.dart';
class ZcashBalance extends Balance {
ZcashBalance({required this.confirmed, required this.unconfirmed, required final int frozen})
: _frozen = frozen,
super.fromInt(confirmed, unconfirmed);
factory ZcashBalance.zero() => ZcashBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
final int confirmed;
final int unconfirmed;
final int _frozen;
BigInt get frozen => BigInt.from(_frozen);
@override
String get formattedAvailableBalance {
return CryptoCurrency.zec.formatAmount(BigInt.from(confirmed));
}
@override
String get formattedAdditionalBalance {
if (unconfirmed == 0) return '0.0';
return CryptoCurrency.zec.formatAmount(BigInt.from(unconfirmed));
}
@override
String get formattedUnAvailableBalance {
if (frozen == 0) return '';
return CryptoCurrency.zec.formatAmount(BigInt.from(_frozen));
}
}
@@ -1,5 +0,0 @@
class ZcashMnemonicIsIncorrectException implements Exception {
@override
String toString() =>
'Zcash mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
}
@@ -1,102 +0,0 @@
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/utils/print_verbose.dart';
enum ZcashAddressType {
transparent,
transparentRotated,
shieldedSapling,
shieldedOrchard,
unifiedType,
}
class ZcashReceivePageOption implements ReceivePageOption {
factory ZcashReceivePageOption.fromType(final ZcashAddressType type) {
switch (type) {
case ZcashAddressType.transparent:
return transparent;
case ZcashAddressType.transparentRotated:
return transparentRotated;
case ZcashAddressType.shieldedSapling:
return shieldedSapling;
case ZcashAddressType.shieldedOrchard:
return shieldedOrchard;
case ZcashAddressType.unifiedType:
return unified;
}
}
const ZcashReceivePageOption._(
this.type,
this.value, {
this.iconPath,
this.description,
this.isCommon = false,
});
final String value;
final String? iconPath;
final String? description;
final bool isCommon;
final bool addAddressWord = false;
static const transparent = ZcashReceivePageOption._(
ZcashAddressType.transparent,
"Public",
description: "Static & Transparent",
iconPath: "assets/new-ui/address-type-picker-icons/zec/public.svg",
);
static const transparentRotated = ZcashReceivePageOption._(
ZcashAddressType.transparentRotated,
"Transparent",
description: "Disposable",
iconPath: "assets/new-ui/address-type-picker-icons/zec/transparent.svg",
isCommon: true,
);
static const shieldedSapling = ZcashReceivePageOption._(
ZcashAddressType.shieldedSapling,
"Legacy Shielded",
description: "Sapling",
iconPath: "assets/new-ui/address-type-picker-icons/zec/sapling.svg",
);
static const shieldedOrchard = ZcashReceivePageOption._(
ZcashAddressType.shieldedOrchard,
"Shielded",
description: "Default (Orchard)",
iconPath: "assets/new-ui/address-type-picker-icons/zec/shielded.svg",
isCommon: true,
);
static const unified = ZcashReceivePageOption._(
ZcashAddressType.unifiedType,
"Unified",
description: "Compatible Shielded",
iconPath: "assets/new-ui/address-type-picker-icons/zec/unified.svg",
isCommon: true,
);
final ZcashAddressType type;
String toString() {
return value;
}
static const all = [
ZcashReceivePageOption.shieldedOrchard,
ZcashReceivePageOption.shieldedSapling,
ZcashReceivePageOption.unified,
ZcashReceivePageOption.transparentRotated,
ZcashReceivePageOption.transparent,
];
ZcashAddressType toType() {
return type;
}
static ZcashAddressType typeFromString(final String str) {
for (int i = 0; i < ZcashAddressType.values.length; i++) {
if (str == ZcashAddressType.values[i].toString()) {
return ZcashAddressType.values[i];
}
}
printV("Not found for: $str");
return ZcashAddressType.shieldedOrchard;
}
}
@@ -1,445 +0,0 @@
// create offset wallets for T address rotation.
// Each address is considered one time use, if it receives less than 15000 zatoshi balance will be essentially burned.
//
// This class takes address index as constructor, and will create 10 addresses by default, if any of the addresses gets used
// it will be moved out of the pool
//
// T addresses are generated by creating new wallets with an extra seed word (or appending to "passphrase") the following
// :tgen:${CRC32(seed)}
// It is highly unlikely for users to create a wallet with that specific last word and add index to it
// We could leverage other fields such as native account index alone, but users may want to set that in future
// This solution is rather simple and if standard would get estabilished for this purpose it can be easily
// turned off and replaced with proper solution.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_zcash/src/util/crc32.dart';
import 'package:cw_zcash/src/zcash_wallet.dart';
import 'package:cw_zcash/src/zcash_wallet_service.dart';
import 'package:flutter/foundation.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'package:warp_api/warp_api.dart';
import 'package:flat_buffers/flat_buffers.dart' as fb;
import 'package:path/path.dart' as p;
class ZcashTaddressRotation {
ZcashTaddressRotation(this.accountIndex);
final int accountIndex;
static bool _isStarted = false;
static const coin = ZcashWalletBase.coin;
static String seedForOffset(final String seed) {
final seedWords = seed.split(" ");
if ([12, 24].contains(seedWords.length)) {
seedWords.add("");
}
final lastI = seedWords.length - 1;
final crc = CRC32.compute(seed);
seedWords[lastI] += ":tgen:${crc}";
return seedWords.join(" ");
}
static bool isSeedForWallet(final String? mainWallet, final String? subWallet) {
if (mainWallet == null || subWallet == null) return false;
return seedForOffset(mainWallet.trim()) == subWallet;
}
static Map<int, List<Account>> rotationAccounts = {};
static Map<int, List<Account>> rotationAccountsUsable = {};
static Map<int, List<ShieldedTx>> shieldedAccountsTx = {};
static Future<void> init() async {
printV("Deserializing previous state");
if (_isStarted) {
return;
}
_isStarted = true;
final pfw = await pathForWalletTypeDir(type: WalletType.zcash);
try {
final f = File(p.join(pfw, "zec-taddr.json"));
if (!f.existsSync()) {
f.writeAsBytesSync(serialize());
}
deserialize(f.readAsBytesSync());
} catch (e) {
printV("Failed to deserialize T address DB for zcash, it doesn't really matter: $e");
}
unawaited(
(() async {
await Future.delayed(Duration(seconds: 5));
return _jobRunner();
})(),
);
}
static Future<void> serializeToFile() async {
final pfw = await pathForWalletTypeDir(type: WalletType.zcash);
final f = File(p.join(pfw, "zec-taddr.json"));
f.writeAsBytesSync(serialize());
}
static Uint8List serialize() {
final data = {
"rotationAccounts": rotationAccounts.map(
(final k, final v) => MapEntry(
k.toRadixString(16),
v.map((final a) => flatBuffersPack(a.unpack().pack)).toList(),
),
),
if (kDebugMode)
"_rotationAccounts": rotationAccounts.map(
(final k, final v) =>
MapEntry(k.toRadixString(16), v.map((final a) => a.toString()).toList()),
),
"rotationAccountsUsable": rotationAccountsUsable.map(
(final k, final v) => MapEntry(
k.toRadixString(16),
v.map((final a) => flatBuffersPack(a.unpack().pack)).toList(),
),
),
if (kDebugMode)
"_rotationAccountsUsable": rotationAccountsUsable.map(
(final k, final v) =>
MapEntry(k.toRadixString(16), v.map((final a) => a.toString()).toList()),
),
"shieldedAccountsTx": shieldedAccountsTx.map(
(final k, final v) => MapEntry(
k.toRadixString(16),
v.map((final a) => flatBuffersPack(a.unpack().pack)).toList(),
),
),
if (kDebugMode)
"_shieldedAccountsTx": shieldedAccountsTx.map(
(final k, final v) =>
MapEntry(k.toRadixString(16), v.map((final a) => a.toString()).toList()),
),
};
if (kDebugMode) {
return utf8.encode(JsonEncoder.withIndent(" ").convert(data));
}
return utf8.encode(jsonEncode(data));
}
static String flatBuffersPack(final int Function(fb.Builder fbBuilder) pack) {
final fbBuilder = fb.Builder();
final offset = pack(fbBuilder);
fbBuilder.finish(offset);
return base64.encode(fbBuilder.buffer);
}
static void deserialize(final Uint8List bytes) {
// todo: replace keys with sums and reverse in serialize
// final List<String> seeds = [];
// for (int i = 0; i < accounts.length; i++) {
// final acc = accounts[i];
// final backup = WarpApi.getBackup(coin, acc.id);
// await WarpApi.transparentSync(coin, acc.id, syncHeight);
// seeds.add(backup.seed!);
// }
try {
final Map<dynamic, dynamic> data = jsonDecode(utf8.decode(bytes));
rotationAccounts = (data["rotationAccounts"] as Map<String, dynamic>).map(
(final k, final v) => MapEntry(
int.parse(k, radix: 16),
(v as List).map((final a) => Account(atob(a))).toList(),
),
);
rotationAccountsUsable = (data["rotationAccountsUsable"] as Map<String, dynamic>).map(
(final k, final v) => MapEntry(
int.parse(k, radix: 16),
(v as List).map((final a) => Account(atob(a))).toList(),
),
);
shieldedAccountsTx = (data["shieldedAccountsTx"] as Map<String, dynamic>).map(
(final k, final v) => MapEntry(
int.parse(k, radix: 16),
(v as List).map((final a) => ShieldedTx(atob(a))).toList(),
),
);
} catch (e) {
rethrow;
}
}
static Uint8List atob(final String value) =>
Uint8List.fromList(List<int>.from(base64.decode(value)));
static Future<void> createAndSweepTAddresses() async {
int chainHeight = 0;
try {
chainHeight = await WarpApi.getLatestHeight(coin);
} catch (e) {
printV("Error getting latest height: $e");
}
final dbHeight = WarpApi.getDbHeight(coin);
final height = dbHeight.unpack();
final syncHeight = height.height;
if (chainHeight != syncHeight) {
printV(
"Waiting for sync to finish: chainHeight(${chainHeight}) != syncHeight(${syncHeight})",
);
return;
}
final Map<int, List<ShieldedTx>> newShieldedAccountsTx = {};
final accounts = WarpApi.getAccountList(coin);
if (accounts.isEmpty) return;
final Map<int, String> seeds = {};
for (int i = 0; i < accounts.length; i++) {
final acc = accounts[i];
final backup = WarpApi.getBackup(coin, acc.id);
await WarpApi.transparentSync(coin, acc.id, syncHeight);
if (backup.seed == null) continue;
seeds[acc.id] = backup.seed!;
}
for (int i = 0; i < accounts.length; i++) {
final seed = seeds[accounts[i].id]!;
if ([12, 13, 24, 25].contains(seed.split(" ").length)) {
if (seed.split(" ").last.contains(":tgen:")) continue;
rotationAccounts[accounts[i].id] = [];
}
}
final raKeys = rotationAccounts.keys.toList();
seeds.removeWhere((_, final val) => raKeys.contains(val));
printV("raKeys: ${raKeys.length}");
for (int i = 0; i < raKeys.length; i++) {
final accs = accounts.where((final a) {
final seed = WarpApi.getBackup(coin, a.id).seed;
return isSeedForWallet(seeds[raKeys[i]], seed);
}).toList();
rotationAccounts[raKeys[i]]!.addAll(accs);
final acc = accountForSeed(seeds[raKeys[i]]!)!;
final txs = WarpApi.getTxsSync(coin, acc.id);
newShieldedAccountsTx[acc.id] ??= [];
newShieldedAccountsTx[acc.id]!.addAll(txs);
}
rotationAccountsUsable = rotationAccounts.map((final k, final v) => MapEntry(k, v.toList()));
printV("rotationAccounts: ${rotationAccounts.length}");
for (int i = 0; i < raKeys.length; i++) {
final acc = accountForSeed(seeds[raKeys[i]]!)!;
newShieldedAccountsTx[acc.id] = [];
for (int j = 0; j < (rotationAccounts[raKeys[i]]?.length ?? 0); j++) {
final txs = WarpApi.getTxsSync(coin, rotationAccounts[raKeys[i]]![j].id);
rotationAccounts[raKeys[i]]![j];
newShieldedAccountsTx[acc.id]!.addAll(txs);
}
rotationAccountsUsable[raKeys[i]]!.removeWhere((final a) {
final txs = WarpApi.getTxsSync(coin, a.id);
return txs.isNotEmpty;
});
// remove hidden addresses
final wis = await WalletInfo.selectList('type = ?', [WalletType.zcash.index]);
final List<String> hiddenAddresses_ = [];
for (int k = 0; k < wis.length; k++) {
final addrs = await wis[k].getHiddenAddresses();
final addr2 = await wis[k].getUsedAddresses();
hiddenAddresses_.addAll(addrs);
hiddenAddresses_.addAll(addr2);
}
final Set<String> hiddenAddresses = hiddenAddresses_.toSet();
rotationAccountsUsable[raKeys[i]]!.removeWhere((final a) {
return hiddenAddresses.contains(WarpApi.getTAddr(coin, a.id));
});
}
printV("rotationAccountsUsable: ${rotationAccountsUsable.length}");
bool didAddNewAccount = false;
for (int i = 0; i < raKeys.length; i++) {
if (rotationAccountsUsable[raKeys[i]]!.length < 5) {
final seed = seedForOffset(seeds[raKeys[i]]!);
final name = CRC32.compute(raKeys[i].toString()).toString();
final id = await ZcashWalletService.runInDbMutex(
() => WarpApi.newAccount(coin, name, seed, rotationAccounts[raKeys[i]]!.length),
);
printV("new id: $id / $seed");
printV("${rotationAccounts[raKeys[i]]}");
printV(raKeys[i]);
rotationAccounts[raKeys[i]]!.forEach((final a) {
final b = WarpApi.getBackup(coin, a.id);
printV("${a.id}");
});
didAddNewAccount = true;
}
}
printV("didAddNewAccount: ${didAddNewAccount}");
if (didAddNewAccount) {
return createAndSweepTAddresses();
}
for (var i = 0; i < raKeys.length; i++) {
final accs = rotationAccounts[raKeys[i]]!;
inner:
for (var j = 0; j < accs.length; j++) {
int bal = 0;
try {
bal += await WarpApi.getTBalance(coin, accs[j].id);
} catch (e) {
printV("getTBalance: $e");
return;
}
if (bal < 30000) continue inner;
final to = accountForSeed(seeds[raKeys[i]]!)!;
// final toAddress = WarpApi.getTAddr(coin, to.id);
// final fromSeed = WarpApi.getBackup(coin, to.id);
// final txId = await ZcashWalletService.runInDbMutex(
// () => WarpApi.sweepTransparentSeed(
// to.coin,
// to.id,
// chainHeight,
// fromSeed.seed!,
// 7,
// toAddress,
// 0,
// 1,
// FeeT(fee: 10000, minFee: 0, maxFee: 0, scheme: 0),
// ),
// );
final recipientBuilder = RecipientObjectBuilder(
address: WarpApi.getAddress(coin, to.id, 4),
pools: 4,
feeIncluded: true,
amount: bal,
);
final recipient = Recipient(recipientBuilder.toBytes());
final fee = FeeT(fee: 10000, minFee: 0, maxFee: 0, scheme: 0);
await ZcashWalletBase.loadProver();
final txPlan = await ZcashWalletService.runInDbMutex(
() => WarpApi.prepareTx(
coin,
accs[j].id,
[recipient],
3, // pools: (Transparent + Sapling)
1,
0, // anchorOffset
fee,
),
);
final _txId = await ZcashWalletService.runInDbMutex(
() => WarpApi.signAndBroadcast(ZcashWalletBase.coin, accs[j].id, txPlan),
);
await ZcashWalletService.addShieldedTx(_txId);
printV("shielded: $_txId");
await Future.delayed(Duration(seconds: 120)); // let it be
return createAndSweepTAddresses();
}
}
final nsatKeys = newShieldedAccountsTx.keys.toList();
for (int i = 0; i < nsatKeys.length; i++) {
inner:
for (int j = 0; j < newShieldedAccountsTx[nsatKeys[i]]!.length; j++) {
final tx = newShieldedAccountsTx[nsatKeys[i]]?[j];
if (tx == null) {
continue inner;
}
final _txId = tx.txId;
if (_txId == null) {
continue inner;
}
if (tx.value < 0) {
await ZcashWalletService.addShieldedTx(_txId);
// continue inner;
}
if (ZcashWalletService.autoshieldTx.contains(_txId)) {
continue inner;
}
}
}
shieldedAccountsTx = newShieldedAccountsTx.map((final k, final v) => MapEntry(k, v.toList()));
await serializeToFile();
return;
}
static ShieldedTx reverseAmount(final ShieldedTx t) {
final tt = t.unpack();
tt.value = -tt.value;
return ShieldedTx(base64.decode(flatBuffersPack(tt.pack)));
}
static Account? accountForSeed(final String seed) {
final accounts = WarpApi.getAccountList(coin);
if (accounts.isEmpty) return null;
for (int i = 0; i < accounts.length; i++) {
final acc = accounts[i];
final backup = WarpApi.getBackup(coin, acc.id);
if (backup.seed == seed) {
return acc;
}
}
return null;
}
static Future<void> _jobRunner() async {
for (;;) {
try {
await Future.delayed(Duration(seconds: 5));
await createAndSweepTAddresses();
} catch (e, s) {
printV(e);
s.toString().split("\n").forEach(printV);
rethrow;
} finally {
await Future.delayed(Duration(seconds: 30));
}
}
}
static String? addressForAccount(final int accountId, final Set<String>? hiddenAddresses) {
final accs = rotationAccountsUsable[accountId] ?? [];
for (int i = 0; i < accs.length; i++) {
final addr = WarpApi.getTAddr(coin, accs[i].id);
if (hiddenAddresses?.contains(addr) == true) {
continue;
}
return addr;
}
return null;
}
static List<String>? allAddressesForAccount(final int accountId) {
final acc = rotationAccounts[accountId];
if (acc == null) {
printV("Nothing found");
return null;
}
for (int i = 0; i < acc.length; i++) {
final b = WarpApi.getBackup(coin, acc[i].id);
printV("$i. ${b.seed?.split(" ").last}, ${b.index}, ${WarpApi.getTAddr(coin, acc[i].id)}");
}
return acc.map((final a) => WarpApi.getTAddr(coin, a.id)).toList();
}
static List<String>? allUsedAddressesForAccount(final int accountId) {
final seed = WarpApi.getBackup(coin, accountId).seed;
if (seed == null) return [];
final acc = rotationAccounts[seed]?.toList();
if (acc == null) {
printV("Nothing found");
return null;
}
acc.removeWhere((final a1) {
for (int i = 0; i < (rotationAccountsUsable[seed]?.length ?? 0); i++) {
if (rotationAccountsUsable[seed]?[i].id == a1.id) {
return true;
}
}
return false;
});
for (int i = 0; i < acc.length; i++) {
final b = WarpApi.getBackup(coin, acc[i].id);
printV("$i. ${b.seed?.split(" ").last}, ${b.index}, ${WarpApi.getTAddr(coin, acc[i].id)}");
}
return acc.map((final a) => WarpApi.getTAddr(coin, a.id)).toList();
}
}
@@ -1,15 +0,0 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/output_info.dart';
class ZcashTransactionCredentials {
ZcashTransactionCredentials({
required this.outputs,
required this.priority,
required this.currency,
});
final List<OutputInfo> outputs;
final MoneroTransactionPriority priority;
final CryptoCurrency currency;
}
@@ -1,105 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_zcash/cw_zcash.dart';
import 'package:path/path.dart' as p;
class ZcashTransactionInfo extends TransactionInfo {
ZcashTransactionInfo({
required final String id,
required final int amount,
required final int fee,
required final TransactionDirection direction,
required final bool isPending,
required final DateTime date,
required final int height,
required final int confirmations,
required final String to,
final String? memo,
}) {
this.id = id;
this.amount = amount;
this.fee = fee;
this.height = height;
this.direction = direction;
this.date = date;
this.isPending = isPending;
this.confirmations = confirmations;
this.to = getCachedDestinationAddress(id);
if (memo != null && memo.isNotEmpty) {
additionalInfo['memo'] = memo;
}
additionalInfo['autoShield'] = false;
// note: this won't work yet, fee is not in zcash_lib metadata,
// leaving here so it can start working automagically in future
if (amount == fee && to.startsWith("u")) {
additionalInfo['autoShield'] = true;
}
// remove below when above starts working
additionalInfo['autoShield'] = ZcashWalletService.autoshieldTx.contains(txHash);
// --- == === cut here === == ---
if (additionalInfo['autoShield'] == true) {
additionalInfo['memo'] ??= '';
additionalInfo['memo'] += '\This is an auto-shielding transaction. Enjoy default privacy!';
additionalInfo['memo'] = additionalInfo['memo'].trim();
}
}
String? _fiatAmount;
@override
String amountFormatted() =>
'${walletTypeToCryptoCurrency(WalletType.zcash).formatAmount(BigInt.from(amount))} ${walletTypeToCryptoCurrency(WalletType.zcash).title}';
@override
String? feeFormatted() {
if (fee == null || fee == 0) return null;
return '${walletTypeToCryptoCurrency(WalletType.zcash).formatAmount(BigInt.from(fee!))} ${walletTypeToCryptoCurrency(WalletType.zcash).title}';
}
@override
String fiatAmount() => _fiatAmount ?? '';
@override
void changeFiatAmount(final String amount) => _fiatAmount = formatAmount(amount);
String? get memo => additionalInfo['memo'] as String?;
static final Map<String, String> _destinationAddressMap = {};
static String? getCachedDestinationAddress(final String txId) {
return _destinationAddressMap[txId] ??
_destinationAddressMap['"$txId"'] ??
_destinationAddressMap[txId.replaceAll('"', '')];
}
static Future<void> addCachedDestinationAddress(final String txId, final String address) async {
_destinationAddressMap[txId] = address;
final pfwt = await pathForWalletTypeDir(type: WalletType.zcash);
final f = File(p.join(pfwt, "sent-tx-map.json"));
f.writeAsStringSync(json.encode(_destinationAddressMap));
}
static Future<void> init() async {
try {
final pfwt = await pathForWalletTypeDir(type: WalletType.zcash);
final f = File(p.join(pfwt, "sent-tx-map.json"));
if (!f.existsSync()) {
f.writeAsStringSync('{}');
}
final tmpMap = json.decode(f.readAsStringSync());
tmpMap.forEach((final k, final v) {
_destinationAddressMap[k.toString()] = v.toString();
});
} catch (e, s) {
printV("failed to deserialize: $e, $s");
}
}
}
-24
View File
@@ -1,24 +0,0 @@
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/transaction_history.dart';
class ZcashTransactionHistory extends TransactionHistoryBase<TransactionInfo> {
ZcashTransactionHistory() {
transactions = ObservableMap<String, TransactionInfo>();
}
@override
void addMany(final Map<String, TransactionInfo> txs) {
transactions.addAll(txs);
}
@override
void addOne(final TransactionInfo txs) {
transactions[txs.txHash] = txs;
}
@override
Future<void> save() async {
// No need to save anything, backend is taking care of that
}
}
File diff suppressed because it is too large Load Diff
@@ -1,247 +0,0 @@
import 'package:cw_core/payment_uris.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_zcash/cw_zcash.dart';
import 'package:cw_zcash/src/zcash_taddress_rotation.dart';
import 'package:mobx/mobx.dart';
import 'package:warp_api/warp_api.dart';
part 'zcash_wallet_addresses.g.dart';
class ZcashWalletAddresses = ZcashWalletAddressesBase with _$ZcashWalletAddresses;
abstract class ZcashWalletAddressesBase extends WalletAddresses with Store {
ZcashWalletAddressesBase(this.accountId, final WalletInfo walletInfo) : super(walletInfo) {
addressPageType = ZcashReceivePageOption.typeFromString(walletInfo.addressPageType ?? "");
}
final int accountId;
static const int transparentType = 0;
static const int shieldedSaplingType = 1;
static const int shieldedOrchardType = 2;
static const int unifiedType = 3;
String? _transparentAddress;
String? _saplingAddress;
String? _orchardAddress;
String? _unifiedAddress;
@override
@computed
String get latestAddress {
switch (addressPageType) {
case ZcashAddressType.transparent:
return transparentAddress;
case ZcashAddressType.transparentRotated:
// just to display something, after the wallet is synced the address
// will start rotating, and since we can't rotate address until the wallet syncs
// we can't know if some T address already got used, so we would still default to
// the same address.
// So.. we can
// 1) display placeholder text (looks ugly)
// 2) display static address
// 3) display first address from rotated pool which is also static until synced
// 2 seems like an obvious winner simply because it offers little to no benefit over 3
// and is noticably less complex in implementation
return transparentAddressRotated ?? transparentAddress;
case ZcashAddressType.shieldedSapling:
return saplingAddress;
case ZcashAddressType.shieldedOrchard:
return orchardAddress;
case ZcashAddressType.unifiedType:
if (_unifiedAddress == null) {
_unifiedAddress = WarpApi.getAddress(ZcashWalletBase.coin, accountId, 6);
}
return _unifiedAddress ?? "";
}
}
@observable
ZcashAddressType? _addressPageType;
@computed
ZcashAddressType get addressPageType => _addressPageType ?? ZcashAddressType.shieldedOrchard;
@computed
set addressPageType(final ZcashAddressType newZat) {
_addressPageType = newZat;
init();
address = latestAddress;
}
@action
Future<void> setAddressType(final ZcashAddressType type) async {
addressPageType = ZcashReceivePageOption.typeFromString(type.toString());
walletInfo.addressPageType = type.toString();
await walletInfo.save();
}
String get transparentAddress {
try {
if (_transparentAddress == null) {
_transparentAddress = WarpApi.getTAddr(ZcashWalletBase.coin, accountId);
}
return _transparentAddress ?? "";
} catch (e) {
return "";
}
}
@computed
String? get transparentAddressRotated {
try {
return ZcashTaddressRotation.addressForAccount(accountId, hiddenAddresses);
} catch (e) {
return null;
}
}
String get saplingAddress {
try {
if (_saplingAddress == null) {
_saplingAddress = WarpApi.getAddress(ZcashWalletBase.coin, accountId, 2);
}
return _saplingAddress ?? "";
} catch (e) {
return "";
}
}
String get orchardAddress {
try {
if (_orchardAddress == null) {
_orchardAddress = WarpApi.getAddress(ZcashWalletBase.coin, accountId, 4);
}
return _orchardAddress ?? "";
} catch (e) {
return "";
}
}
@override
@computed
String get addressForExchange => transparentAddressRotated ?? transparentAddress;
@override
@computed
String get addressForBuy => transparentAddressRotated ?? transparentAddress;
@override
bool containsAddress(final String address) {
return this.address == address || addressesMap.values.contains(address);
}
static int get coin => ZcashWalletBase.coin;
@override
@observable
Set<String> hiddenAddresses = {};
Future<void> _initAddresses() async {
addressesMap = await walletInfo.getAddresses();
addressInfos = await walletInfo.getAddressInfos();
usedAddresses = await walletInfo.getUsedAddresses();
manualAddresses = await walletInfo.getManualAddresses();
hiddenAddresses = await walletInfo.getHiddenAddresses();
address = latestAddress;
}
@override
Future<void> init() async {
await _initAddresses();
await ZcashTaddressRotation.init();
int accountIndex = 0;
addressInfos = {
0:
ZcashTaddressRotation.allAddressesForAccount(accountId)?.map((final v) {
return WalletInfoAddressInfo(
walletInfoId: walletInfo.internalId,
mapKey: ++accountIndex,
accountIndex: 0,
address: v,
label: "",
);
}).toList() ??
[],
};
hiddenAddresses.addAll(
ZcashTaddressRotation.allUsedAddressesForAccount(accountId)?.toSet() ?? {},
);
// addressInfos[0]?.removeWhere((final test) => hiddenAddresses.contains(test.address));
if (_addressPageType == ZcashAddressType.transparentRotated) {
final addr = ZcashTaddressRotation.addressForAccount(accountId, hiddenAddresses);
if (addr != null) {
address = addr;
}
}
await saveAddressesInBox();
}
@observable
late String _address = latestAddress;
@computed
String get address {
return _address;
}
void set address(final String _$address) => _address = _$address;
@override
String get primaryAddress => address;
@override
Future<void> saveAddressesInBox() async {
try {
await walletInfo.setAddresses(addressesMap);
await walletInfo.setAddressInfos(addressInfos);
await walletInfo.setUsedAddresses(usedAddresses.toList());
await walletInfo.setHiddenAddresses(hiddenAddresses.toList());
await walletInfo.setManualAddresses(manualAddresses.toList());
await _initAddresses();
} catch (e) {
printV("Error saving addresses: $e");
}
}
@override
Future<void> updateAddressesInBox() async {
_transparentAddress = null;
_saplingAddress = null;
_orchardAddress = null;
_unifiedAddress = null;
}
@override
Map<String, String> get usableAddressesMap => addressesMap;
@override
Map<String, String> get usableAllAddressesMap => allAddressesMap;
List<WalletInfoAddressInfo> getAddressInfos() {
if (addressPageType != ZcashAddressType.transparentRotated) {
return [];
}
final List<WalletInfoAddressInfo> allInfos = [];
for (final entry in addressInfos.entries) {
allInfos.addAll(entry.value);
}
return allInfos;
}
@override
List<ReceivePageOption> get receivePageOptions {
return [
...ZcashReceivePageOption.all,
...ReceivePageOptions.where((final element) => element != ReceivePageOption.mainnet),
];
}
@override
PaymentURI getPaymentUri(final String amount) => ZcashURI(amount: amount, address: address);
}
-181
View File
@@ -1,181 +0,0 @@
import 'dart:io';
import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_zcash/cw_zcash.dart';
import 'package:mutex/mutex.dart';
import 'package:path/path.dart' as p;
class ZcashWalletService
extends
WalletService<
ZcashNewWalletCredentials,
ZcashFromSeedWalletCredentials,
ZcashFromKeysWalletCredentials,
ZcashNewWalletCredentials
> {
ZcashWalletService();
static Mutex dbMutex = Mutex();
static int dbMutexQueue = 0;
static Future<T> runInDbMutex<T>(final Future<T> Function() call) async {
try {
printV("dbMutexQueue: ${dbMutexQueue++}");
await dbMutex.acquire();
return await call();
} finally {
dbMutex.release();
dbMutexQueue--;
}
}
static Set<String> autoshieldTx = {};
static Future<void> addShieldedTx(final String txId) async {
final pathForWalletType = await pathForWalletTypeDir(type: type);
final shieldedListFile = p.join(pathForWalletType, "autoshield_list.txt");
autoshieldTx.add(txId.replaceAll(RegExp(r'[^a-zA-Z0-9 ]'), ''));
final shieldedList = File(shieldedListFile);
shieldedList.writeAsStringSync(autoshieldTx.join("\n"));
}
static Future<void> loadShieldTxs() async {
try {
final pathForWalletType = await pathForWalletTypeDir(type: type);
final shieldedListFile = p.join(pathForWalletType, "autoshield_list.txt");
final shieldedList = File(shieldedListFile);
if (!shieldedList.existsSync()) {
return;
}
autoshieldTx = shieldedList.readAsLinesSync().toSet();
} catch (e) {
printV("loadShieldTxs failed: $e");
}
}
static bool walletFilesExist(final String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync();
@override
WalletType getType() => type;
static WalletType get type => WalletType.zcash;
@override
Future<ZcashWallet> create(final WalletCredentials credentials, {final bool? isTestnet}) {
return ZcashWalletBase.create(credentials);
}
@override
Future<bool> isWalletExit(final String name) async {
final path = await pathForWallet(name: name, type: getType());
return File(path).existsSync();
}
@override
Future<ZcashWallet> openWallet(final String name, final String password) async {
await loadShieldTxs();
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await ZcashWalletBase.open(
name: name,
password: password,
walletInfo: walletInfo,
);
await wallet.init();
await saveBackup(name);
return wallet;
} catch (e) {
await restoreWalletFilesFromBackup(name);
final wallet = await ZcashWalletBase.open(
name: name,
password: password,
walletInfo: walletInfo,
);
await wallet.init();
return wallet;
}
}
@override
Future<void> remove(final String wallet) async {
final path = await pathForWalletDir(name: wallet, type: getType());
final file = Directory(path);
final isExist = file.existsSync();
if (isExist) {
await file.delete(recursive: true);
}
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
Future<void> rename(final String currentName, final String password, final String newName) async {
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final accountId = await ZcashWalletBase.getZcashAccountIdForName(currentName);
if (accountId == null) {
throw Exception('Wallet account not found for name: $currentName');
}
final currentWallet = ZcashWallet(
currentWalletInfo,
await currentWalletInfo.getDerivationInfo(),
accountId: accountId,
);
await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await newWalletInfo.save();
}
@override
Future<ZcashWallet> restoreFromKeys(
final ZcashFromKeysWalletCredentials credentials, {
final bool? isTestnet,
}) async {
return ZcashWalletBase.restoreKeys(credentials);
}
@override
Future<ZcashWallet> restoreFromSeed(
final ZcashFromSeedWalletCredentials credentials, {
final bool? isTestnet,
}) async {
if (credentials.seed == null || credentials.seed!.isEmpty) {
throw ZcashMnemonicIsIncorrectException();
}
if (!bip39.validateMnemonic(credentials.seed!)) {
throw ZcashMnemonicIsIncorrectException();
}
return ZcashWalletBase.restore(credentials);
}
@override
Future<ZcashWallet> restoreFromHardwareWallet(final ZcashNewWalletCredentials credentials) {
throw UnimplementedError(
"Restoring a Zcash wallet from a hardware wallet is not yet supported!",
);
}
}
-38
View File
@@ -1,38 +0,0 @@
name: cw_zcash
description: Zcash library for cake wallet
version: 1.0.0
publish_to: none
environment:
sdk: ^3.8.0
dependencies:
flutter:
sdk: flutter
warp_api:
path: ../scripts/zcash_lib/packages/warp_api_ffi
cw_core:
path: ../cw_core
mobx: ^2.5.0
bip39: ^1.0.6
mutex: ^3.1.0
path: any
flat_buffers: any
zkool:
git:
url: https://github.com/cake-tech/zkool2
ref: b1642f260da4fa00dd2f8d4dd689073d0b56960a
path_provider: any
dependency_overrides:
decimal: ^2.3.3
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_resolvers: ^2.4.4
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
ffigen: ^16.1.0
+1 -1
View File
@@ -24,7 +24,7 @@ IF NOT EXIST "%secrets_file_path%" (
) ELSE (echo === Using previously/already generated secrets file: %secrets_file_path% ===)
echo === Generating mobx models ===
for /d %%i in (cw_core cw_monero cw_bitcoin cw_evm cw_nano cw_bitcoin_cash cw_solana cw_tron cw_zcash.) do (
for /d %%i in (cw_core cw_monero cw_bitcoin cw_evm cw_nano cw_bitcoin_cash cw_wownero cw_dogecoin) do (
cd %%i
call flutter pub get > nul
call dart run build_runner build --delete-conflicting-outputs > nul
-3
View File
@@ -271,13 +271,10 @@ flutter:
- assets/dogecoin_electrum_server_list.yml
- assets/base_node_list.yml
- assets/arbitrum_node_list.yml
- assets/zcash_node_list.yml
- assets/bsc_node_list.yml
- assets/text/
- assets/faq/
- assets/animation/
- scripts/zcash_lib/assets/sapling-spend.params
- scripts/zcash_lib/assets/sapling-output.params
- assets/new-ui/
- assets/new-ui/icons/
- assets/new-ui/hero/
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -x -e
cd "$(dirname "$0")"
../prepare_zcash.sh
pushd ../zcash_lib
./build-scripts/build-android.sh
popd
-3
View File
@@ -5,7 +5,6 @@ ARG BITBOX_IMAGE
ARG MONERO_IMAGE
ARG DECRED_IMAGE
ARG MWEBD_IMAGE
ARG ZCASH_IMAGE
FROM ${TORCH_IMAGE} AS torch_src
FROM ${REOWN_IMAGE} AS reown_src
@@ -13,7 +12,6 @@ FROM ${BITBOX_IMAGE} AS bitbox_src
FROM ${MONERO_IMAGE} AS monero_src
FROM ${DECRED_IMAGE} AS decred_src
FROM ${MWEBD_IMAGE} AS mwebd_src
FROM ${ZCASH_IMAGE} AS zcash_src
FROM --platform=linux/amd64 alpine
@@ -23,4 +21,3 @@ COPY --from=bitbox_src /w /w.top
COPY --from=monero_src /w /w.top
COPY --from=decred_src /w /w.top
COPY --from=mwebd_src /w /w.top
COPY --from=zcash_src /w /w.top
-11
View File
@@ -1,11 +0,0 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
RUN mkdir -p /w/scripts/android
COPY scripts/prepare_zcash.sh /w/scripts/prepare_zcash.sh
COPY scripts/android/build_zcash.sh /w/scripts/android/build_zcash.sh
RUN /w/scripts/android/build_zcash.sh \
&& rm -rf /w/scripts/zcash_lib/target
FROM --platform=linux/amd64 alpine
COPY --from=build /w /w
+2 -6
View File
@@ -56,9 +56,8 @@ reown_ver=$(tinysha $SCRIPT_DIR/Dockerfile.reown $REPO_ROOT/scripts/prepare_reow
bitbox_ver=$(tinysha $SCRIPT_DIR/Dockerfile.bitbox $REPO_ROOT/scripts/build_bitbox_flutter.sh)
monero_ver=$(tinysha $SCRIPT_DIR/Dockerfile.monero $REPO_ROOT/scripts/prepare_moneroc.sh $REPO_ROOT/scripts/android/build_monero_all.sh)
mwebd_ver=$(tinysha $SCRIPT_DIR/Dockerfile.mwebd $(find $REPO_ROOT/cw_mweb/go -type f))
zcash_ver=$(tinysha $SCRIPT_DIR/Dockerfile.zcash $REPO_ROOT/scripts/prepare_zcash.sh $REPO_ROOT/scripts/android/build_zcash.sh)
decred_ver=$(tinysha $SCRIPT_DIR/Dockerfile.torch $SCRIPT_DIR/Dockerfile.decred $REPO_ROOT/scripts/android/build_decred.sh)
echo $base_ver $torch_ver $reown_ver $bitbox_ver $monero_ver $mwebd_ver $zcash_ver $decred_ver > /tmp/docker_build_versions
echo $base_ver $torch_ver $reown_ver $bitbox_ver $monero_ver $mwebd_ver $decred_ver > /tmp/docker_build_versions
final_ver=$(tinysha /tmp/docker_build_versions)
docker create --name temp_extract $(img final $final_ver) \
@@ -83,8 +82,6 @@ build reown "$reown_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")"
build monero "$monero_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")"
build zcash "$zcash_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")"
build torch "$torch_ver" \
--build-arg BASE_IMAGE="$(img base "$base_ver")"
@@ -99,8 +96,7 @@ build final $final_ver \
--build-arg BITBOX_IMAGE="$(img bitbox $bitbox_ver)" \
--build-arg MONERO_IMAGE="$(img monero $monero_ver)" \
--build-arg DECRED_IMAGE="$(img decred $decred_ver)" \
--build-arg MWEBD_IMAGE="$(img mwebd $mwebd_ver)" \
--build-arg ZCASH_IMAGE="$(img zcash $zcash_ver)"
--build-arg MWEBD_IMAGE="$(img mwebd $mwebd_ver)"
echo "done: $(img final $final_ver)"
echo $(img final $final_ver) > /tmp/cakewallet_docker
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -x -e
cd "$(dirname "$0")"
../prepare_zcash.sh
pushd ../zcash_lib
./build-scripts/mac/build-mac.sh
popd
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -x -e
cd "$(dirname "$0")"
../prepare_zcash.sh
pushd ../zcash_lib
./build-scripts/build-linux.sh
popd
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -x -e
cd "$(dirname "$0")"
../prepare_zcash.sh
pushd ../zcash_lib
./build-scripts/mac/build-mac.sh
popd
-28
View File
@@ -1,28 +0,0 @@
#!/bin/bash
set -x -e
cd "$(dirname "$0")"
HASH=647231e2e6d93c1d6c16d4019cd3a11a3aaa9504
if [[ ! -d "zcash_lib/.git" ]];
then
rm -rf zcash_lib
git clone https://github.com/MrCyjaneK/zwallet.git zcash_lib
cd zcash_lib
else
cd zcash_lib
git fetch -a
fi
git reset --hard
git checkout $HASH
git reset --hard
git submodule update --init --force --recursive
# in go I could nicely replace => the heck out of it
# sadly rust is not go
find . -name Cargo.toml -exec sed -i.bak -E '
s|rusqlite[[:space:]]*=[[:space:]]*\{[^}]*\}|rusqlite = { version = "0.29.0", features = ["bundled", "modern_sqlite", "backup"] }|g
' {} +
# find . -name Cargo.toml -exec sed -i.bak -E 's|r2d2_sqlite[[:space:]]*=[[:space:]]*([^\n]*)|r2d2_sqlite = "0.32.0"|g' {} +