@aztec/ethereum 0.0.1-commit.c31f2472 → 0.0.1-commit.c52d6e7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/client.d.ts +25 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +60 -8
- package/dest/config.d.ts +29 -8
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +69 -14
- package/dest/contracts/chain_state_override.d.ts +78 -0
- package/dest/contracts/chain_state_override.d.ts.map +1 -0
- package/dest/contracts/chain_state_override.js +137 -0
- package/dest/contracts/fee_asset_handler.d.ts +1 -1
- package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
- package/dest/contracts/fee_asset_handler.js +2 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
- package/dest/contracts/fee_asset_price_oracle.js +656 -0
- package/dest/contracts/governance.d.ts +135 -35
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +231 -18
- package/dest/contracts/governance_proposer.d.ts +33 -2
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +58 -7
- package/dest/contracts/gse.d.ts +8 -2
- package/dest/contracts/gse.d.ts.map +1 -1
- package/dest/contracts/gse.js +11 -2
- package/dest/contracts/inbox.d.ts +3 -3
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +12 -7
- package/dest/contracts/index.d.ts +4 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +3 -2
- package/dest/contracts/multicall.d.ts +130 -13
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +203 -93
- package/dest/contracts/outbox.d.ts +27 -4
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +34 -7
- package/dest/contracts/registry.d.ts +3 -1
- package/dest/contracts/registry.d.ts.map +1 -1
- package/dest/contracts/registry.js +30 -1
- package/dest/contracts/rollup.d.ts +5428 -31
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +414 -75
- package/dest/contracts/slashing_proposer.d.ts +170 -0
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/slashing_proposer.js +748 -0
- package/dest/deploy_aztec_l1_contracts.d.ts +19 -12
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +65 -39
- package/dest/deploy_l1_contract.js +3 -3
- package/dest/foundry_binary.d.ts +13 -0
- package/dest/foundry_binary.d.ts.map +1 -0
- package/dest/foundry_binary.js +52 -0
- package/dest/l1_artifacts.d.ts +15526 -15844
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_artifacts.js +9 -24
- package/dest/l1_contract_addresses.d.ts +61 -65
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +101 -82
- package/dest/l1_reader.d.ts +5 -5
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +9 -7
- package/dest/l1_tx_utils/config.d.ts +7 -1
- package/dest/l1_tx_utils/config.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.js +17 -4
- package/dest/l1_tx_utils/constants.d.ts +1 -1
- package/dest/l1_tx_utils/constants.js +2 -2
- package/dest/l1_tx_utils/factory.d.ts +18 -10
- package/dest/l1_tx_utils/factory.d.ts.map +1 -1
- package/dest/l1_tx_utils/factory.js +17 -7
- package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
- package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
- package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
- package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
- package/dest/l1_tx_utils/index-blobs.js +2 -2
- package/dest/l1_tx_utils/index.d.ts +2 -1
- package/dest/l1_tx_utils/index.d.ts.map +1 -1
- package/dest/l1_tx_utils/index.js +1 -0
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +23 -4
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_fee_analyzer.js +65 -3
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -7
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +185 -88
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +13 -8
- package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
- package/dest/{test → l1_tx_utils}/tx_delayer.js +67 -37
- package/dest/l1_tx_utils/types.d.ts +27 -1
- package/dest/l1_tx_utils/types.d.ts.map +1 -1
- package/dest/l1_tx_utils/types.js +10 -0
- package/dest/publisher_manager.d.ts +34 -8
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +119 -8
- package/dest/queries.d.ts +13 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +69 -4
- package/dest/test/blob_kzg_warmup.d.ts +19 -0
- package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
- package/dest/test/blob_kzg_warmup.js +64 -0
- package/dest/test/chain_monitor.d.ts +55 -4
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +104 -3
- package/dest/test/eth_cheat_codes.d.ts +37 -6
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +89 -41
- package/dest/test/index.d.ts +2 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -2
- package/dest/test/rollup_cheat_codes.d.ts +60 -3
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +86 -7
- package/dest/test/start_anvil.d.ts +23 -3
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +182 -32
- package/dest/test/upgrade_utils.js +2 -2
- package/dest/utils.d.ts +10 -2
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +94 -16
- package/package.json +6 -9
- package/src/client.ts +66 -2
- package/src/config.ts +97 -18
- package/src/contracts/chain_state_override.ts +200 -0
- package/src/contracts/fee_asset_handler.ts +2 -0
- package/src/contracts/fee_asset_price_oracle.ts +285 -0
- package/src/contracts/governance.ts +315 -13
- package/src/contracts/governance_proposer.ts +62 -10
- package/src/contracts/gse.ts +19 -2
- package/src/contracts/inbox.ts +13 -5
- package/src/contracts/index.ts +3 -2
- package/src/contracts/multicall.ts +247 -103
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/registry.ts +31 -1
- package/src/contracts/rollup.ts +467 -78
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +93 -28
- package/src/deploy_aztec_l1_contracts.ts +92 -47
- package/src/deploy_l1_contract.ts +3 -3
- package/src/foundry_binary.ts +57 -0
- package/src/l1_artifacts.ts +12 -35
- package/src/l1_contract_addresses.ts +120 -94
- package/src/l1_reader.ts +17 -10
- package/src/l1_tx_utils/config.ts +23 -3
- package/src/l1_tx_utils/constants.ts +2 -2
- package/src/l1_tx_utils/factory.ts +31 -31
- package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
- package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
- package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
- package/src/l1_tx_utils/index-blobs.ts +2 -2
- package/src/l1_tx_utils/index.ts +1 -0
- package/src/l1_tx_utils/l1_fee_analyzer.ts +75 -3
- package/src/l1_tx_utils/l1_tx_utils.ts +170 -68
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +13 -8
- package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
- package/src/l1_tx_utils/types.ts +32 -0
- package/src/publisher_manager.ts +143 -12
- package/src/queries.ts +76 -4
- package/src/test/blob_kzg_warmup.ts +65 -0
- package/src/test/chain_monitor.ts +148 -3
- package/src/test/eth_cheat_codes.ts +82 -45
- package/src/test/index.ts +1 -2
- package/src/test/rollup_cheat_codes.ts +115 -6
- package/src/test/start_anvil.ts +212 -32
- package/src/test/upgrade_utils.ts +2 -2
- package/src/utils.ts +98 -18
- package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
- package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/empire_slashing_proposer.js +0 -209
- package/dest/contracts/tally_slashing_proposer.d.ts +0 -140
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/tally_slashing_proposer.js +0 -313
- package/dest/generated/l1-contracts-defaults.d.ts +0 -30
- package/dest/generated/l1-contracts-defaults.d.ts.map +0 -1
- package/dest/generated/l1-contracts-defaults.js +0 -30
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
- package/dest/test/delayed_tx_utils.d.ts +0 -13
- package/dest/test/delayed_tx_utils.d.ts.map +0 -1
- package/dest/test/delayed_tx_utils.js +0 -28
- package/dest/test/tx_delayer.d.ts +0 -36
- package/dest/test/tx_delayer.d.ts.map +0 -1
- package/src/contracts/empire_slashing_proposer.ts +0 -270
- package/src/generated/l1-contracts-defaults.ts +0 -32
- package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
- package/src/test/delayed_tx_utils.ts +0 -52
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { L1TxRequest } from '@aztec/ethereum/l1-tx-utils';
|
|
2
2
|
import type { ViemClient } from '@aztec/ethereum/types';
|
|
3
|
-
import { tryExtractEvent } from '@aztec/ethereum/utils';
|
|
3
|
+
import { mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
4
4
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
5
5
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
6
|
+
import { memoize } from '@aztec/foundation/decorators';
|
|
6
7
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
8
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
8
9
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
9
|
-
import {
|
|
10
|
+
import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
|
|
11
|
+
import { SlashingProposerAbi } from '@aztec/l1-artifacts/SlashingProposerAbi';
|
|
10
12
|
|
|
11
13
|
import {
|
|
12
14
|
type GetContractReturnType,
|
|
@@ -18,13 +20,19 @@ import {
|
|
|
18
20
|
} from 'viem';
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
* Wrapper around the
|
|
23
|
+
* Wrapper around the SlashingProposer contract that provides
|
|
22
24
|
* a TypeScript interface for interacting with the consensus-based slashing system.
|
|
23
25
|
*/
|
|
24
|
-
export class
|
|
25
|
-
private readonly contract: GetContractReturnType<typeof
|
|
26
|
+
export class SlashingProposerContract {
|
|
27
|
+
private readonly contract: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Slash target validators of the last round asked for. Safe to cache because a round's targets are the committees
|
|
31
|
+
* of epochs that had already ended when the round opened (see SLASH_OFFSET_IN_ROUNDS), and those committees are
|
|
32
|
+
* sampled from validator set and randao snapshots taken before the epoch started, so they cannot change while the
|
|
33
|
+
* round is live. The promise is cached rather than the result, so concurrent callers share one in-flight read.
|
|
34
|
+
*/
|
|
35
|
+
private slashTargetValidators: { round: bigint; validators: Promise<EthAddress[]> } | undefined;
|
|
28
36
|
|
|
29
37
|
constructor(
|
|
30
38
|
public readonly client: ViemClient,
|
|
@@ -32,7 +40,7 @@ export class TallySlashingProposerContract {
|
|
|
32
40
|
) {
|
|
33
41
|
this.contract = getContract({
|
|
34
42
|
address: typeof address === 'string' ? address : address.toString(),
|
|
35
|
-
abi:
|
|
43
|
+
abi: SlashingProposerAbi,
|
|
36
44
|
client,
|
|
37
45
|
});
|
|
38
46
|
}
|
|
@@ -65,6 +73,8 @@ export class TallySlashingProposerContract {
|
|
|
65
73
|
return this.contract.read.EXECUTION_DELAY_IN_ROUNDS();
|
|
66
74
|
}
|
|
67
75
|
|
|
76
|
+
/** Returns the slash amounts for the three slash unit levels. Immutable on the contract, so memoized. */
|
|
77
|
+
@memoize
|
|
68
78
|
public getSlashingAmounts(): Promise<[bigint, bigint, bigint]> {
|
|
69
79
|
return Promise.all([
|
|
70
80
|
this.contract.read.SLASH_AMOUNT_SMALL(),
|
|
@@ -135,12 +145,12 @@ export class TallySlashingProposerContract {
|
|
|
135
145
|
|
|
136
146
|
/** Tries to extract a VoteCast event from the given logs. */
|
|
137
147
|
public tryExtractVoteCastEvent(logs: Log[]) {
|
|
138
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
148
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'VoteCast');
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
/** Tries to extract a RoundExecuted event from the given logs. */
|
|
142
152
|
public tryExtractRoundExecutedEvent(logs: Log[]) {
|
|
143
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
153
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'RoundExecuted');
|
|
144
154
|
}
|
|
145
155
|
|
|
146
156
|
/**
|
|
@@ -160,8 +170,9 @@ export class TallySlashingProposerContract {
|
|
|
160
170
|
|
|
161
171
|
return {
|
|
162
172
|
to: this.contract.address,
|
|
173
|
+
abi: SlashingProposerAbi,
|
|
163
174
|
data: encodeFunctionData({
|
|
164
|
-
abi:
|
|
175
|
+
abi: SlashingProposerAbi,
|
|
165
176
|
functionName: 'vote',
|
|
166
177
|
args: [votes, signature.toViemSignature()],
|
|
167
178
|
}),
|
|
@@ -171,7 +182,7 @@ export class TallySlashingProposerContract {
|
|
|
171
182
|
/** Returns the typed data definition to EIP712-sign for voting */
|
|
172
183
|
public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
|
|
173
184
|
const domain = {
|
|
174
|
-
name: '
|
|
185
|
+
name: 'SlashingProposer',
|
|
175
186
|
version: '1',
|
|
176
187
|
chainId: this.client.chain.id,
|
|
177
188
|
verifyingContract: this.contract.address,
|
|
@@ -207,8 +218,9 @@ export class TallySlashingProposerContract {
|
|
|
207
218
|
public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
|
|
208
219
|
return {
|
|
209
220
|
to: this.contract.address,
|
|
221
|
+
abi: SlashingProposerAbi,
|
|
210
222
|
data: encodeFunctionData({
|
|
211
|
-
abi:
|
|
223
|
+
abi: SlashingProposerAbi,
|
|
212
224
|
functionName: 'vote',
|
|
213
225
|
args: [votes, signature],
|
|
214
226
|
}),
|
|
@@ -224,43 +236,79 @@ export class TallySlashingProposerContract {
|
|
|
224
236
|
public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
|
|
225
237
|
return {
|
|
226
238
|
to: this.contract.address,
|
|
239
|
+
abi: mergeAbis([SlashingProposerAbi, SlasherAbi]),
|
|
227
240
|
data: encodeFunctionData({
|
|
228
|
-
abi:
|
|
241
|
+
abi: SlashingProposerAbi,
|
|
229
242
|
functionName: 'executeRound',
|
|
230
243
|
args: [round, committees.map(c => c.map(addr => addr.toString()))],
|
|
231
244
|
}),
|
|
232
245
|
};
|
|
233
246
|
}
|
|
234
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Returns the validators eligible to be voted against in a round, in the order votes encode them. Cached for the
|
|
250
|
+
* last round asked for: reading them runs a committee sampling simulation on L1, and every vote of a round decodes
|
|
251
|
+
* against the same list, so a caller walking a round's votes would otherwise repeat that call per vote. Only the
|
|
252
|
+
* last round is kept since callers move forward round by round.
|
|
253
|
+
*/
|
|
254
|
+
public getSlashTargetValidators(round: bigint): Promise<EthAddress[]> {
|
|
255
|
+
const cached = this.slashTargetValidators;
|
|
256
|
+
if (cached?.round === round) {
|
|
257
|
+
return cached.validators;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const entry = { round, validators: this.fetchSlashTargetValidators(round) };
|
|
261
|
+
this.slashTargetValidators = entry;
|
|
262
|
+
// A failed read must not stay cached, or every later vote of the round would replay the same rejection
|
|
263
|
+
entry.validators.catch(() => {
|
|
264
|
+
if (this.slashTargetValidators === entry) {
|
|
265
|
+
this.slashTargetValidators = undefined;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
return entry.validators;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private async fetchSlashTargetValidators(round: bigint): Promise<EthAddress[]> {
|
|
272
|
+
const { result } = await this.contract.simulate.getSlashTargetCommittees([round]);
|
|
273
|
+
return result.flat().map(validator => EthAddress.fromString(validator));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Returns the slash amount voted for each target validator by a single vote of a round.
|
|
278
|
+
* @param index - Position of the vote within the round, from 0 (inclusive) to the round's vote count (exclusive)
|
|
279
|
+
*/
|
|
280
|
+
public async getVoteAt(round: bigint, index: bigint): Promise<SlashVoteTarget[]> {
|
|
281
|
+
const [validators, vote, slashAmounts] = await Promise.all([
|
|
282
|
+
this.getSlashTargetValidators(round),
|
|
283
|
+
this.contract.read.getVotes([round, index]),
|
|
284
|
+
this.getSlashingAmounts(),
|
|
285
|
+
]);
|
|
286
|
+
return decodeVote(vote, validators, slashAmounts);
|
|
287
|
+
}
|
|
288
|
+
|
|
235
289
|
/** Returns the last vote emitted for a given round */
|
|
236
290
|
public async getLastVote(round: bigint) {
|
|
237
291
|
const { voteCount } = await this.getRound(round);
|
|
238
|
-
|
|
239
|
-
const vote = await this.contract.read.getVotes([round, voteCount - 1n]);
|
|
240
|
-
const decoded = decodeSlashConsensusVotes(hexToBuffer(vote));
|
|
241
|
-
const slashAmounts = await this.getSlashingAmounts();
|
|
242
|
-
return decoded
|
|
243
|
-
.map((units, i) => ({
|
|
244
|
-
validator: EthAddress.fromString(validators[i]),
|
|
245
|
-
slashAmount: slashAmounts[units - 1] ?? 0n,
|
|
246
|
-
}))
|
|
247
|
-
.filter(v => v.slashAmount > 0n);
|
|
292
|
+
return await this.getVoteAt(round, voteCount - 1n);
|
|
248
293
|
}
|
|
249
294
|
|
|
250
295
|
/**
|
|
251
296
|
* Listen for VoteCast events
|
|
252
|
-
* @param callback - Callback
|
|
297
|
+
* @param callback - Callback receiving the round, the vote's index within the round as accepted by `getVoteAt`,
|
|
298
|
+
* and the proposer that cast it
|
|
253
299
|
* @returns Unwatch function
|
|
254
300
|
*/
|
|
255
|
-
public listenToVoteCast(
|
|
301
|
+
public listenToVoteCast(
|
|
302
|
+
callback: (args: { round: bigint; voteIndex: bigint; proposer: string }) => void,
|
|
303
|
+
): () => void {
|
|
256
304
|
return this.contract.watchEvent.VoteCast(
|
|
257
305
|
{},
|
|
258
306
|
{
|
|
259
307
|
onLogs: logs => {
|
|
260
308
|
for (const log of logs) {
|
|
261
|
-
const { round, proposer } = log.args;
|
|
262
|
-
if (round !== undefined && proposer) {
|
|
263
|
-
callback({ round, proposer });
|
|
309
|
+
const { round, voteIndex, proposer } = log.args;
|
|
310
|
+
if (round !== undefined && voteIndex !== undefined && proposer) {
|
|
311
|
+
callback({ round, voteIndex, proposer });
|
|
264
312
|
}
|
|
265
313
|
}
|
|
266
314
|
},
|
|
@@ -292,6 +340,23 @@ export class TallySlashingProposerContract {
|
|
|
292
340
|
}
|
|
293
341
|
}
|
|
294
342
|
|
|
343
|
+
/**
|
|
344
|
+
* A validator targeted by a slashing vote, with the amount voted. The position is the validator's index in the
|
|
345
|
+
* round's flattened slash target committees — the unit the contract tallies quorum by. A validator sitting in
|
|
346
|
+
* several of the round's committees holds several positions, each with its own tally.
|
|
347
|
+
*/
|
|
348
|
+
export type SlashVoteTarget = { validator: EthAddress; slashAmount: bigint; position: number };
|
|
349
|
+
|
|
350
|
+
function decodeVote(vote: Hex, validators: EthAddress[], slashAmounts: [bigint, bigint, bigint]): SlashVoteTarget[] {
|
|
351
|
+
return decodeSlashConsensusVotes(hexToBuffer(vote))
|
|
352
|
+
.map((units, position) => ({
|
|
353
|
+
validator: validators[position],
|
|
354
|
+
slashAmount: slashAmounts[units - 1] ?? 0n,
|
|
355
|
+
position,
|
|
356
|
+
}))
|
|
357
|
+
.filter(v => v.slashAmount > 0n);
|
|
358
|
+
}
|
|
359
|
+
|
|
295
360
|
/**
|
|
296
361
|
* Decodes a Buffer containing slash votes back into an array of numbers.
|
|
297
362
|
* Each vote is represented as a 2-bit value (0, 1, 2, or 3) representing slashing units.
|
|
@@ -5,35 +5,37 @@ import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
|
5
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
6
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
7
7
|
import type { Fr } from '@aztec/foundation/schemas';
|
|
8
|
-
import { fileURLToPath } from '@aztec/foundation/url';
|
|
9
8
|
|
|
10
9
|
import { bn254 } from '@noble/curves/bn254';
|
|
11
10
|
import type { Abi, Narrow } from 'abitype';
|
|
12
11
|
import { spawn } from 'child_process';
|
|
13
12
|
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
14
|
import { tmpdir } from 'os';
|
|
15
15
|
import { dirname, join, resolve } from 'path';
|
|
16
16
|
import readline from 'readline';
|
|
17
17
|
import type { Hex } from 'viem';
|
|
18
|
-
import {
|
|
18
|
+
import { mainnet, sepolia } from 'viem/chains';
|
|
19
19
|
|
|
20
20
|
import { createEthereumChain, isAnvilTestChain } from './chain.js';
|
|
21
21
|
import { createExtendedL1Client } from './client.js';
|
|
22
|
-
import type
|
|
22
|
+
import { type L1ContractsConfig, assertValidSlotDurations } from './config.js';
|
|
23
23
|
import { deployMulticall3 } from './contracts/multicall.js';
|
|
24
24
|
import { RollupContract } from './contracts/rollup.js';
|
|
25
|
+
import { resolveFoundryBinary } from './foundry_binary.js';
|
|
25
26
|
import type { L1ContractAddresses } from './l1_contract_addresses.js';
|
|
26
|
-
import type { L1TxUtilsConfig } from './l1_tx_utils/config.js';
|
|
27
27
|
import type { ExtendedViemWalletClient } from './types.js';
|
|
28
28
|
|
|
29
29
|
const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
|
|
30
30
|
|
|
31
|
+
const require = createRequire(import.meta.url);
|
|
32
|
+
|
|
31
33
|
const JSON_DEPLOY_RESULT_PREFIX = 'JSON DEPLOY RESULT:';
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
|
-
* Runs a process
|
|
35
|
-
*
|
|
36
|
-
*
|
|
36
|
+
* Runs a process and parses JSON deploy results from stdout.
|
|
37
|
+
* Lines starting with JSON_DEPLOY_RESULT_PREFIX are parsed and returned.
|
|
38
|
+
* All other stdout goes to logger.info, stderr goes to logger.warn.
|
|
37
39
|
*/
|
|
38
40
|
function runProcess<T>(
|
|
39
41
|
command: string,
|
|
@@ -49,26 +51,41 @@ function runProcess<T>(
|
|
|
49
51
|
});
|
|
50
52
|
|
|
51
53
|
let result: T | undefined;
|
|
54
|
+
let parseError: Error | undefined;
|
|
55
|
+
let settled = false;
|
|
52
56
|
|
|
53
57
|
readline.createInterface({ input: proc.stdout }).on('line', line => {
|
|
54
58
|
const trimmedLine = line.trim();
|
|
55
59
|
if (trimmedLine.startsWith(JSON_DEPLOY_RESULT_PREFIX)) {
|
|
56
60
|
const jsonStr = trimmedLine.slice(JSON_DEPLOY_RESULT_PREFIX.length).trim();
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
try {
|
|
62
|
+
result = JSON.parse(jsonStr);
|
|
63
|
+
} catch {
|
|
64
|
+
parseError = new Error(`Failed to parse deploy result JSON: ${jsonStr.slice(0, 200)}`);
|
|
65
|
+
}
|
|
59
66
|
} else {
|
|
60
67
|
logger.info(line);
|
|
61
68
|
}
|
|
62
69
|
});
|
|
63
|
-
readline.createInterface({ input: proc.stderr }).on('line', logger.
|
|
70
|
+
readline.createInterface({ input: proc.stderr }).on('line', logger.warn.bind(logger));
|
|
64
71
|
|
|
65
72
|
proc.on('error', error => {
|
|
73
|
+
if (settled) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
settled = true;
|
|
66
77
|
reject(new Error(`Failed to spawn ${command}: ${error.message}`));
|
|
67
78
|
});
|
|
68
79
|
|
|
69
80
|
proc.on('close', code => {
|
|
81
|
+
if (settled) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
settled = true;
|
|
70
85
|
if (code !== 0) {
|
|
71
|
-
reject(new Error(`${command} exited with code ${code}
|
|
86
|
+
reject(new Error(`${command} exited with code ${code}`));
|
|
87
|
+
} else if (parseError) {
|
|
88
|
+
reject(parseError);
|
|
72
89
|
} else {
|
|
73
90
|
resolve(result);
|
|
74
91
|
}
|
|
@@ -79,11 +96,16 @@ function runProcess<T>(
|
|
|
79
96
|
|
|
80
97
|
// Covers an edge where where we may have a cached BlobLib that is not meant for production.
|
|
81
98
|
// Despite the profile apparently sometimes cached code remains (so says Lasse after his ignition-monorepo arc).
|
|
82
|
-
async function maybeForgeForceProductionBuild(
|
|
99
|
+
async function maybeForgeForceProductionBuild(
|
|
100
|
+
forgeBin: string,
|
|
101
|
+
l1ContractsPath: string,
|
|
102
|
+
script: string,
|
|
103
|
+
chainId: number,
|
|
104
|
+
) {
|
|
83
105
|
if (chainId === mainnet.id) {
|
|
84
106
|
logger.info(`Recompiling ${script} with production profile for mainnet deployment`);
|
|
85
107
|
logger.info('This may take a minute but ensures production BlobLib is used.');
|
|
86
|
-
await runProcess(
|
|
108
|
+
await runProcess(forgeBin, ['build', script, '--force'], { FOUNDRY_PROFILE: 'production' }, l1ContractsPath);
|
|
87
109
|
}
|
|
88
110
|
}
|
|
89
111
|
|
|
@@ -109,15 +131,13 @@ export interface ValidatorJson {
|
|
|
109
131
|
}
|
|
110
132
|
|
|
111
133
|
/**
|
|
112
|
-
* Gets the path to the l1-contracts foundry artifacts directory.
|
|
113
|
-
*
|
|
114
|
-
*
|
|
134
|
+
* Gets the path to the l1-contracts foundry artifacts directory bundled inside @aztec/l1-artifacts.
|
|
135
|
+
* Resolved through the package (its "." export -> dest/index.js) so it works whether the package is
|
|
136
|
+
* linked via portal (monorepo) or installed under node_modules (published npm) — resolution follows
|
|
137
|
+
* the symlink in the portal case. The bundled foundry subtree sits alongside dest/, at <pkg>/l1-contracts.
|
|
115
138
|
*/
|
|
116
139
|
export function getL1ContractsPath(): string {
|
|
117
|
-
|
|
118
|
-
// Go up from yarn-project/ethereum/dest to yarn-project, then to l1-artifacts/l1-contracts
|
|
119
|
-
const l1ContractsPath = resolve(currentDir, '..', '..', 'l1-artifacts', 'l1-contracts');
|
|
120
|
-
return l1ContractsPath;
|
|
140
|
+
return resolve(dirname(require.resolve('@aztec/l1-artifacts')), '..', 'l1-contracts');
|
|
121
141
|
}
|
|
122
142
|
|
|
123
143
|
// Cached deployment directory
|
|
@@ -168,6 +188,9 @@ export function prepareL1ContractsForDeployment(): string {
|
|
|
168
188
|
const foundryTomlPath = join(basePath, 'foundry.toml');
|
|
169
189
|
let foundryToml = readFileSync(foundryTomlPath, 'utf-8');
|
|
170
190
|
const solcPathMatch = foundryToml.match(/solc\s*=\s*"\.\/solc-([^"]+)"/);
|
|
191
|
+
// Did we find a hardcoded solc path that we need to make absolute?
|
|
192
|
+
// This code path happens in CI currently as we bundle solc there to avoid race conditions when
|
|
193
|
+
// downloading solc.
|
|
171
194
|
if (solcPathMatch) {
|
|
172
195
|
const solcVersion = solcPathMatch[1];
|
|
173
196
|
const absoluteSolcPath = join(basePath, `solc-${solcVersion}`);
|
|
@@ -213,7 +236,6 @@ export function computeValidatorData(operator: Operator): ValidatorJson {
|
|
|
213
236
|
export interface RollupUpgradeAddresses {
|
|
214
237
|
rollupAddress: string;
|
|
215
238
|
verifierAddress: string;
|
|
216
|
-
slashFactoryAddress: string;
|
|
217
239
|
inboxAddress: string;
|
|
218
240
|
outboxAddress: string;
|
|
219
241
|
feeJuicePortalAddress: string;
|
|
@@ -226,7 +248,6 @@ export interface RollupUpgradeAddresses {
|
|
|
226
248
|
export interface ForgeRollupUpgradeResult {
|
|
227
249
|
rollupAddress: Hex;
|
|
228
250
|
verifierAddress: Hex;
|
|
229
|
-
slashFactoryAddress: Hex;
|
|
230
251
|
inboxAddress: Hex;
|
|
231
252
|
outboxAddress: Hex;
|
|
232
253
|
feeJuicePortalAddress: Hex;
|
|
@@ -267,6 +288,7 @@ export async function deployAztecL1Contracts(
|
|
|
267
288
|
args: DeployAztecL1ContractsArgs,
|
|
268
289
|
): Promise<DeployAztecL1ContractsReturnType> {
|
|
269
290
|
logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
|
|
291
|
+
assertValidSlotDurations(args);
|
|
270
292
|
if (args.initialValidators && args.initialValidators.length > 0 && args.existingTokenAddress) {
|
|
271
293
|
throw new Error(
|
|
272
294
|
'Cannot deploy with both initialValidators and existingTokenAddress. ' +
|
|
@@ -304,8 +326,9 @@ export async function deployAztecL1Contracts(
|
|
|
304
326
|
// Use foundry-artifacts from l1-artifacts package
|
|
305
327
|
const l1ContractsPath = prepareL1ContractsForDeployment();
|
|
306
328
|
|
|
329
|
+
const forgeBin = resolveFoundryBinary('forge');
|
|
307
330
|
const FORGE_SCRIPT = 'script/deploy/DeployAztecL1Contracts.s.sol';
|
|
308
|
-
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
331
|
+
await maybeForgeForceProductionBuild(forgeBin, l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
309
332
|
|
|
310
333
|
// Verify contracts on Etherscan when on mainnet/sepolia and ETHERSCAN_API_KEY is available.
|
|
311
334
|
const isVerifiableChain = chainId === mainnet.id || chainId === sepolia.id;
|
|
@@ -318,11 +341,8 @@ export async function deployAztecL1Contracts(
|
|
|
318
341
|
);
|
|
319
342
|
}
|
|
320
343
|
|
|
321
|
-
|
|
322
|
-
const MAGIC_ANVIL_BATCH_SIZE = 8;
|
|
323
|
-
// Anvil seems to stall with unbounded batch size. Otherwise no max batch size is desirable.
|
|
344
|
+
const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
|
|
324
345
|
const forgeArgs = [
|
|
325
|
-
'script',
|
|
326
346
|
FORGE_SCRIPT,
|
|
327
347
|
'--sig',
|
|
328
348
|
'run()',
|
|
@@ -330,17 +350,22 @@ export async function deployAztecL1Contracts(
|
|
|
330
350
|
privateKey,
|
|
331
351
|
'--rpc-url',
|
|
332
352
|
rpcUrl,
|
|
333
|
-
'--broadcast',
|
|
334
|
-
...(chainId === foundry.id ? ['--batch-size', MAGIC_ANVIL_BATCH_SIZE.toString()] : []),
|
|
335
353
|
...(shouldVerify ? ['--verify'] : []),
|
|
336
354
|
];
|
|
337
355
|
const forgeEnv = {
|
|
356
|
+
// Resolved forge binary picked up by forge_broadcast.js, so it works without forge on PATH.
|
|
357
|
+
FORGE_BIN: forgeBin,
|
|
338
358
|
// Env vars required by l1-contracts/script/deploy/DeploymentConfiguration.sol.
|
|
339
359
|
NETWORK: getActiveNetworkName(),
|
|
340
360
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
341
361
|
...getDeployAztecL1ContractsEnvVars(args),
|
|
342
362
|
};
|
|
343
|
-
const result = await runProcess<ForgeL1ContractsDeployResult>(
|
|
363
|
+
const result = await runProcess<ForgeL1ContractsDeployResult>(
|
|
364
|
+
process.execPath,
|
|
365
|
+
[scriptPath, ...forgeArgs],
|
|
366
|
+
forgeEnv,
|
|
367
|
+
l1ContractsPath,
|
|
368
|
+
);
|
|
344
369
|
if (!result) {
|
|
345
370
|
throw new Error('Forge script did not output deployment result');
|
|
346
371
|
}
|
|
@@ -386,7 +411,6 @@ export async function deployAztecL1Contracts(
|
|
|
386
411
|
governanceProposerAddress: EthAddress.fromString(result.governanceProposerAddress),
|
|
387
412
|
governanceAddress: EthAddress.fromString(result.governanceAddress),
|
|
388
413
|
stakingAssetAddress: EthAddress.fromString(result.stakingAssetAddress),
|
|
389
|
-
slashFactoryAddress: result.slashFactoryAddress ? EthAddress.fromString(result.slashFactoryAddress) : undefined,
|
|
390
414
|
feeAssetHandlerAddress: result.feeAssetHandlerAddress
|
|
391
415
|
? EthAddress.fromString(result.feeAssetHandlerAddress)
|
|
392
416
|
: undefined,
|
|
@@ -473,7 +497,25 @@ export type VerificationRecord = {
|
|
|
473
497
|
libraries: VerificationLibraryEntry[];
|
|
474
498
|
};
|
|
475
499
|
|
|
476
|
-
export interface DeployAztecL1ContractsArgs
|
|
500
|
+
export interface DeployAztecL1ContractsArgs
|
|
501
|
+
extends Omit<
|
|
502
|
+
L1ContractsConfig,
|
|
503
|
+
| 'gasLimitBufferPercentage'
|
|
504
|
+
| 'maxGwei'
|
|
505
|
+
| 'maxBlobGwei'
|
|
506
|
+
| 'priorityFeeBumpPercentage'
|
|
507
|
+
| 'priorityFeeRetryBumpPercentage'
|
|
508
|
+
| 'minimumPriorityFeePerGas'
|
|
509
|
+
| 'maxSpeedUpAttempts'
|
|
510
|
+
| 'checkIntervalMs'
|
|
511
|
+
| 'stallTimeMs'
|
|
512
|
+
| 'txTimeoutMs'
|
|
513
|
+
| 'cancelTxOnTimeout'
|
|
514
|
+
| 'txCancellationFinalTimeoutMs'
|
|
515
|
+
| 'txUnseenConsideredDroppedMs'
|
|
516
|
+
| 'enableDelayer'
|
|
517
|
+
| 'txDelayerMaxInclusionTimeIntoSlot'
|
|
518
|
+
> {
|
|
477
519
|
/** The vk tree root. */
|
|
478
520
|
vkTreeRoot: Fr;
|
|
479
521
|
/** The hash of the protocol contracts. */
|
|
@@ -550,13 +592,18 @@ export function getDeployRollupForUpgradeEnvVars(
|
|
|
550
592
|
AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
|
|
551
593
|
AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
|
|
552
594
|
AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
|
|
553
|
-
|
|
595
|
+
AZTEC_SLASHER_ENABLED: args.slasherEnabled ? 'true' : 'false',
|
|
554
596
|
AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
|
|
555
597
|
AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
|
|
556
598
|
AZTEC_SLASHING_OFFSET_IN_ROUNDS: args.slashingOffsetInRounds.toString(),
|
|
557
599
|
AZTEC_SLASH_AMOUNT_SMALL: args.slashAmountSmall.toString(),
|
|
558
600
|
AZTEC_SLASH_AMOUNT_MEDIUM: args.slashAmountMedium.toString(),
|
|
559
601
|
AZTEC_SLASH_AMOUNT_LARGE: args.slashAmountLarge.toString(),
|
|
602
|
+
AZTEC_ENTRY_QUEUE_BOOTSTRAP_VALIDATOR_SET_SIZE: args.entryQueueBootstrapValidatorSetSize.toString(),
|
|
603
|
+
AZTEC_ENTRY_QUEUE_BOOTSTRAP_FLUSH_SIZE: args.entryQueueBootstrapFlushSize.toString(),
|
|
604
|
+
AZTEC_ENTRY_QUEUE_FLUSH_SIZE_MIN: args.entryQueueFlushSizeMin.toString(),
|
|
605
|
+
AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT: args.entryQueueFlushSizeQuotient.toString(),
|
|
606
|
+
AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE: args.entryQueueMaxFlushSize.toString(),
|
|
560
607
|
} as const;
|
|
561
608
|
}
|
|
562
609
|
|
|
@@ -580,21 +627,15 @@ export const deployRollupForUpgrade = async (
|
|
|
580
627
|
// Use foundry-artifacts from l1-artifacts package
|
|
581
628
|
const l1ContractsPath = prepareL1ContractsForDeployment();
|
|
582
629
|
|
|
630
|
+
const forgeBin = resolveFoundryBinary('forge');
|
|
583
631
|
const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
|
|
584
|
-
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
632
|
+
await maybeForgeForceProductionBuild(forgeBin, l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
585
633
|
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
FORGE_SCRIPT,
|
|
589
|
-
'--sig',
|
|
590
|
-
'run()',
|
|
591
|
-
'--private-key',
|
|
592
|
-
privateKey,
|
|
593
|
-
'--rpc-url',
|
|
594
|
-
rpcUrl,
|
|
595
|
-
'--broadcast',
|
|
596
|
-
];
|
|
634
|
+
const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
|
|
635
|
+
const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
|
|
597
636
|
const forgeEnv = {
|
|
637
|
+
// Resolved forge binary picked up by forge_broadcast.js, so it works without forge on PATH.
|
|
638
|
+
FORGE_BIN: forgeBin,
|
|
598
639
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
599
640
|
// Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
|
|
600
641
|
REGISTRY_ADDRESS: registryAddress.toString(),
|
|
@@ -602,7 +643,12 @@ export const deployRollupForUpgrade = async (
|
|
|
602
643
|
...getDeployRollupForUpgradeEnvVars(args),
|
|
603
644
|
};
|
|
604
645
|
|
|
605
|
-
const result = await runProcess<ForgeRollupUpgradeResult>(
|
|
646
|
+
const result = await runProcess<ForgeRollupUpgradeResult>(
|
|
647
|
+
process.execPath,
|
|
648
|
+
[scriptPath, ...forgeArgs],
|
|
649
|
+
forgeEnv,
|
|
650
|
+
l1ContractsPath,
|
|
651
|
+
);
|
|
606
652
|
if (!result) {
|
|
607
653
|
throw new Error('Forge script did not output deployment result');
|
|
608
654
|
}
|
|
@@ -614,6 +660,5 @@ export const deployRollupForUpgrade = async (
|
|
|
614
660
|
|
|
615
661
|
return {
|
|
616
662
|
rollup,
|
|
617
|
-
slashFactoryAddress: result.slashFactoryAddress,
|
|
618
663
|
};
|
|
619
664
|
};
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from './deploy_aztec_l1_contracts.js';
|
|
25
25
|
import { RegisterNewRollupVersionPayloadArtifact } from './l1_artifacts.js';
|
|
26
26
|
import { type L1TxUtilsConfig, getL1TxUtilsConfigEnvVars } from './l1_tx_utils/config.js';
|
|
27
|
-
import {
|
|
27
|
+
import { createL1TxUtils } from './l1_tx_utils/factory.js';
|
|
28
28
|
import type { L1TxUtils } from './l1_tx_utils/l1_tx_utils.js';
|
|
29
29
|
import type { GasPrice, L1TxConfig, L1TxRequest } from './l1_tx_utils/types.js';
|
|
30
30
|
import type { ExtendedViemWalletClient } from './types.js';
|
|
@@ -46,7 +46,7 @@ export class L1Deployer {
|
|
|
46
46
|
private createVerificationJson: boolean = false,
|
|
47
47
|
) {
|
|
48
48
|
this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), { size: 32 }) : undefined;
|
|
49
|
-
this.l1TxUtils =
|
|
49
|
+
this.l1TxUtils = createL1TxUtils(
|
|
50
50
|
this.client,
|
|
51
51
|
{ logger: this.logger, dateProvider },
|
|
52
52
|
{ ...this.txUtilsConfig, debugMaxGasLimit: acceleratedTestDeployments },
|
|
@@ -179,7 +179,7 @@ export async function deployL1Contract(
|
|
|
179
179
|
|
|
180
180
|
if (!l1TxUtils) {
|
|
181
181
|
const config = getL1TxUtilsConfigEnvVars();
|
|
182
|
-
l1TxUtils =
|
|
182
|
+
l1TxUtils = createL1TxUtils(
|
|
183
183
|
extendedClient,
|
|
184
184
|
{ logger },
|
|
185
185
|
{ ...config, debugMaxGasLimit: acceleratedTestDeployments },
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import { accessSync, constants } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
function isExecutable(path: string): boolean {
|
|
7
|
+
try {
|
|
8
|
+
accessSync(path, constants.X_OK);
|
|
9
|
+
return true;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Locate a Foundry binary (`anvil`, `forge`, ...) without relying on the caller's PATH. Order:
|
|
17
|
+
* 1. `$<NAME>_BIN` (e.g. `$ANVIL_BIN`, `$FORGE_BIN`) — explicit override, e.g. for CI with a pinned
|
|
18
|
+
* version. Throws if set but not pointing at an executable, instead of silently falling back.
|
|
19
|
+
* 2. `~/.aztec/current/internal-bin/<name>` — where aztec-up installs it.
|
|
20
|
+
* 3. `~/.aztec/current/bin/aztec-<name>` — the publicly-exposed symlink.
|
|
21
|
+
* 4. `~/.foundry/bin/<name>` — standalone foundryup install.
|
|
22
|
+
* 5. `command -v <name>` — anything else on PATH.
|
|
23
|
+
*
|
|
24
|
+
* Throws with a directive message if none work.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveFoundryBinary(name: string): string {
|
|
27
|
+
const envVar = `${name.toUpperCase()}_BIN`;
|
|
28
|
+
const envBin = process.env[envVar];
|
|
29
|
+
if (envBin) {
|
|
30
|
+
if (!isExecutable(envBin)) {
|
|
31
|
+
throw new Error(`$${envVar} is set to ${envBin}, which does not exist or is not executable.`);
|
|
32
|
+
}
|
|
33
|
+
return envBin;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const candidates = [
|
|
37
|
+
join(homedir(), '.aztec', 'current', 'internal-bin', name),
|
|
38
|
+
join(homedir(), '.aztec', 'current', 'bin', `aztec-${name}`),
|
|
39
|
+
join(homedir(), '.foundry', 'bin', name),
|
|
40
|
+
];
|
|
41
|
+
for (const path of candidates) {
|
|
42
|
+
if (isExecutable(path)) {
|
|
43
|
+
return path;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const which = spawnSync('sh', ['-c', `command -v ${name}`], { encoding: 'utf8' });
|
|
48
|
+
if (which.status === 0 && which.stdout.trim()) {
|
|
49
|
+
return which.stdout.trim();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error(
|
|
53
|
+
`${name} binary not found. Tried $${envVar}, ~/.aztec/current/internal-bin/${name}, ` +
|
|
54
|
+
`~/.aztec/current/bin/aztec-${name}, ~/.foundry/bin/${name}, and $PATH. ` +
|
|
55
|
+
`Install via \`aztec-up\` or set ${envVar} to a working binary.`,
|
|
56
|
+
);
|
|
57
|
+
}
|