feat: prepare for NU7 (#3764)
What changed, and why it matters
This commit prepares Cake Wallet for the Zcash NU7 network upgrade. Most changes update how the app talks to its Zcash library (zkool) so it passes an explicit account ID instead of relying on a shared 'current account' state. It also adds a GitHub token helper for Docker builds so private repository clones can authenticate without leaking the token into Docker image layers. There is no obvious malicious code, but the large refactor touches sensitive wallet logic and build secrets, so it deserves careful review.
Treat this as a high-priority functional and security review commit. Verify that the new explicit-account API correctly isolates multi-account wallets and does not accidentally use the wrong account ID when signing or broadcasting. Confirm the git-credential-github helper only emits credentials for github.com and that BuildKit secrets are not persisted in final images. Review the zkool2 dependency bump separately for any consensus-breaking or cryptographic changes related to NU7. Run integration tests for Zcash sends, Ledger signing, auto-shielding, and multi-account scenarios before release.
Security signals we found
Large refactor of Zcash wallet state management and account isolation
New build-secret handling for GitHub tokens in Docker (reduces risk of token leakage in image layers)
Hardware wallet signing now requires explicit account ID and throws if unset
Removal of per-call `setAccount` mutations in favor of explicit account parameters
Dependency bump of zkool2 (Zcash Rust library) to a newer commit
Changes to transaction broadcast flow and mempool URL handling
Evidence from the diff
The commit is titled ‘feat: prepare for NU7 (#3764)’ and primarily updates cw_zcash to match a newer zkool2 API. Key technical changes include: (1) adding an account parameter to many zkool calls (signTransaction, broadcast, balance, listNotes, getAddresses, etc.) and removing c.setAccount() mutations; (2) changing runWithCoin to use the existing c coin object rather than reopening the database and setting account per call; (3) updating the hardware wallet service to carry an explicit account ID for Ledger signing; (4) replacing extractTransaction+broadcastTransaction with a single broadcast(package: signed, ...) flow; (5) adding noteShieldBroadcast() and simplifying auto-shield thresholds; (6) adding scripts/docker/git-credential-github.sh and passing GITHUB_TOKEN/GH_TOKEN as BuildKit secrets into Docker builds. The zkool dependency is bumped from b56be32e to cb3e96f1. No security vulnerability is directly visible in the diff, but the refactor changes concurrency, account isolation, and secret handling.
Changed components
cw_zcash Zcash wallet implementationZcash hardware/Ledger signing pathZcash transaction broadcast and mempool logicZcash t-address rotation and auto-shieldingDocker build scripts for Android and LinuxGitHub Actions workflow environment variablesInspect captured patch +334 / −159
### .github/workflows/pr_test_build_linux.yml
@@ -13,6 +13,7 @@ defaults:
env:
CW_DOCKER_REGISTRY: ghcr.io/cake-tech/cake_wallet
CW_DOCKER_USE_CLOUD: "true"
+ GITHUB_TOKEN: ${{ github.token }}
concurrency:
group: linux-${{ github.event.pull_request.number }}
### .github/workflows/reusable-build.yml
@@ -21,6 +21,7 @@ defaults:
env:
CW_DOCKER_REGISTRY: ghcr.io/cake-tech/cake_wallet
CW_DOCKER_USE_CLOUD: "true"
+ GITHUB_TOKEN: ${{ github.token }}
jobs:
base:
### .github/workflows/reusable-integration-test.yml
@@ -60,6 +60,7 @@ jobs:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
+ GITHUB_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
### cw_zcash/lib/src/hardware/zcash_hardware_wallet_service.dart
@@ -6,5 +6,6 @@ abstract class ZcashHardwareWalletService extends HardwareWalletService {
final zkool_coin.Coin? coin;
- ZcashHardwareWalletService withCoin(zkool_coin.Coin coin) => throw UnimplementedError();
+ ZcashHardwareWalletService withCoin(zkool_coin.Coin coin, {int? account}) =>
+ throw UnimplementedError();
}
### cw_zcash/lib/src/hardware/zcash_ledger_service.dart
@@ -22,15 +22,26 @@ const _notConnected =
"The Ledger is not connected. Make sure it is unlocked and nearby, then try again.";
class ZcashLedgerService extends ZcashHardwareWalletService {
- ZcashLedgerService(this.connection, {this.network = ZcashNetwork.mainnet, zkool_coin.Coin? coin})
- : super(coin);
+ ZcashLedgerService(
+ this.connection, {
+ this.network = ZcashNetwork.mainnet,
+ zkool_coin.Coin? coin,
+ this.account,
+ }) : super(coin);
final LedgerConnection connection;
final ZcashNetwork network;
+ /// zkool database account id used when the device signs.
+ final int? account;
+
@override
- ZcashLedgerService withCoin(zkool_coin.Coin coin) =>
- ZcashLedgerService(connection, network: network, coin: coin);
+ ZcashLedgerService withCoin(zkool_coin.Coin coin, {int? account}) => ZcashLedgerService(
+ connection,
+ network: network,
+ coin: coin,
+ account: account ?? this.account,
+ );
/// Ironwood/v6 signing shipped in app-zcash 3.9.2.
static const (int, int, int) minAppVersion = (3, 9, 2);
@@ -127,9 +138,13 @@ class ZcashLedgerService extends ZcashHardwareWalletService {
final zkool_pay.PcztPackage package,
final zkool_coin.Coin coin,
) async* {
+ final accountId = account;
+ if (accountId == null) {
+ throw const ZcashLedgerException("Zcash account not set, please contact support");
+ }
lastTransportError = null;
yield* zkool_ledger
- .ledgerSignTransaction(package: package, c: coin, exchange: exchange)
+ .ledgerSignTransaction(package: package, account: accountId, c: coin, exchange: exchange)
.handleError((final Object e) => throw _withTransportCause(e));
}
### cw_zcash/lib/src/pending_zcash_transaction.dart
@@ -1,10 +1,11 @@
+import 'dart:async';
+
import 'package:cw_core/amount/money.dart';
import 'package:cw_core/exceptions.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_zcash/cw_zcash.dart';
import 'package:zkool/src/rust/api/pay.dart' as zkool_pay;
-import 'package:zkool/src/rust/api/network.dart' as zkool_network;
class PendingZcashTransaction with PendingTransaction {
PendingZcashTransaction({
@@ -44,12 +45,15 @@ class PendingZcashTransaction with PendingTransaction {
await ZcashWalletBase.runWithCoin(
accountId: zcashWallet.accountId,
func: (coin) async {
- final signTx = signedTxPackage ?? await zkool_pay.signTransaction(pczt: txPlan, c: coin);
- final txBytes = await zkool_pay.extractTransaction(package: signTx);
- final currentHeight = await zkool_network.getCurrentHeight(c: coin);
- final result = await zkool_pay.broadcastTransaction(
- height: currentHeight,
- txBytes: txBytes,
+ final signed = signedTxPackage ??
+ await zkool_pay.signTransaction(
+ pczt: txPlan,
+ account: zcashWallet.accountId,
+ c: coin,
+ );
+ final result = await zkool_pay.broadcast(
+ package: signed,
+ account: zcashWallet.accountId,
c: coin,
);
printV("result: $result");
@@ -59,16 +63,20 @@ class PendingZcashTransaction with PendingTransaction {
}
_txId = txId;
if (isShieldingTx) {
+ zcashWallet.noteShieldBroadcast();
await ZcashWalletService.addShieldedTx(txId);
} else {
zcashWallet.rememberPendingOutgoingAmount(txId, amount);
}
},
);
- await zcashWallet.updateTransactions();
- await zcashWallet.updateBalance(
- runAutoShield: !isShieldingTx, runIronwoodMigrate: !isShieldingTx);
+
+ unawaited(zcashWallet.updateTransactions());
+ unawaited(zcashWallet.updateBalance(
+ runAutoShield: !isShieldingTx,
+ runIronwoodMigrate: !isShieldingTx,
+ ));
}
@override
### cw_zcash/lib/src/zcash_mempool.dart
@@ -42,17 +42,17 @@ class ZcashMempoolService {
)
.toList();
- Future<void> ensureRunning(final zkool_coin.Coin c) async {
- if (c.url.isEmpty) {
+ Future<void> ensureRunning(final zkool_coin.Coin c, {required final String url}) async {
+ if (url.isEmpty) {
return;
}
- if (_loopRunning && _nodeUrl == c.url) {
+ if (_loopRunning && _nodeUrl == url) {
return;
}
await stop();
- _nodeUrl = c.url;
+ _nodeUrl = url;
_loopRunning = true;
- unawaited(_runLoop(c));
+ unawaited(_runLoop(c, url));
}
Future<void> stop() async {
@@ -69,8 +69,8 @@ class ZcashMempoolService {
_mempool = null;
}
- Future<void> _runLoop(final zkool_coin.Coin c) async {
- while (_loopRunning && _nodeUrl == c.url) {
+ Future<void> _runLoop(final zkool_coin.Coin c, final String url) async {
+ while (_loopRunning && _nodeUrl == url) {
try {
final completer = Completer<void>();
_subscription = _mempoolInstance
### cw_zcash/lib/src/zcash_taddress_rotation.dart
@@ -96,6 +96,7 @@ class ZcashTaddressRotation {
c: coin,
aggregate: false,
poolFilter: 1,
+ account: rotationAccountId,
);
final external = all.where((final e) => e.scope == 0).toList();
if (external.isEmpty) {
@@ -106,7 +107,12 @@ class ZcashTaddressRotation {
final height = await zkool_network.getCurrentHeight(c: coin);
final completer = Completer<void>();
final sub = scanner
- .run(endHeight: height, gapLimit: maxD + _lookahead + 1, c: coin)
+ .run(
+ endHeight: height,
+ gapLimit: maxD + _lookahead + 1,
+ account: rotationAccountId,
+ c: coin,
+ )
.listen(
(_) {},
onError: (final e) {
@@ -137,7 +143,7 @@ class ZcashTaddressRotation {
await ZcashWalletBase.runWithCoin(
accountId: rotationAccountId,
func: (final coin) async {
- await zkool_sync.cancelSync();
+ await zkool_sync.cancelSync(c: coin);
await zkool_account.resetSync(id: rotationAccountId, c: coin);
final height = await zkool_network.getCurrentHeight(c: coin);
final sync = zkool_sync.synchronize(
@@ -264,7 +270,12 @@ class ZcashTaddressRotation {
final all = await ZcashWalletBase.runWithCoin(
accountId: rotationAccountId,
func: (final coin) =>
- zkool_account.fetchAddressTxCount(c: coin, aggregate: false, poolFilter: 1),
+ zkool_account.fetchAddressTxCount(
+ c: coin,
+ aggregate: false,
+ poolFilter: 1,
+ account: rotationAccountId,
+ ),
);
return all.where((final e) => e.scope == 0).toList()
..sort((final a, final b) => a.dindex.compareTo(b.dindex));
@@ -280,7 +291,7 @@ class ZcashTaddressRotation {
for (var i = 0; i < toGenerate; i++) {
await ZcashWalletBase.runWithCoin(
accountId: rotationAccountId,
- func: (final coin) => zkool_account.generateNextDindex(c: coin),
+ func: (final coin) => zkool_account.generateNextDindex(account: rotationAccountId, c: coin),
);
}
if (toGenerate > 0) {
@@ -297,7 +308,7 @@ class ZcashTaddressRotation {
return ZcashWalletBase.runWithCoin(
accountId: rotationAccount,
func: (final coin) async {
- final b = await zkool_sync.balance(c: coin);
+ final b = await zkool_sync.balance(account: rotationAccount, c: coin);
return b.field0.elementAt(0);
},
);
@@ -307,12 +318,12 @@ class ZcashTaddressRotation {
return ZcashWalletBase.runWithCoin(
accountId: accountId,
func: (final coin) async {
- final txsI = await zkool_account.listTxHistory(c: coin);
+ final txsI = await zkool_account.listTxHistory(account: accountId, c: coin);
final txs = <ZkoolTx>[];
for (int i = 0; i < txsI.length; i++) {
final tx = txsI[i];
try {
- final details = await zkool_account.getTxDetails(idTx: tx.id, c: coin);
+ final details = await zkool_account.getTxDetails(idTx: tx.id, account: accountId, c: coin);
txs.add(ZkoolTx(tx, details));
} catch (e) {
printV("getTxDetails skipped for tx ${tx.id}: $e");
@@ -339,7 +350,6 @@ class ZcashTaddressRotation {
try {
final mainAccountIds = ZcashWalletBase.walletsByAccountId.keys.toList();
if (mainAccountIds.isEmpty) {
- await createAndSweepTAddressesForAccount(c.account);
return;
}
for (final mainAccountId in mainAccountIds) {
@@ -398,7 +408,7 @@ class ZcashTaddressRotation {
final toAddress = await ZcashWalletBase.runWithCoin(
accountId: cId,
func: (final coin) async {
- final addrs = await zkool_account.getAddresses(c: coin, uaPools: 7);
+ final addrs = await zkool_account.getAddresses(c: coin, uaPools: 7, account: cId);
final orchard = addrs.oaddr;
if (orchard == null || orchard.isEmpty) {
throw AddressRotationException('Orchard address unavailable for rotation sweep');
@@ -410,7 +420,7 @@ class ZcashTaddressRotation {
accountId: rotationAccount,
func: (final coin) async {
final height = await zkool_network.getCurrentHeight(c: coin);
- final notes = await zkool_account.listNotes(c: coin);
+ final notes = await zkool_account.listNotes(account: rotationAccount, c: coin);
var amount = BigInt.zero;
for (final note in notes) {
if (note.pool != NotePool.transparent.index || note.locked) {
@@ -442,11 +452,15 @@ class ZcashTaddressRotation {
recipientPaysFee: true,
smartTransparent: false,
),
+ account: rotationAccount,
+ c: coin,
+ );
+ final signTx = await zkool_pay.signTransaction(
+ pczt: tx,
+ account: rotationAccount,
c: coin,
);
- final signTx = await zkool_pay.signTransaction(pczt: tx, c: coin);
- final txBytes = await zkool_pay.extractTransaction(package: signTx);
- return zkool_pay.broadcastTransaction(height: height, txBytes: txBytes, c: coin);
+ return zkool_pay.broadcast(package: signTx, account: rotationAccount, c: coin);
},
);
printV("rotation sweep broadcast: $result");
### cw_zcash/lib/src/zcash_wallet.dart
@@ -111,9 +111,6 @@ abstract class ZcashWalletBase
// zkool's migrate::MIN_SD.
static const int _ironwoodMigrateMinNote = 500000;
- static int _minSweepThreshold({required final bool ironwood}) =>
- ironwood ? _ironwoodMigrateMinNote : _autoShieldMinSweep;
-
Money _feeFromTxPlan(
final zkool_pay.PcztPackage txPlan,
final TransactionPriority priority,
@@ -186,7 +183,7 @@ abstract class ZcashWalletBase
printV("Setting LWD URL to: $lwdUrl");
c = c.setLwd(url: lwdUrl, serverType: 0);
syncStatus = ConnectedSyncStatus();
- unawaited(ZcashMempoolService.instance.ensureRunning(c));
+ unawaited(ZcashMempoolService.instance.ensureRunning(c, url: lwdUrl));
unawaited(_updateIronwoodActive());
_ensureSyncLoopRunning();
unawaited(_refreshSyncStatus());
@@ -240,10 +237,8 @@ abstract class ZcashWalletBase
var lowest = await _getWalletDbHeight();
for (final acc in accounts) {
if (acc.id == accountId) continue;
- c = await c.setAccount(account: acc.id);
- lowest = min(lowest, await _getWalletDbHeight());
+ lowest = min(lowest, await _getWalletDbHeight(acc.id));
}
- c = await c.setAccount(account: accountId);
return lowest;
}
@@ -253,13 +248,10 @@ abstract class ZcashWalletBase
Future<bool> _anyAccountNeedsSyncUnlocked(final int currentHeight) async {
final accounts = await zkool_account.listAccounts(c: c);
for (final acc in accounts) {
- c = await c.setAccount(account: acc.id);
- if (currentHeight > await _getWalletDbHeight()) {
- c = await c.setAccount(account: accountId);
+ if (currentHeight > await _getWalletDbHeight(acc.id)) {
return true;
}
}
- c = await c.setAccount(account: accountId);
return false;
}
@@ -283,12 +275,13 @@ abstract class ZcashWalletBase
});
}
- Future<int> _getWalletDbHeight() async {
+ Future<int> _getWalletDbHeight([final int? forAccount]) async {
+ final id = forAccount ?? accountId;
try {
- return (await zkool_sync.getDbHeight(c: c)).height;
+ return (await zkool_sync.getDbHeight(account: id, c: c)).height;
} catch (_) {
final accounts = await zkool_account.listAccounts(c: c);
- final account = accounts.where((final a) => a.id == accountId).firstOrNull;
+ final account = accounts.where((final a) => a.id == id).firstOrNull;
if (account != null) {
return account.height;
}
@@ -307,7 +300,6 @@ abstract class ZcashWalletBase
Future<void> _refreshSyncStatus() async {
try {
await withSharedCoinLock(() async {
- c = await c.setAccount(account: accountId);
final currentHeight = await zkool_network.getCurrentHeight(c: c);
final walletDbHeight = await _getWalletDbHeight();
_broadcastSyncProgress(currentHeight, walletDbHeight);
@@ -327,7 +319,6 @@ abstract class ZcashWalletBase
late final int currentHeight;
late final int walletDbHeight;
await withSharedCoinLock(() async {
- c = await c.setAccount(account: accountId);
currentHeight = await zkool_network.getCurrentHeight(c: c);
walletDbHeight = await _getWalletDbHeight();
});
@@ -339,58 +330,61 @@ abstract class ZcashWalletBase
isSyncing = false;
return true;
}
- await zkool_sync.cancelSync();
+ await zkool_sync.cancelSync(c: c);
late final List<int> accountList;
late final int lagHeight;
await withSharedCoinLock(() async {
final accounts = await zkool_account.listAccounts(c: c);
accountList = accounts.map((final a) => a.id).toList()
- ..removeWhere((final a) => a == c.account);
- c = await c.setAccount(account: accountId);
+ ..removeWhere((final a) => a == accountId);
lagHeight = await _getLowestSyncHeight();
});
_broadcastSyncProgress(currentHeight, lagHeight);
final sync = zkool_sync.synchronize(
- accounts: [c.account, ...accountList],
+ accounts: [accountId, ...accountList],
currentHeight: currentHeight,
actionsPerSync: 10000,
transparentLimit: 100,
checkpointAge: 200,
c: c,
fast: false,
);
- await withSharedCoinLock(() async {
- c = await c.setAccount(account: accountId);
- });
final randInt = CRC32.compute("${DateTime.now().microsecondsSinceEpoch}").toRadixString(16);
oneshotSyncCount++;
final completer = Completer<void>();
var lastLoggedHeight = walletDbHeight;
var chainTip = currentHeight;
- late final StreamSubscription<zkool_sync.SyncProgress> subscription;
+ late final StreamSubscription<zkool_sync.SyncEvent> subscription;
subscription = sync.listen(
- (final syncProgress) {
- if (syncProgress.height > chainTip) {
- chainTip = syncProgress.height;
+ (final syncEvent) {
+ final height = switch (syncEvent) {
+ zkool_sync.SyncEvent_Progress(:final height) => height,
+ zkool_sync.SyncEvent_Finished() => null,
+ };
+ if (height == null) {
+ return;
}
- if (syncProgress.height >= lastLoggedHeight + 5000) {
- lastLoggedHeight = syncProgress.height;
+ if (height > chainTip) {
+ chainTip = height;
+ }
+ if (height >= lastLoggedHeight + 5000) {
+ lastLoggedHeight = height;
printV(
- "[${c.account} ($accountList)] [$oneshotSyncCount/$randInt] sync: ${syncProgress.height}",
+ "[$accountId ($accountList)] [$oneshotSyncCount/$randInt] sync: $height",
);
unawaited(
zkool_network.getCurrentHeight(c: c).then((final tip) {
if (tip > chainTip) {
chainTip = tip;
- _onSyncCheckpoint(chainTip, syncProgress.height);
+ _onSyncCheckpoint(chainTip, height);
}
}),
);
}
- _onSyncCheckpoint(chainTip, syncProgress.height);
+ _onSyncCheckpoint(chainTip, height);
},
onError: (final e) {
- printV("[${c.account} ($accountList)] [$oneshotSyncCount/$randInt] error syncing: $e");
+ printV("[$accountId ($accountList)] [$oneshotSyncCount/$randInt] error syncing: $e");
runInAction(() {
syncStatus = FailedSyncStatus(
error:
@@ -404,12 +398,11 @@ abstract class ZcashWalletBase
}
},
onDone: () async {
- printV("[${c.account} ($accountList)] [$oneshotSyncCount/$randInt] synchronized");
+ printV("[$accountId ($accountList)] [$oneshotSyncCount/$randInt] synchronized");
oneshotSyncCount--;
isSyncing = false;
try {
await withSharedCoinLock(() async {
- c = await c.setAccount(account: accountId);
if (await _anyAccountNeedsSyncUnlocked(currentHeight)) {
final lagHeight = await _getLowestSyncHeight();
_broadcastSyncProgress(currentHeight, lagHeight);
@@ -507,6 +500,7 @@ abstract class ZcashWalletBase
recipientPaysFee: receipientPaysFee,
smartTransparent: false,
),
+ account: accountId,
c: coin,
);
final txFee = _feeFromTxPlan(txPlan, creds.priority, tryReduceFeeAmount, coin: coin);
@@ -521,13 +515,9 @@ abstract class ZcashWalletBase
if (isHardwareWallet) {
final unsignedTx = await zkool_pay.packTransaction(pczt: txPlan);
-
- final signedTxBytes = await runWithCoin(
- accountId: accountId,
- func: (coin) => hardwareWalletService!.withCoin(coin).signTransaction(
- transaction: base64Encode(unsignedTx),
- )
- );
+ final signedTxBytes = await hardwareWalletService!
+ .withCoin(c, account: accountId)
+ .signTransaction(transaction: base64Encode(unsignedTx));
final signedTx = await zkool_pay.unpackTransaction(bytes: signedTxBytes);
return PendingZcashTransaction(
@@ -782,9 +772,9 @@ abstract class ZcashWalletBase
}
}
- Future<Set<String>> _ownedAddressSet(final zkool_coin.Coin coin) async {
+ Future<Set<String>> _ownedAddressSet(final zkool_coin.Coin coin, final int account) async {
final owned = <String>{};
- for (final address in await zkool_account.listOwnedAddresses(c: coin)) {
+ for (final address in await zkool_account.listOwnedAddresses(account: account, c: coin)) {
if (address.isEmpty) {
continue;
}
@@ -853,10 +843,10 @@ abstract class ZcashWalletBase
final (txs, currentHeight, ownedAddresses) = await runWithCoin(
accountId: accountId,
func: (coin) async {
- final owned = await _ownedAddressSet(coin);
- final txsI = await zkool_account.listTxHistory(c: coin);
+ final owned = await _ownedAddressSet(coin, accountId);
+ final txsI = await zkool_account.listTxHistory(account: accountId, c: coin);
final txsA = await Future.wait(
- txsI.map((final tx) => zkool_account.getTxDetails(idTx: tx.id, c: coin)),
+ txsI.map((final tx) => zkool_account.getTxDetails(idTx: tx.id, account: accountId, c: coin)),
);
final txs = <ZkoolTx>[];
for (int i = 0; i < txsI.length; i++) {
@@ -971,7 +961,6 @@ abstract class ZcashWalletBase
Future<void> _initKeys() async {
try {
- c = await c.setAccount(account: accountId);
final ufvk = await zkool_account.getAccountUfvk(account: accountId, c: c, pools: 7);
keys = {
@@ -982,7 +971,6 @@ abstract class ZcashWalletBase
keys = {"privateViewKey": e.toString()};
}
try {
- c = await c.setAccount(account: accountId);
final s = (await zkool_account.getAccountSeed(account: accountId, c: c));
if (s == null) {
@@ -1057,7 +1045,7 @@ abstract class ZcashWalletBase
Future<void> rescan({required final int height}) async {
syncStatus = StartingScanSyncStatus(height);
try {
- await zkool_sync.cancelSync();
+ await zkool_sync.cancelSync(c: c);
isSyncing = false;
await runWithCoin(
@@ -1229,6 +1217,10 @@ abstract class ZcashWalletBase
static final autoShieldMutex = Mutex();
static DateTime? _lastAutoShieldAt;
+ void noteShieldBroadcast() {
+ _lastAutoShieldAt = DateTime.now();
+ }
+
static final ironwoodMigrateMutex = Mutex();
static DateTime? _lastIronwoodMigrateAt;
@@ -1253,8 +1245,8 @@ abstract class ZcashWalletBase
}
/// Total of transparent + sapling notes at or above the per-note spendable floor.
- static Future<BigInt> _sweepableTotal(final zkool_coin.Coin coin) async {
- final notes = await zkool_account.listNotes(c: coin);
+ static Future<BigInt> _sweepableTotal(final zkool_coin.Coin coin, final int account) async {
+ final notes = await zkool_account.listNotes(account: account, c: coin);
BigInt sweepable = BigInt.zero;
for (int i = 0; i < notes.length; i++) {
final note = notes[i];
@@ -1271,8 +1263,11 @@ abstract class ZcashWalletBase
}
/// Orchard notes that migration will actually split or move to Ironwood.
- static Future<BigInt> _migratableOrchardTotal(final zkool_coin.Coin coin) async {
- final notes = await zkool_account.listNotes(c: coin);
+ static Future<BigInt> _migratableOrchardTotal(
+ final zkool_coin.Coin coin,
+ final int account,
+ ) async {
+ final notes = await zkool_account.listNotes(account: account, c: coin);
BigInt migratable = BigInt.zero;
for (int i = 0; i < notes.length; i++) {
final note = notes[i];
@@ -1292,10 +1287,10 @@ abstract class ZcashWalletBase
final txPlan = await runWithCoin(
accountId: accountId,
func: (coin) async {
- final sweepable = await _sweepableTotal(coin);
+ final sweepable = await _sweepableTotal(coin, accountId);
final ironwood = await zkool_network.isIronwoodActive(c: coin);
- if (sweepable <= BigInt.from(_minSweepThreshold(ironwood: ironwood))) {
+ if (sweepable <= BigInt.from(_autoShieldMinSweep)) {
return null;
}
return zkool_pay.prepare(
@@ -1312,6 +1307,7 @@ abstract class ZcashWalletBase
recipientPaysFee: true,
smartTransparent: false,
),
+ account: accountId,
c: coin,
);
},
@@ -1324,23 +1320,19 @@ abstract class ZcashWalletBase
zkool_pay.PcztPackage? signedTxPackage;
if (isHardwareWallet) {
final unsignedTx = await zkool_pay.packTransaction(pczt: txPlan);
-
- final signedTxBytes = await runWithCoin(
- accountId: accountId,
- func: (coin) =>
- hardwareWalletService!.withCoin(coin).signTransaction(
- transaction: base64Encode(unsignedTx),
- )
- );
-
+ final signedTxBytes = await hardwareWalletService!
+ .withCoin(c, account: accountId)
+ .signTransaction(transaction: base64Encode(unsignedTx));
signedTxPackage = await zkool_pay.unpackTransaction(bytes: signedTxBytes);
}
final zcashBalance = balance[CryptoCurrency.zec];
final availableBalance = zcashBalance?.available ?? Money.zero(currency);
- final sweepableBigInt = await runWithCoin(accountId: accountId, func: _sweepableTotal);
+ final sweepableBigInt = await runWithCoin(
+ accountId: accountId,
+ func: (final coin) => _sweepableTotal(coin, accountId),
+ );
final sweepable = Money(sweepableBigInt, currency);
- _lastAutoShieldAt = DateTime.now();
return PendingZcashTransaction(
zcashWallet: this as ZcashWallet,
@@ -1383,11 +1375,11 @@ abstract class ZcashWalletBase
if (!await zkool_network.isIronwoodActive(c: coin)) {
return null;
}
- final bal = await zkool_sync.balance(c: coin);
+ final bal = await zkool_sync.balance(account: accountId, c: coin);
if (bal.field0.length <= 2 || bal.field0[2] <= BigInt.zero) {
return null;
}
- return zkool_migrate.stepMigration(c: coin);
+ return zkool_migrate.stepMigration(account: accountId, c: coin);
},
);
if (event == null) {
@@ -1464,9 +1456,9 @@ abstract class ZcashWalletBase
final (bal, sweepable, migratableOrchard) = await runWithCoin(
accountId: accountId,
func: (final coin) async => (
- await zkool_sync.balance(c: coin),
- await _sweepableTotal(coin),
- await _migratableOrchardTotal(coin),
+ await zkool_sync.balance(account: accountId, c: coin),
+ await _sweepableTotal(coin, accountId),
+ await _migratableOrchardTotal(coin, accountId),
),
);
@@ -1478,16 +1470,14 @@ abstract class ZcashWalletBase
// Unavailable uses the same per-note totals and thresholds as auto-shield/migration guards.
final BigInt availableAmount;
final BigInt unavailableAmount;
+ final sweepableUnavailable =
+ sweepable <= BigInt.from(_autoShieldMinSweep) ? BigInt.zero : sweepable;
if (ironwoodActive == true && orchard > BigInt.zero) {
- final sweepableUnavailable = sweepable <= BigInt.from(_ironwoodMigrateMinNote)
- ? BigInt.zero
- : sweepable;
availableAmount = ironwood;
unavailableAmount = migratableOrchard + sweepableUnavailable;
} else {
- final minSweep = _minSweepThreshold(ironwood: ironwoodActive);
availableAmount = orchard + ironwood;
- unavailableAmount = sweepable <= BigInt.from(minSweep) ? BigInt.zero : sweepable;
+ unavailableAmount = sweepableUnavailable;
}
final notShowSweepable = _lastAutoShieldAt?.isAfter(
@@ -1671,7 +1661,6 @@ abstract class ZcashWalletBase
if (accountId == null) {
throw ZcashAccountException("accountId is null");
}
- c = await c.setAccount(account: accountId);
final wallet = ZcashWallet(
walletInfo,
await walletInfo.getDerivationInfo(),
@@ -1877,17 +1866,11 @@ abstract class ZcashWalletBase
required final FutureOr<T> Function(zkool_coin.Coin c) func,
}) async {
await runWithCoinMutex.acquire();
- var newC = zkool_coin.Coin();
- newC = await newC.openDatabase(dbFilepath: c.dbFilepath);
- newC = await newC.setAccount(account: accountId);
- newC = await newC.setLwd(serverType: c.serverType, url: c.url);
- newC = await newC.setTransport(transport: c.transport).setProxy(proxy: c.proxy);
-
+ final coin = c;
runWithCoinCount++;
- printV("run with coin: $runWithCoinCount");
+ printV("run with coin: $runWithCoinCount (account $accountId)");
try {
- newC = await newC.setAccount(account: accountId);
- return await func(newC);
+ return await func(coin);
} finally {
runWithCoinMutex.release();
runWithCoinCount--;
### cw_zcash/lib/src/zcash_wallet_addresses.dart
@@ -169,7 +169,7 @@ abstract class ZcashWalletAddressesBase extends WalletAddresses with Store {
final addr = await ZcashWalletBase.runWithCoin(
accountId: accountId,
- func: (final coin) => zkool_account.getAddresses(c: coin, uaPools: 7),
+ func: (final coin) => zkool_account.getAddresses(c: coin, uaPools: 7, account: accountId),
);
transparentAddress = addr.taddr ?? 'unknown addr.taddr';
### cw_zcash/lib/src/zkooltx.dart
@@ -166,6 +166,7 @@ class ZkoolTx {
Map<String, dynamic> toJson() => {
"tx": {
"id": _tx.id,
+ "account": _tx.account,
"txid": base64.encode(_tx.txid),
"height": _tx.height,
"time": _tx.time,
@@ -210,6 +211,7 @@ class ZkoolTx {
(final output) => {
"id": output.id,
"pool": output.pool,
+ "vout": output.vout,
"height": output.height,
"value": output.value.toString(),
"address": output.address,
@@ -278,6 +280,7 @@ class ZkoolTx {
return ZkoolTx(
zkool_account.Tx(
id: _asInt(txJson["id"]),
+ account: _asInt(txJson["account"] ?? txAccountJson["account"]),
txid: _txidFromJson(txJson["txid"]),
height: _asInt(txJson["height"]),
time: _asInt(txJson["time"]),
@@ -322,6 +325,7 @@ class ZkoolTx {
(final a) => zkool_account.TxOutput(
id: _asInt(a["id"]),
pool: _asInt(a["pool"]),
+ vout: _asInt(a["vout"]),
height: _asInt(a["height"]),
value: BigInt.parse(a["value"].toString()),
address: a["address"] as String? ?? "",
### cw_zcash/pubspec.yaml
@@ -20,7 +20,7 @@ dependencies:
zkool:
git:
url: https://github.com/cake-tech/zkool2.git
- ref: b56be32eca373e5ddd0ef703614d4f81cba403f7
+ ref: cb3e96f10e58a8fc8632b40636a0c64aa21fb653
path_provider: any
sqflite_common_ffi: any
### pubspec.lock
@@ -952,6 +952,30 @@ packages:
url: "https://github.com/cake-tech/flutter_file_picker.git"
source: git
version: "8.1.6"
+ file_selector:
+ dependency: transitive
+ description:
+ name: file_selector
+ sha256: bd15e43e9268db636b53eeaca9f56324d1622af30e5c34d6e267649758c84d9a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ file_selector_android:
+ dependency: transitive
+ description:
+ name: file_selector_android
+ sha256: "89243030ea4b3463fb402b44d5eeacc4ccb1c46a88870cb2a5080d693200c1ed"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.5.2+6"
+ file_selector_ios:
+ dependency: transitive
+ description:
+ name: file_selector_ios
+ sha256: "97269e5307a0ab813b1fa2430bada0a96e0afb74848417f8676f64ba5de0051c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.5.3+6"
file_selector_linux:
dependency: "direct overridden"
description:
@@ -979,6 +1003,14 @@ packages:
url: "https://github.com/flutter/packages"
source: git
version: "2.7.0"
+ file_selector_web:
+ dependency: transitive
+ description:
+ name: file_selector_web
+ sha256: "73181fbc5257776d8ecaa6a94ab3c8e920ad143b9132a6d984a9271dfc6928d3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.5"
file_selector_windows:
dependency: "direct overridden"
description:
@@ -2629,8 +2661,8 @@ packages:
dependency: "direct overridden"
description:
path: rust_builder
- ref: b56be32eca373e5ddd0ef703614d4f81cba403f7
- resolved-ref: b56be32eca373e5ddd0ef703614d4f81cba403f7
+ ref: cb3e96f10e58a8fc8632b40636a0c64aa21fb653
+ resolved-ref: cb3e96f10e58a8fc8632b40636a0c64aa21fb653
url: "https://github.com/cake-tech/zkool2.git"
source: git
version: "0.0.1"
@@ -2999,6 +3031,14 @@ packages:
url: "https://github.com/dart-lang/tools"
source: git
version: "1.4.1"
+ swm_chart:
+ dependency: transitive
+ description:
+ name: swm_chart
+ sha256: "9c11363172adb74d046b6bc43ff82b05f7ee8a9e3d71277c6014a1bb38afaa27"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.1"
sync_http:
dependency: "direct overridden"
description:
@@ -3487,11 +3527,11 @@ packages:
dependency: "direct overridden"
description:
path: "."
- ref: b56be32eca373e5ddd0ef703614d4f81cba403f7
- resolved-ref: b56be32eca373e5ddd0ef703614d4f81cba403f7
+ ref: cb3e96f10e58a8fc8632b40636a0c64aa21fb653
+ resolved-ref: cb3e96f10e58a8fc8632b40636a0c64aa21fb653
url: "https://github.com/cake-tech/zkool2.git"
source: git
- version: "6.31.0"
+ version: "6.35.0"
sdks:
dart: ">=3.10.0 <4.0.0"
- flutter: ">=3.19.0"
+ flutter: ">=3.38.0"
### pubspec_overrides.yaml
@@ -1239,7 +1239,7 @@ dependency_overrides:
rlz:
git:
url: https://github.com/cake-tech/zkool2.git
- ref: b56be32eca373e5ddd0ef703614d4f81cba403f7
+ ref: cb3e96f10e58a8fc8632b40636a0c64aa21fb653
path: ./rust_builder
rxdart:
git:
@@ -1681,4 +1681,4 @@ dependency_overrides:
zkool:
git:
url: https://github.com/cake-tech/zkool2.git
- ref: b56be32eca373e5ddd0ef703614d4f81cba403f7
+ ref: cb3e96f10e58a8fc8632b40636a0c64aa21fb653
### scripts/android/docker/Dockerfile.decred
@@ -5,14 +5,19 @@ FROM ${TORCH_IMAGE} AS torch_source
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
RUN mkdir -p /w/scripts/android
RUN mkdir -p /w/cw_decred/lib/api
COPY --from=torch_source /w/scripts/torch_dart /w/scripts/torch_dart
COPY cw_core/pubspec.yaml /w/cw_core/pubspec.yaml
COPY cw_decred/pubspec.yaml /w/cw_decred/pubspec.yaml
COPY pubspec_overrides.yaml /w/cw_decred/pubspec_overrides.yaml
COPY scripts/android/build_decred.sh /w/scripts/android/build_decred.sh
-RUN /w/scripts/android/build_decred.sh
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/android/build_decred.sh
FROM --platform=linux/amd64 alpine
COPY --from=build /w /w
### scripts/android/docker/Dockerfile.monero
@@ -1,6 +1,10 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
COPY scripts/functions.sh /w/scripts/functions.sh
COPY scripts/prepare_moneroc.sh /w/scripts/prepare_moneroc.sh
COPY scripts/android/build_monero_all.sh /w/scripts/android/build_monero_all.sh
@@ -18,7 +22,8 @@ ARG COIN
ARG TARGET
ENV COIN=$COIN
ENV TARGET=$TARGET
-RUN /w/scripts/android/build_monero_all.sh \
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/android/build_monero_all.sh \
&& rm -rf /w/scripts/monero_c/contrib \
/w/scripts/monero_c/.git \
/w/scripts/monero_c/monero_libwallet2_api_c/build \
### scripts/android/docker/Dockerfile.mwebd
@@ -1,6 +1,10 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
RUN mkdir -p /w/scripts/android
COPY pubspec_base.yaml /w/pubspec.yaml
COPY pubspec_overrides.yaml /w/pubspec_overrides.yaml
@@ -9,7 +13,8 @@ COPY cw_mweb/pubspec.yaml /w/cw_mweb/pubspec.yaml
COPY cw_mweb/ffigen_config.yaml /w/cw_mweb/ffigen_config.yaml
COPY cw_mweb/go /w/cw_mweb/go
COPY scripts/android/build_mwebd.sh /w/scripts/android/build_mwebd.sh
-RUN /w/scripts/android/build_mwebd.sh
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/android/build_mwebd.sh
FROM --platform=linux/amd64 alpine
COPY --from=build /w /w
### scripts/android/docker/Dockerfile.reown
@@ -1,12 +1,18 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
RUN mkdir -p /w/scripts/android
COPY scripts/functions.sh /w/scripts/functions.sh
COPY scripts/prepare_reown.sh /w/scripts/prepare_reown.sh
COPY scripts/android/build_reown_deps.sh /w/scripts/android/build_reown_deps.sh
-RUN cd $(dirname $(which flutter)) && git checkout 3.32.0
-RUN /w/scripts/android/build_reown_deps.sh \
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ cd $(dirname $(which flutter)) && git checkout 3.32.0
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/android/build_reown_deps.sh \
&& rm -rf /w/scripts/reown_flutter/packages/reown_core/.dart_tool \
&& rm -rf /w/scripts/reown_flutter/packages/reown_sign/.dart_tool \
&& rm -rf /w/scripts/reown_flutter/packages/reown_cli/.dart_tool \
### scripts/android/docker/Dockerfile.torch
@@ -2,6 +2,10 @@ ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
COPY scripts/prepare_torch.sh /w/scripts/prepare_torch.sh
COPY scripts/android/build_torch.sh /w/scripts/android/build_torch.sh
ARG UID=10001
@@ -14,7 +18,8 @@ RUN sudo chown $UID:$GID -R /w /root
USER builder
ARG TARGET
-RUN if [ -n "$TARGET" ]; then \
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ if [ -n "$TARGET" ]; then \
/w/scripts/android/build_torch.sh "$TARGET"; \
else \
/w/scripts/android/build_torch.sh; \
### scripts/android/docker/Dockerfile.zcash
@@ -1,10 +1,15 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
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 \
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/android/build_zcash.sh \
&& rm -rf /w/scripts/zcash_lib/target
FROM --platform=linux/amd64 alpine
### scripts/android/docker/build.sh
@@ -8,6 +8,7 @@ IMAGE_PREFIX="android-deps"
SCRIPT_DIR="$(pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
+GIT_CREDENTIAL_HELPER="$REPO_ROOT/scripts/docker/git-credential-github.sh"
ONLY=""
TARGET=""
@@ -169,7 +170,16 @@ build() {
return 0
fi
echo "==> building $name${extra}"
- docker build \
+ # Pass the token as a BuildKit secret, not a build-arg. Secret values are
+ # excluded from the layer cache key, so a new GITHUB_TOKEN does not rebuild.
+ local -a secret_args=()
+ if [[ -n "${GITHUB_TOKEN:-}" ]]; then
+ secret_args+=(--secret id=github_token,env=GITHUB_TOKEN)
+ elif [[ -n "${GH_TOKEN:-}" ]]; then
+ secret_args+=(--secret id=github_token,env=GH_TOKEN)
+ fi
+ DOCKER_BUILDKIT=1 docker build \
+ ${secret_args[@]+"${secret_args[@]}"} \
--platform linux/amd64 \
--file "$SCRIPT_DIR/Dockerfile.${name}" \
--tag "$tag" \
@@ -234,13 +244,13 @@ merge_slices() {
}
base_ver="latest"
-torch_ver="$(tinysha $SCRIPT_DIR/Dockerfile.torch $REPO_ROOT/scripts/prepare_torch.sh $REPO_ROOT/scripts/android/build_torch.sh)"
-reown_ver=$(tinysha $SCRIPT_DIR/Dockerfile.reown $REPO_ROOT/scripts/functions.sh $REPO_ROOT/scripts/prepare_reown.sh $REPO_ROOT/scripts/android/build_reown_deps.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 $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f | sort))
-zcash_ver=$(tinysha $SCRIPT_DIR/Dockerfile.zcash $REPO_ROOT/scripts/prepare_zcash.sh $REPO_ROOT/scripts/android/build_zcash.sh)
+torch_ver="$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.torch $REPO_ROOT/scripts/prepare_torch.sh $REPO_ROOT/scripts/android/build_torch.sh)"
+reown_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.reown $REPO_ROOT/scripts/functions.sh $REPO_ROOT/scripts/prepare_reown.sh $REPO_ROOT/scripts/android/build_reown_deps.sh)
+monero_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.monero $REPO_ROOT/scripts/prepare_moneroc.sh $REPO_ROOT/scripts/android/build_monero_all.sh)
+mwebd_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.mwebd $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f | sort))
+zcash_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.zcash $REPO_ROOT/scripts/prepare_zcash.sh $REPO_ROOT/scripts/android/build_zcash.sh)
torch_assembled_extra > /tmp/cw_torch_assembled_extra
-decred_ver=$(tinysha $SCRIPT_DIR/Dockerfile.torch $SCRIPT_DIR/Dockerfile.decred $REPO_ROOT/scripts/android/build_decred.sh $REPO_ROOT/cw_decred/pubspec.yaml $REPO_ROOT/pubspec_overrides.yaml /tmp/cw_torch_assembled_extra)
+decred_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.torch $SCRIPT_DIR/Dockerfile.decred $REPO_ROOT/scripts/android/build_decred.sh $REPO_ROOT/cw_decred/pubspec.yaml $REPO_ROOT/pubspec_overrides.yaml /tmp/cw_torch_assembled_extra)
echo $base_ver $torch_ver $reown_ver $monero_ver $mwebd_ver $zcash_ver $decred_ver "$(torch_assembled_extra)" "$(monero_assembled_extra)" > /tmp/docker_build_versions
final_ver=$(tinysha /tmp/docker_build_versions)
### scripts/docker/git-credential-github.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+# Git credential helper for clones inside `docker build`.
+if [ "$1" != "get" ]; then
+ exit 0
+fi
+
+secret=/run/secrets/github_token
+if [ ! -r "$secret" ]; then
+ exit 0
+fi
+
+token=$(tr -d '\n\r' < "$secret")
+if [ -z "$token" ]; then
+ exit 0
+fi
+
+host=
+while IFS= read -r line || [ -n "$line" ]; do
+ case "$line" in
+ host=*) host=${line#host=} ;;
+ esac
+done
+host=${host%%:*}
+
+case "$host" in
+ github.com) ;;
+ *) exit 0 ;;
+esac
+
+printf 'username=x-access-token\npassword=%s\n' "$token"
### scripts/linux/docker/Dockerfile.monero
@@ -1,6 +1,10 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
ARG UID=10001
ARG GID=10001
ARG USER=builder
@@ -17,7 +21,8 @@ ARG COIN
ARG TARGET
ENV COIN=$COIN
ENV TARGET=$TARGET
-RUN /w/scripts/linux/build_monero_all.sh \
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/linux/build_monero_all.sh \
&& rm -rf /w/scripts/monero_c/contrib/depends/simplybs \
/w/scripts/monero_c/monero_libwallet2_api_c/build \
/w/scripts/monero_c/wownero_libwallet2_api_c/build
### scripts/linux/docker/Dockerfile.mwebd
@@ -1,6 +1,10 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
RUN mkdir -p /w/scripts/android
COPY pubspec_base.yaml /w/pubspec.yaml
COPY pubspec_overrides.yaml /w/pubspec_overrides.yaml
@@ -9,7 +13,8 @@ COPY cw_mweb/pubspec.yaml /w/cw_mweb/pubspec.yaml
COPY cw_mweb/ffigen_config.yaml /w/cw_mweb/ffigen_config.yaml
COPY cw_mweb/go /w/cw_mweb/go
COPY scripts/android/build_mwebd.sh /w/scripts/android/build_mwebd.sh
-RUN /w/scripts/android/build_mwebd.sh
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/android/build_mwebd.sh
FROM --platform=linux/amd64 alpine
COPY --from=build /w /w
### scripts/linux/docker/Dockerfile.reown
@@ -1,12 +1,18 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
RUN mkdir -p /w/scripts/android
COPY scripts/functions.sh /w/scripts/functions.sh
COPY scripts/prepare_reown.sh /w/scripts/prepare_reown.sh
COPY scripts/android/build_reown_deps.sh /w/scripts/android/build_reown_deps.sh
-RUN cd $(dirname $(which flutter)) && git checkout 3.32.0
-RUN /w/scripts/android/build_reown_deps.sh \
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ cd $(dirname $(which flutter)) && git checkout 3.32.0
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/android/build_reown_deps.sh \
&& rm -rf /w/scripts/reown_flutter/packages/reown_core/.dart_tool \
&& rm -rf /w/scripts/reown_flutter/packages/reown_sign/.dart_tool \
&& rm -rf /w/scripts/reown_flutter/packages/reown_cli/.dart_tool \
### scripts/linux/docker/Dockerfile.torch
@@ -2,6 +2,10 @@ ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
COPY scripts/prepare_torch.sh /w/scripts/prepare_torch.sh
COPY scripts/linux/build_torch.sh /w/scripts/linux/build_torch.sh
@@ -15,7 +19,8 @@ RUN sudo chown $UID:$GID -R /w /root
USER builder
ARG TARGET
-RUN if [ -n "$TARGET" ]; then \
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ if [ -n "$TARGET" ]; then \
/w/scripts/linux/build_torch.sh "$TARGET"; \
else \
/w/scripts/linux/build_torch.sh; \
### scripts/linux/docker/Dockerfile.zcash
@@ -1,10 +1,15 @@
ARG BASE_IMAGE
FROM --platform=linux/amd64 ${BASE_IMAGE} AS build
+COPY scripts/docker/git-credential-github.sh /usr/local/bin/git-credential-github
+RUN chmod 755 /usr/local/bin/git-credential-github \
+ && git config --system credential.helper /usr/local/bin/git-credential-github
+
RUN mkdir -p /w/scripts/linux
COPY scripts/prepare_zcash.sh /w/scripts/prepare_zcash.sh
COPY scripts/linux/build_zcash.sh /w/scripts/linux/build_zcash.sh
-RUN /w/scripts/linux/build_zcash.sh
+RUN --mount=type=secret,id=github_token,required=false,mode=0444 \
+ /w/scripts/linux/build_zcash.sh
FROM --platform=linux/amd64 alpine
COPY --from=build /w /w
### scripts/linux/docker/build.sh
@@ -8,6 +8,7 @@ IMAGE_PREFIX="linux-deps-amd64"
SCRIPT_DIR="$(pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
+GIT_CREDENTIAL_HELPER="$REPO_ROOT/scripts/docker/git-credential-github.sh"
ONLY=""
TARGET=""
@@ -150,7 +151,16 @@ build() {
return 0
fi
echo "==> building $name${extra}"
- docker build \
+ # Pass the token as a BuildKit secret, not a build-arg. Secret values are
+ # excluded from the layer cache key, so a new GITHUB_TOKEN does not rebuild.
+ local -a secret_args=()
+ if [[ -n "${GITHUB_TOKEN:-}" ]]; then
+ secret_args+=(--secret id=github_token,env=GITHUB_TOKEN)
+ elif [[ -n "${GH_TOKEN:-}" ]]; then
+ secret_args+=(--secret id=github_token,env=GH_TOKEN)
+ fi
+ DOCKER_BUILDKIT=1 docker build \
+ ${secret_args[@]+"${secret_args[@]}"} \
--platform linux/amd64 \
--file "$SCRIPT_DIR/Dockerfile.${name}" \
--tag "$tag" \
@@ -215,11 +225,11 @@ merge_slices() {
}
base_ver="latest"
-torch_ver="$(tinysha $SCRIPT_DIR/Dockerfile.torch $REPO_ROOT/scripts/prepare_torch.sh $REPO_ROOT/scripts/linux/build_torch.sh)"
-reown_ver=$(tinysha $SCRIPT_DIR/Dockerfile.reown $REPO_ROOT/scripts/functions.sh $REPO_ROOT/scripts/prepare_reown.sh $REPO_ROOT/scripts/android/build_reown_deps.sh)
-monero_ver=$(tinysha $SCRIPT_DIR/Dockerfile.monero $REPO_ROOT/scripts/prepare_moneroc.sh $REPO_ROOT/scripts/linux/build_monero_all.sh)
-mwebd_ver=$(tinysha $SCRIPT_DIR/Dockerfile.mwebd $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f | sort))
-zcash_ver=$(tinysha $SCRIPT_DIR/Dockerfile.zcash $REPO_ROOT/scripts/prepare_zcash.sh $REPO_ROOT/scripts/linux/build_zcash.sh)
+torch_ver="$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.torch $REPO_ROOT/scripts/prepare_torch.sh $REPO_ROOT/scripts/linux/build_torch.sh)"
+reown_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.reown $REPO_ROOT/scripts/functions.sh $REPO_ROOT/scripts/prepare_reown.sh $REPO_ROOT/scripts/android/build_reown_deps.sh)
+monero_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.monero $REPO_ROOT/scripts/prepare_moneroc.sh $REPO_ROOT/scripts/linux/build_monero_all.sh)
+mwebd_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.mwebd $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f | sort))
+zcash_ver=$(tinysha $GIT_CREDENTIAL_HELPER $SCRIPT_DIR/Dockerfile.zcash $REPO_ROOT/scripts/prepare_zcash.sh $REPO_ROOT/scripts/linux/build_zcash.sh)
echo $base_ver $torch_ver $reown_ver $monero_ver $mwebd_ver $zcash_ver "$(monero_assembled_extra)" > /tmp/docker_build_versions
final_ver=$(tinysha /tmp/docker_build_versions)
Why this scored 27/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.