@aztec/ethereum 0.0.1-commit.10bd49492 → 0.0.1-commit.11bf3dd6e
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 +10 -2
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +13 -7
- package/dest/config.d.ts +6 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +5 -9
- package/dest/contracts/chain_state_override.d.ts +38 -0
- package/dest/contracts/chain_state_override.d.ts.map +1 -0
- package/dest/contracts/chain_state_override.js +100 -0
- package/dest/contracts/inbox.d.ts +3 -3
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +5 -6
- package/dest/contracts/index.d.ts +3 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +2 -2
- package/dest/contracts/multicall.d.ts +51 -2
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +85 -0
- package/dest/contracts/rollup.d.ts +79 -19
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +280 -53
- package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +13 -15
- package/dest/deploy_aztec_l1_contracts.d.ts +3 -6
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +2 -4
- package/dest/generated/l1-contracts-defaults.d.ts +1 -1
- package/dest/generated/l1-contracts-defaults.js +1 -1
- package/dest/l1_artifacts.d.ts +8644 -15983
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_artifacts.js +9 -24
- package/dest/l1_contract_addresses.d.ts +1 -5
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +0 -6
- package/dest/l1_reader.d.ts +3 -1
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +6 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +3 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +34 -26
- package/dest/publisher_manager.d.ts +21 -7
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +81 -7
- package/dest/queries.js +3 -3
- package/dest/test/chain_monitor.d.ts +22 -3
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +33 -2
- package/dest/test/eth_cheat_codes.d.ts +6 -4
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +6 -4
- package/dest/test/rollup_cheat_codes.d.ts +5 -1
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +9 -1
- package/dest/test/start_anvil.d.ts +8 -1
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +16 -2
- package/package.json +5 -5
- package/src/client.ts +10 -2
- package/src/config.ts +9 -13
- package/src/contracts/chain_state_override.ts +147 -0
- package/src/contracts/inbox.ts +4 -4
- package/src/contracts/index.ts +2 -2
- package/src/contracts/multicall.ts +65 -1
- package/src/contracts/rollup.ts +293 -50
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +14 -16
- package/src/deploy_aztec_l1_contracts.ts +1 -5
- package/src/generated/l1-contracts-defaults.ts +1 -1
- package/src/l1_artifacts.ts +12 -35
- package/src/l1_contract_addresses.ts +0 -7
- package/src/l1_reader.ts +13 -1
- package/src/l1_tx_utils/l1_tx_utils.ts +24 -14
- package/src/publisher_manager.ts +105 -10
- package/src/queries.ts +3 -3
- package/src/test/chain_monitor.ts +60 -3
- package/src/test/eth_cheat_codes.ts +6 -4
- package/src/test/rollup_cheat_codes.ts +11 -1
- package/src/test/start_anvil.ts +25 -2
- package/dest/contracts/empire_slashing_proposer.d.ts +0 -69
- package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/empire_slashing_proposer.js +0 -216
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- package/src/contracts/empire_slashing_proposer.ts +0 -265
package/src/contracts/rollup.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
4
4
|
import { memoize } from '@aztec/foundation/decorators';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import type { ViemSignature } from '@aztec/foundation/eth-signature';
|
|
7
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
7
8
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
8
9
|
import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
|
|
9
10
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
type Hex,
|
|
17
18
|
type StateOverride,
|
|
18
19
|
type WatchContractEventReturnType,
|
|
20
|
+
encodeAbiParameters,
|
|
19
21
|
encodeFunctionData,
|
|
20
22
|
getContract,
|
|
21
23
|
hexToBigInt,
|
|
@@ -29,11 +31,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
|
|
|
29
31
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
30
32
|
import type { ViemClient } from '../types.js';
|
|
31
33
|
import { formatViemError } from '../utils.js';
|
|
32
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
33
34
|
import { GSEContract } from './gse.js';
|
|
34
35
|
import type { L1EventLog } from './log.js';
|
|
35
36
|
import { SlasherContract } from './slasher_contract.js';
|
|
36
|
-
import {
|
|
37
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
37
38
|
import { checkBlockTag } from './utils.js';
|
|
38
39
|
|
|
39
40
|
export type ViemCommitteeAttestation = {
|
|
@@ -55,7 +56,6 @@ export type L1RollupContractAddresses = Pick<
|
|
|
55
56
|
| 'feeJuiceAddress'
|
|
56
57
|
| 'stakingAssetAddress'
|
|
57
58
|
| 'rewardDistributorAddress'
|
|
58
|
-
| 'slashFactoryAddress'
|
|
59
59
|
| 'gseAddress'
|
|
60
60
|
>;
|
|
61
61
|
|
|
@@ -85,12 +85,6 @@ export type ViemGasFees = {
|
|
|
85
85
|
feePerL2Gas: bigint;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
export enum SlashingProposerType {
|
|
89
|
-
None = 0,
|
|
90
|
-
Tally = 1,
|
|
91
|
-
Empire = 2,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
88
|
/**
|
|
95
89
|
* Status of a validator/attester in the staking system.
|
|
96
90
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -134,6 +128,25 @@ export type L1FeeData = {
|
|
|
134
128
|
blobFee: bigint;
|
|
135
129
|
};
|
|
136
130
|
|
|
131
|
+
/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */
|
|
132
|
+
export enum TempCheckpointLogField {
|
|
133
|
+
HeaderHash = 0,
|
|
134
|
+
BlobCommitmentsHash = 1,
|
|
135
|
+
OutHash = 2,
|
|
136
|
+
AttestationsHash = 3,
|
|
137
|
+
PayloadDigest = 4,
|
|
138
|
+
SlotNumber = 5,
|
|
139
|
+
FeeHeader = 6,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
143
|
+
export type ManaMinFeeComponents = {
|
|
144
|
+
sequencerCost: bigint;
|
|
145
|
+
proverCost: bigint;
|
|
146
|
+
congestionCost: bigint;
|
|
147
|
+
congestionMultiplier: bigint;
|
|
148
|
+
};
|
|
149
|
+
|
|
137
150
|
/**
|
|
138
151
|
* Reward configuration for the rollup
|
|
139
152
|
*/
|
|
@@ -204,6 +217,7 @@ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
|
204
217
|
|
|
205
218
|
export class RollupContract {
|
|
206
219
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
220
|
+
private readonly logger = createLogger('ethereum:rollup');
|
|
207
221
|
|
|
208
222
|
private static cachedStfStorageSlot: Hex | undefined;
|
|
209
223
|
private cachedEscapeHatch?: {
|
|
@@ -259,34 +273,18 @@ export class RollupContract {
|
|
|
259
273
|
return this.rollup;
|
|
260
274
|
}
|
|
261
275
|
|
|
262
|
-
public async getSlashingProposer(): Promise<
|
|
263
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
264
|
-
> {
|
|
276
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
265
277
|
const slasher = await this.getSlasherContract();
|
|
266
278
|
if (!slasher) {
|
|
267
279
|
return undefined;
|
|
268
280
|
}
|
|
269
281
|
|
|
270
282
|
const proposerAddress = await slasher.getProposer();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
type: 'function',
|
|
274
|
-
name: 'SLASHING_PROPOSER_TYPE',
|
|
275
|
-
inputs: [],
|
|
276
|
-
outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
|
|
277
|
-
stateMutability: 'view',
|
|
278
|
-
},
|
|
279
|
-
] as const;
|
|
280
|
-
|
|
281
|
-
const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
|
|
282
|
-
const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
|
|
283
|
-
if (proposerType === SlashingProposerType.Tally.valueOf()) {
|
|
284
|
-
return new TallySlashingProposerContract(this.client, proposerAddress);
|
|
285
|
-
} else if (proposerType === SlashingProposerType.Empire.valueOf()) {
|
|
286
|
-
return new EmpireSlashingProposerContract(this.client, proposerAddress);
|
|
287
|
-
} else {
|
|
288
|
-
throw new Error(`Unknown slashing proposer type: ${proposerType}`);
|
|
283
|
+
if (proposerAddress.isZero()) {
|
|
284
|
+
return undefined;
|
|
289
285
|
}
|
|
286
|
+
|
|
287
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
290
288
|
}
|
|
291
289
|
|
|
292
290
|
@memoize
|
|
@@ -379,6 +377,20 @@ export class RollupContract {
|
|
|
379
377
|
return Fr.fromString(await this.rollup.read.archiveAt([0n]));
|
|
380
378
|
}
|
|
381
379
|
|
|
380
|
+
@memoize
|
|
381
|
+
async getVkTreeRoot(): Promise<Fr> {
|
|
382
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
|
|
383
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
384
|
+
return Fr.fromString(value ?? '0x0');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
@memoize
|
|
388
|
+
async getProtocolContractsHash(): Promise<Fr> {
|
|
389
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
|
|
390
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
391
|
+
return Fr.fromString(value ?? '0x0');
|
|
392
|
+
}
|
|
393
|
+
|
|
382
394
|
/**
|
|
383
395
|
* Returns rollup constants used for epoch queries.
|
|
384
396
|
* Return type is `L1RollupConstants` which is defined in stdlib,
|
|
@@ -473,7 +485,11 @@ export class RollupContract {
|
|
|
473
485
|
|
|
474
486
|
const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
|
|
475
487
|
return isOpen;
|
|
476
|
-
} catch {
|
|
488
|
+
} catch (err) {
|
|
489
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
490
|
+
epoch: Number(epoch),
|
|
491
|
+
error: err,
|
|
492
|
+
});
|
|
477
493
|
return false;
|
|
478
494
|
}
|
|
479
495
|
}
|
|
@@ -509,8 +525,9 @@ export class RollupContract {
|
|
|
509
525
|
return this.rollup.read.getCheckpointReward();
|
|
510
526
|
}
|
|
511
527
|
|
|
512
|
-
async getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
513
|
-
|
|
528
|
+
async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
529
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
530
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
|
|
514
531
|
}
|
|
515
532
|
|
|
516
533
|
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
@@ -518,18 +535,31 @@ export class RollupContract {
|
|
|
518
535
|
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
519
536
|
}
|
|
520
537
|
|
|
521
|
-
async getSlotNumber(): Promise<SlotNumber> {
|
|
522
|
-
|
|
538
|
+
async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
|
|
539
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
540
|
+
return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
|
|
523
541
|
}
|
|
524
542
|
|
|
525
|
-
async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
|
|
526
|
-
|
|
543
|
+
async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
|
|
544
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
545
|
+
const result = await this.rollup.read.getL1FeesAt([timestamp], options);
|
|
527
546
|
return {
|
|
528
547
|
baseFee: result.baseFee,
|
|
529
548
|
blobFee: result.blobFee,
|
|
530
549
|
};
|
|
531
550
|
}
|
|
532
551
|
|
|
552
|
+
async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
|
|
553
|
+
const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
|
|
554
|
+
return {
|
|
555
|
+
excessMana: result.excessMana,
|
|
556
|
+
manaUsed: result.manaUsed,
|
|
557
|
+
ethPerFeeAsset: result.ethPerFeeAsset,
|
|
558
|
+
congestionCost: result.congestionCost,
|
|
559
|
+
proverCost: result.proverCost,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
533
563
|
getEthPerFeeAsset(): Promise<bigint> {
|
|
534
564
|
return this.rollup.read.getEthPerFeeAsset();
|
|
535
565
|
}
|
|
@@ -604,8 +634,9 @@ export class RollupContract {
|
|
|
604
634
|
return EthAddress.fromString(result);
|
|
605
635
|
}
|
|
606
636
|
|
|
607
|
-
async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
|
|
608
|
-
|
|
637
|
+
async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
|
|
638
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
639
|
+
const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
|
|
609
640
|
return {
|
|
610
641
|
archive: Fr.fromString(result.archive),
|
|
611
642
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -638,6 +669,30 @@ export class RollupContract {
|
|
|
638
669
|
);
|
|
639
670
|
}
|
|
640
671
|
|
|
672
|
+
/**
|
|
673
|
+
* Returns the effective pending checkpoint, accounting for potential prunes.
|
|
674
|
+
* When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
|
|
675
|
+
* This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
|
|
676
|
+
* @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
|
|
677
|
+
* @param options - Optional L1 block number to pin the queries to.
|
|
678
|
+
*/
|
|
679
|
+
getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
|
|
680
|
+
return retry(
|
|
681
|
+
async () => {
|
|
682
|
+
const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
|
|
683
|
+
const canPrune = await this.canPruneAtTime(timestamp, options);
|
|
684
|
+
if (canPrune) {
|
|
685
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
|
|
686
|
+
return await this.getCheckpoint(provenCheckpointNumber, options);
|
|
687
|
+
}
|
|
688
|
+
const pendingCheckpointNumber = await this.getCheckpointNumber(options);
|
|
689
|
+
return await this.getCheckpoint(pendingCheckpointNumber, options);
|
|
690
|
+
},
|
|
691
|
+
'getting effective pending checkpoint',
|
|
692
|
+
makeBackoff([0.5, 0.5, 0.5]),
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
641
696
|
async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
|
|
642
697
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
643
698
|
return {
|
|
@@ -756,14 +811,13 @@ export class RollupContract {
|
|
|
756
811
|
* timestamp of the next L1 block
|
|
757
812
|
* @throws otherwise
|
|
758
813
|
*/
|
|
759
|
-
public async
|
|
814
|
+
public async canProposeAt(
|
|
760
815
|
archive: Buffer,
|
|
761
816
|
account: `0x${string}` | Account,
|
|
762
|
-
|
|
763
|
-
|
|
817
|
+
timestamp: bigint,
|
|
818
|
+
stateOverride: StateOverride = [],
|
|
764
819
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
765
|
-
const
|
|
766
|
-
const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
|
|
820
|
+
const timeOfNextL1Slot = timestamp;
|
|
767
821
|
const who = typeof account === 'string' ? account : account.address;
|
|
768
822
|
|
|
769
823
|
try {
|
|
@@ -775,7 +829,7 @@ export class RollupContract {
|
|
|
775
829
|
functionName: 'canProposeAtTime',
|
|
776
830
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
777
831
|
account,
|
|
778
|
-
stateOverride
|
|
832
|
+
stateOverride,
|
|
779
833
|
});
|
|
780
834
|
|
|
781
835
|
return {
|
|
@@ -811,6 +865,151 @@ export class RollupContract {
|
|
|
811
865
|
];
|
|
812
866
|
}
|
|
813
867
|
|
|
868
|
+
/**
|
|
869
|
+
* Returns a state override that sets tempCheckpointLogs[checkpointNumber].feeHeader to the compressed fee header.
|
|
870
|
+
* Used when simulating a propose call where the parent checkpoint hasn't landed on L1 yet (pipelining).
|
|
871
|
+
*/
|
|
872
|
+
public async makeFeeHeaderOverride(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader): Promise<StateOverride> {
|
|
873
|
+
const { epochDuration, proofSubmissionEpochs } = await this.getRollupConstants();
|
|
874
|
+
const roundaboutSize = BigInt(epochDuration * (proofSubmissionEpochs + 1) + 1);
|
|
875
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
876
|
+
|
|
877
|
+
// tempCheckpointLogs is at offset 2 in RollupStore
|
|
878
|
+
const tempCheckpointLogsMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 2n;
|
|
879
|
+
|
|
880
|
+
// Solidity mapping slot: keccak256(abi.encode(key, baseSlot))
|
|
881
|
+
const structBaseSlot = hexToBigInt(
|
|
882
|
+
keccak256(
|
|
883
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
|
|
884
|
+
),
|
|
885
|
+
);
|
|
886
|
+
|
|
887
|
+
// feeHeader is the 7th field (offset 6) in CompressedTempCheckpointLog
|
|
888
|
+
const feeHeaderSlot = structBaseSlot + 6n;
|
|
889
|
+
const compressed = RollupContract.compressFeeHeader(feeHeader);
|
|
890
|
+
|
|
891
|
+
return [
|
|
892
|
+
{
|
|
893
|
+
address: this.address,
|
|
894
|
+
stateDiff: [
|
|
895
|
+
{
|
|
896
|
+
slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
|
|
897
|
+
value: `0x${compressed.toString(16).padStart(64, '0')}`,
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
},
|
|
901
|
+
];
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
906
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
907
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
908
|
+
*/
|
|
909
|
+
public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
|
|
910
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
911
|
+
const archiveSlot = hexToBigInt(
|
|
912
|
+
keccak256(
|
|
913
|
+
encodeAbiParameters(
|
|
914
|
+
[{ type: 'uint256' }, { type: 'uint256' }],
|
|
915
|
+
[BigInt(checkpointNumber), archivesMappingBase],
|
|
916
|
+
),
|
|
917
|
+
),
|
|
918
|
+
);
|
|
919
|
+
return [
|
|
920
|
+
{
|
|
921
|
+
address: this.address,
|
|
922
|
+
stateDiff: [
|
|
923
|
+
{
|
|
924
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
925
|
+
value: archive.toString(),
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
},
|
|
929
|
+
];
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
|
|
933
|
+
public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
|
|
934
|
+
type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
|
|
935
|
+
const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
|
|
936
|
+
for (const override of overrides) {
|
|
937
|
+
for (const entry of override) {
|
|
938
|
+
const key = entry.address.toLowerCase();
|
|
939
|
+
const existing = byAddress.get(key);
|
|
940
|
+
if (existing) {
|
|
941
|
+
existing.stateDiff.push(...(entry.stateDiff ?? []));
|
|
942
|
+
if (entry.balance !== undefined) {
|
|
943
|
+
existing.balance = entry.balance;
|
|
944
|
+
}
|
|
945
|
+
} else {
|
|
946
|
+
byAddress.set(key, {
|
|
947
|
+
address: entry.address,
|
|
948
|
+
balance: entry.balance,
|
|
949
|
+
stateDiff: [...(entry.stateDiff ?? [])],
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return [...byAddress.values()];
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
|
|
958
|
+
public static compressFeeHeader(feeHeader: FeeHeader): bigint {
|
|
959
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
960
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
961
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
962
|
+
|
|
963
|
+
let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
|
|
964
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
965
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
966
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
967
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
968
|
+
value |= 1n << 255n; // preheat flag
|
|
969
|
+
return value;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
973
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
|
|
974
|
+
public static computeChildFeeHeader(
|
|
975
|
+
parentFeeHeader: FeeHeader,
|
|
976
|
+
childManaUsed: bigint,
|
|
977
|
+
feeAssetPriceModifier: bigint,
|
|
978
|
+
manaTarget: bigint,
|
|
979
|
+
): FeeHeader {
|
|
980
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
981
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
982
|
+
|
|
983
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
984
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
985
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
986
|
+
|
|
987
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
988
|
+
const parentPrice =
|
|
989
|
+
parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
990
|
+
let newPrice: bigint;
|
|
991
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
992
|
+
newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
|
|
993
|
+
} else {
|
|
994
|
+
const absMod = -feeAssetPriceModifier;
|
|
995
|
+
newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
|
|
996
|
+
}
|
|
997
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
998
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
999
|
+
}
|
|
1000
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
1001
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return {
|
|
1005
|
+
excessMana,
|
|
1006
|
+
manaUsed: childManaUsed,
|
|
1007
|
+
ethPerFeeAsset: newPrice,
|
|
1008
|
+
congestionCost: 0n,
|
|
1009
|
+
proverCost: 0n,
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
814
1013
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
815
1014
|
public buildInvalidateBadAttestationRequest(
|
|
816
1015
|
checkpointNumber: CheckpointNumber,
|
|
@@ -859,8 +1058,18 @@ export class RollupContract {
|
|
|
859
1058
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
860
1059
|
}
|
|
861
1060
|
|
|
862
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
863
|
-
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
1061
|
+
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
|
|
1062
|
+
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
1066
|
+
const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
|
|
1067
|
+
return {
|
|
1068
|
+
sequencerCost: result.sequencerCost,
|
|
1069
|
+
proverCost: result.proverCost,
|
|
1070
|
+
congestionCost: result.congestionCost,
|
|
1071
|
+
congestionMultiplier: result.congestionMultiplier,
|
|
1072
|
+
};
|
|
864
1073
|
}
|
|
865
1074
|
|
|
866
1075
|
async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
|
|
@@ -906,11 +1115,10 @@ export class RollupContract {
|
|
|
906
1115
|
return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
|
|
907
1116
|
}
|
|
908
1117
|
|
|
909
|
-
async getAttesters(): Promise<EthAddress[]> {
|
|
1118
|
+
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
910
1119
|
const attesterSize = await this.getActiveAttesterCount();
|
|
911
1120
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
912
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
913
|
-
|
|
1121
|
+
const ts = timestamp ?? (await this.client.getBlock()).timestamp;
|
|
914
1122
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
915
1123
|
const chunks = chunk(indices, 1000);
|
|
916
1124
|
|
|
@@ -1090,4 +1298,39 @@ export class RollupContract {
|
|
|
1090
1298
|
},
|
|
1091
1299
|
}));
|
|
1092
1300
|
}
|
|
1301
|
+
|
|
1302
|
+
/** Packs pending and proven checkpoint numbers into the chain tips storage format. */
|
|
1303
|
+
static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
|
|
1304
|
+
return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/** Storage slot for the chain tips (offset 0 within the STF storage struct). */
|
|
1308
|
+
static get chainTipsStorageSlot(): bigint {
|
|
1309
|
+
return BigInt(RollupContract.stfStorageSlot);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
/**
|
|
1313
|
+
* Computes the storage slot for a field within a tempCheckpointLog entry.
|
|
1314
|
+
* @param checkpointNumber - The checkpoint number
|
|
1315
|
+
* @param field - The field within the CompressedTempCheckpointLog struct
|
|
1316
|
+
*/
|
|
1317
|
+
async getTempCheckpointLogStorageSlot(
|
|
1318
|
+
checkpointNumber: CheckpointNumber,
|
|
1319
|
+
field: TempCheckpointLogField,
|
|
1320
|
+
): Promise<bigint> {
|
|
1321
|
+
const fieldOffset = BigInt(field);
|
|
1322
|
+
const [epochDuration, proofSubmissionEpochs] = await Promise.all([
|
|
1323
|
+
this.getEpochDuration(),
|
|
1324
|
+
this.getProofSubmissionEpochs(),
|
|
1325
|
+
]);
|
|
1326
|
+
const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
|
|
1327
|
+
const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
|
|
1328
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
1329
|
+
const entryBase = BigInt(
|
|
1330
|
+
keccak256(
|
|
1331
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
|
|
1332
|
+
),
|
|
1333
|
+
);
|
|
1334
|
+
return entryBase + fieldOffset;
|
|
1335
|
+
}
|
|
1093
1336
|
}
|
|
@@ -7,7 +7,7 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
7
7
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
8
8
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
9
9
|
import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
|
|
10
|
-
import {
|
|
10
|
+
import { SlashingProposerAbi } from '@aztec/l1-artifacts/SlashingProposerAbi';
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
type GetContractReturnType,
|
|
@@ -19,13 +19,11 @@ import {
|
|
|
19
19
|
} from 'viem';
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Wrapper around the
|
|
22
|
+
* Wrapper around the SlashingProposer contract that provides
|
|
23
23
|
* a TypeScript interface for interacting with the consensus-based slashing system.
|
|
24
24
|
*/
|
|
25
|
-
export class
|
|
26
|
-
private readonly contract: GetContractReturnType<typeof
|
|
27
|
-
|
|
28
|
-
public readonly type = 'tally' as const;
|
|
25
|
+
export class SlashingProposerContract {
|
|
26
|
+
private readonly contract: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
|
|
29
27
|
|
|
30
28
|
constructor(
|
|
31
29
|
public readonly client: ViemClient,
|
|
@@ -33,7 +31,7 @@ export class TallySlashingProposerContract {
|
|
|
33
31
|
) {
|
|
34
32
|
this.contract = getContract({
|
|
35
33
|
address: typeof address === 'string' ? address : address.toString(),
|
|
36
|
-
abi:
|
|
34
|
+
abi: SlashingProposerAbi,
|
|
37
35
|
client,
|
|
38
36
|
});
|
|
39
37
|
}
|
|
@@ -136,12 +134,12 @@ export class TallySlashingProposerContract {
|
|
|
136
134
|
|
|
137
135
|
/** Tries to extract a VoteCast event from the given logs. */
|
|
138
136
|
public tryExtractVoteCastEvent(logs: Log[]) {
|
|
139
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
137
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'VoteCast');
|
|
140
138
|
}
|
|
141
139
|
|
|
142
140
|
/** Tries to extract a RoundExecuted event from the given logs. */
|
|
143
141
|
public tryExtractRoundExecutedEvent(logs: Log[]) {
|
|
144
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
142
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'RoundExecuted');
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
/**
|
|
@@ -161,9 +159,9 @@ export class TallySlashingProposerContract {
|
|
|
161
159
|
|
|
162
160
|
return {
|
|
163
161
|
to: this.contract.address,
|
|
164
|
-
abi:
|
|
162
|
+
abi: SlashingProposerAbi,
|
|
165
163
|
data: encodeFunctionData({
|
|
166
|
-
abi:
|
|
164
|
+
abi: SlashingProposerAbi,
|
|
167
165
|
functionName: 'vote',
|
|
168
166
|
args: [votes, signature.toViemSignature()],
|
|
169
167
|
}),
|
|
@@ -173,7 +171,7 @@ export class TallySlashingProposerContract {
|
|
|
173
171
|
/** Returns the typed data definition to EIP712-sign for voting */
|
|
174
172
|
public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
|
|
175
173
|
const domain = {
|
|
176
|
-
name: '
|
|
174
|
+
name: 'SlashingProposer',
|
|
177
175
|
version: '1',
|
|
178
176
|
chainId: this.client.chain.id,
|
|
179
177
|
verifyingContract: this.contract.address,
|
|
@@ -209,9 +207,9 @@ export class TallySlashingProposerContract {
|
|
|
209
207
|
public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
|
|
210
208
|
return {
|
|
211
209
|
to: this.contract.address,
|
|
212
|
-
abi:
|
|
210
|
+
abi: SlashingProposerAbi,
|
|
213
211
|
data: encodeFunctionData({
|
|
214
|
-
abi:
|
|
212
|
+
abi: SlashingProposerAbi,
|
|
215
213
|
functionName: 'vote',
|
|
216
214
|
args: [votes, signature],
|
|
217
215
|
}),
|
|
@@ -227,9 +225,9 @@ export class TallySlashingProposerContract {
|
|
|
227
225
|
public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
|
|
228
226
|
return {
|
|
229
227
|
to: this.contract.address,
|
|
230
|
-
abi: mergeAbis([
|
|
228
|
+
abi: mergeAbis([SlashingProposerAbi, SlasherAbi]),
|
|
231
229
|
data: encodeFunctionData({
|
|
232
|
-
abi:
|
|
230
|
+
abi: SlashingProposerAbi,
|
|
233
231
|
functionName: 'executeRound',
|
|
234
232
|
args: [round, committees.map(c => c.map(addr => addr.toString()))],
|
|
235
233
|
}),
|
|
@@ -230,7 +230,6 @@ export function computeValidatorData(operator: Operator): ValidatorJson {
|
|
|
230
230
|
export interface RollupUpgradeAddresses {
|
|
231
231
|
rollupAddress: string;
|
|
232
232
|
verifierAddress: string;
|
|
233
|
-
slashFactoryAddress: string;
|
|
234
233
|
inboxAddress: string;
|
|
235
234
|
outboxAddress: string;
|
|
236
235
|
feeJuicePortalAddress: string;
|
|
@@ -243,7 +242,6 @@ export interface RollupUpgradeAddresses {
|
|
|
243
242
|
export interface ForgeRollupUpgradeResult {
|
|
244
243
|
rollupAddress: Hex;
|
|
245
244
|
verifierAddress: Hex;
|
|
246
|
-
slashFactoryAddress: Hex;
|
|
247
245
|
inboxAddress: Hex;
|
|
248
246
|
outboxAddress: Hex;
|
|
249
247
|
feeJuicePortalAddress: Hex;
|
|
@@ -403,7 +401,6 @@ export async function deployAztecL1Contracts(
|
|
|
403
401
|
governanceProposerAddress: EthAddress.fromString(result.governanceProposerAddress),
|
|
404
402
|
governanceAddress: EthAddress.fromString(result.governanceAddress),
|
|
405
403
|
stakingAssetAddress: EthAddress.fromString(result.stakingAssetAddress),
|
|
406
|
-
slashFactoryAddress: result.slashFactoryAddress ? EthAddress.fromString(result.slashFactoryAddress) : undefined,
|
|
407
404
|
feeAssetHandlerAddress: result.feeAssetHandlerAddress
|
|
408
405
|
? EthAddress.fromString(result.feeAssetHandlerAddress)
|
|
409
406
|
: undefined,
|
|
@@ -585,7 +582,7 @@ export function getDeployRollupForUpgradeEnvVars(
|
|
|
585
582
|
AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
|
|
586
583
|
AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
|
|
587
584
|
AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
|
|
588
|
-
|
|
585
|
+
AZTEC_SLASHER_ENABLED: args.slasherEnabled ? 'true' : 'false',
|
|
589
586
|
AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
|
|
590
587
|
AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
|
|
591
588
|
AZTEC_SLASHING_OFFSET_IN_ROUNDS: args.slashingOffsetInRounds.toString(),
|
|
@@ -645,6 +642,5 @@ export const deployRollupForUpgrade = async (
|
|
|
645
642
|
|
|
646
643
|
return {
|
|
647
644
|
rollup,
|
|
648
|
-
slashFactoryAddress: result.slashFactoryAddress,
|
|
649
645
|
};
|
|
650
646
|
};
|
|
@@ -18,7 +18,7 @@ export const l1ContractsDefaultEnv = {
|
|
|
18
18
|
AZTEC_MANA_TARGET: 100000000,
|
|
19
19
|
AZTEC_PROVING_COST_PER_MANA: 100,
|
|
20
20
|
AZTEC_INITIAL_ETH_PER_FEE_ASSET: 10000000,
|
|
21
|
-
|
|
21
|
+
AZTEC_SLASHER_ENABLED: true,
|
|
22
22
|
AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: 4,
|
|
23
23
|
AZTEC_SLASHING_LIFETIME_IN_ROUNDS: 5,
|
|
24
24
|
AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS: 0,
|