@aztec/ethereum 0.0.1-commit.5de5ca79e → 0.0.1-commit.6201a7b05
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 +7 -1
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +14 -0
- 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 +11 -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 +4996 -20
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +257 -55
- 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 +8675 -16072
- 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.js +1 -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.d.ts +13 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +62 -3
- package/dest/test/chain_monitor.d.ts +2 -1
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +5 -0
- 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 +18 -0
- package/src/config.ts +9 -13
- package/src/contracts/chain_state_override.ts +147 -0
- package/src/contracts/inbox.ts +12 -4
- package/src/contracts/index.ts +2 -2
- package/src/contracts/multicall.ts +65 -1
- package/src/contracts/rollup.ts +274 -52
- 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 +1 -1
- package/src/l1_tx_utils/l1_tx_utils.ts +24 -14
- package/src/publisher_manager.ts +105 -10
- package/src/queries.ts +70 -4
- package/src/test/chain_monitor.ts +5 -0
- 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';
|
|
@@ -14,8 +15,10 @@ import {
|
|
|
14
15
|
type Account,
|
|
15
16
|
type GetContractReturnType,
|
|
16
17
|
type Hex,
|
|
18
|
+
type Log,
|
|
17
19
|
type StateOverride,
|
|
18
20
|
type WatchContractEventReturnType,
|
|
21
|
+
encodeAbiParameters,
|
|
19
22
|
encodeFunctionData,
|
|
20
23
|
getContract,
|
|
21
24
|
hexToBigInt,
|
|
@@ -29,11 +32,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
|
|
|
29
32
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
30
33
|
import type { ViemClient } from '../types.js';
|
|
31
34
|
import { formatViemError } from '../utils.js';
|
|
32
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
33
35
|
import { GSEContract } from './gse.js';
|
|
34
36
|
import type { L1EventLog } from './log.js';
|
|
35
37
|
import { SlasherContract } from './slasher_contract.js';
|
|
36
|
-
import {
|
|
38
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
37
39
|
import { checkBlockTag } from './utils.js';
|
|
38
40
|
|
|
39
41
|
export type ViemCommitteeAttestation = {
|
|
@@ -55,7 +57,6 @@ export type L1RollupContractAddresses = Pick<
|
|
|
55
57
|
| 'feeJuiceAddress'
|
|
56
58
|
| 'stakingAssetAddress'
|
|
57
59
|
| 'rewardDistributorAddress'
|
|
58
|
-
| 'slashFactoryAddress'
|
|
59
60
|
| 'gseAddress'
|
|
60
61
|
>;
|
|
61
62
|
|
|
@@ -85,12 +86,6 @@ export type ViemGasFees = {
|
|
|
85
86
|
feePerL2Gas: bigint;
|
|
86
87
|
};
|
|
87
88
|
|
|
88
|
-
export enum SlashingProposerType {
|
|
89
|
-
None = 0,
|
|
90
|
-
Tally = 1,
|
|
91
|
-
Empire = 2,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
89
|
/**
|
|
95
90
|
* Status of a validator/attester in the staking system.
|
|
96
91
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -134,6 +129,17 @@ export type L1FeeData = {
|
|
|
134
129
|
blobFee: bigint;
|
|
135
130
|
};
|
|
136
131
|
|
|
132
|
+
/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */
|
|
133
|
+
export enum TempCheckpointLogField {
|
|
134
|
+
HeaderHash = 0,
|
|
135
|
+
BlobCommitmentsHash = 1,
|
|
136
|
+
OutHash = 2,
|
|
137
|
+
AttestationsHash = 3,
|
|
138
|
+
PayloadDigest = 4,
|
|
139
|
+
SlotNumber = 5,
|
|
140
|
+
FeeHeader = 6,
|
|
141
|
+
}
|
|
142
|
+
|
|
137
143
|
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
138
144
|
export type ManaMinFeeComponents = {
|
|
139
145
|
sequencerCost: bigint;
|
|
@@ -212,6 +218,7 @@ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
|
212
218
|
|
|
213
219
|
export class RollupContract {
|
|
214
220
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
221
|
+
private readonly logger = createLogger('ethereum:rollup');
|
|
215
222
|
|
|
216
223
|
private static cachedStfStorageSlot: Hex | undefined;
|
|
217
224
|
private cachedEscapeHatch?: {
|
|
@@ -267,34 +274,18 @@ export class RollupContract {
|
|
|
267
274
|
return this.rollup;
|
|
268
275
|
}
|
|
269
276
|
|
|
270
|
-
public async getSlashingProposer(): Promise<
|
|
271
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
272
|
-
> {
|
|
277
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
273
278
|
const slasher = await this.getSlasherContract();
|
|
274
279
|
if (!slasher) {
|
|
275
280
|
return undefined;
|
|
276
281
|
}
|
|
277
282
|
|
|
278
283
|
const proposerAddress = await slasher.getProposer();
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
type: 'function',
|
|
282
|
-
name: 'SLASHING_PROPOSER_TYPE',
|
|
283
|
-
inputs: [],
|
|
284
|
-
outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
|
|
285
|
-
stateMutability: 'view',
|
|
286
|
-
},
|
|
287
|
-
] as const;
|
|
288
|
-
|
|
289
|
-
const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
|
|
290
|
-
const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
|
|
291
|
-
if (proposerType === SlashingProposerType.Tally.valueOf()) {
|
|
292
|
-
return new TallySlashingProposerContract(this.client, proposerAddress);
|
|
293
|
-
} else if (proposerType === SlashingProposerType.Empire.valueOf()) {
|
|
294
|
-
return new EmpireSlashingProposerContract(this.client, proposerAddress);
|
|
295
|
-
} else {
|
|
296
|
-
throw new Error(`Unknown slashing proposer type: ${proposerType}`);
|
|
284
|
+
if (proposerAddress.isZero()) {
|
|
285
|
+
return undefined;
|
|
297
286
|
}
|
|
287
|
+
|
|
288
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
298
289
|
}
|
|
299
290
|
|
|
300
291
|
@memoize
|
|
@@ -495,7 +486,11 @@ export class RollupContract {
|
|
|
495
486
|
|
|
496
487
|
const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
|
|
497
488
|
return isOpen;
|
|
498
|
-
} catch {
|
|
489
|
+
} catch (err) {
|
|
490
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
491
|
+
epoch: Number(epoch),
|
|
492
|
+
error: err,
|
|
493
|
+
});
|
|
499
494
|
return false;
|
|
500
495
|
}
|
|
501
496
|
}
|
|
@@ -531,8 +526,9 @@ export class RollupContract {
|
|
|
531
526
|
return this.rollup.read.getCheckpointReward();
|
|
532
527
|
}
|
|
533
528
|
|
|
534
|
-
async getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
535
|
-
|
|
529
|
+
async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
530
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
531
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
|
|
536
532
|
}
|
|
537
533
|
|
|
538
534
|
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
@@ -540,18 +536,31 @@ export class RollupContract {
|
|
|
540
536
|
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
541
537
|
}
|
|
542
538
|
|
|
543
|
-
async getSlotNumber(): Promise<SlotNumber> {
|
|
544
|
-
|
|
539
|
+
async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
|
|
540
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
541
|
+
return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
|
|
545
542
|
}
|
|
546
543
|
|
|
547
|
-
async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
|
|
548
|
-
|
|
544
|
+
async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
|
|
545
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
546
|
+
const result = await this.rollup.read.getL1FeesAt([timestamp], options);
|
|
549
547
|
return {
|
|
550
548
|
baseFee: result.baseFee,
|
|
551
549
|
blobFee: result.blobFee,
|
|
552
550
|
};
|
|
553
551
|
}
|
|
554
552
|
|
|
553
|
+
async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
|
|
554
|
+
const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
|
|
555
|
+
return {
|
|
556
|
+
excessMana: result.excessMana,
|
|
557
|
+
manaUsed: result.manaUsed,
|
|
558
|
+
ethPerFeeAsset: result.ethPerFeeAsset,
|
|
559
|
+
congestionCost: result.congestionCost,
|
|
560
|
+
proverCost: result.proverCost,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
555
564
|
getEthPerFeeAsset(): Promise<bigint> {
|
|
556
565
|
return this.rollup.read.getEthPerFeeAsset();
|
|
557
566
|
}
|
|
@@ -626,8 +635,9 @@ export class RollupContract {
|
|
|
626
635
|
return EthAddress.fromString(result);
|
|
627
636
|
}
|
|
628
637
|
|
|
629
|
-
async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
|
|
630
|
-
|
|
638
|
+
async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
|
|
639
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
640
|
+
const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
|
|
631
641
|
return {
|
|
632
642
|
archive: Fr.fromString(result.archive),
|
|
633
643
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -660,6 +670,30 @@ export class RollupContract {
|
|
|
660
670
|
);
|
|
661
671
|
}
|
|
662
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Returns the effective pending checkpoint, accounting for potential prunes.
|
|
675
|
+
* When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
|
|
676
|
+
* This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
|
|
677
|
+
* @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
|
|
678
|
+
* @param options - Optional L1 block number to pin the queries to.
|
|
679
|
+
*/
|
|
680
|
+
getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
|
|
681
|
+
return retry(
|
|
682
|
+
async () => {
|
|
683
|
+
const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
|
|
684
|
+
const canPrune = await this.canPruneAtTime(timestamp, options);
|
|
685
|
+
if (canPrune) {
|
|
686
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
|
|
687
|
+
return await this.getCheckpoint(provenCheckpointNumber, options);
|
|
688
|
+
}
|
|
689
|
+
const pendingCheckpointNumber = await this.getCheckpointNumber(options);
|
|
690
|
+
return await this.getCheckpoint(pendingCheckpointNumber, options);
|
|
691
|
+
},
|
|
692
|
+
'getting effective pending checkpoint',
|
|
693
|
+
makeBackoff([0.5, 0.5, 0.5]),
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
|
|
663
697
|
async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
|
|
664
698
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
665
699
|
return {
|
|
@@ -781,12 +815,10 @@ export class RollupContract {
|
|
|
781
815
|
public async canProposeAt(
|
|
782
816
|
archive: Buffer,
|
|
783
817
|
account: `0x${string}` | Account,
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
818
|
+
timestamp: bigint,
|
|
819
|
+
stateOverride: StateOverride = [],
|
|
787
820
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
788
|
-
const
|
|
789
|
-
const timeOfNextL1Slot = latestBlock.timestamp + slotDuration + slotOffset;
|
|
821
|
+
const timeOfNextL1Slot = timestamp;
|
|
790
822
|
const who = typeof account === 'string' ? account : account.address;
|
|
791
823
|
|
|
792
824
|
try {
|
|
@@ -798,7 +830,7 @@ export class RollupContract {
|
|
|
798
830
|
functionName: 'canProposeAtTime',
|
|
799
831
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
800
832
|
account,
|
|
801
|
-
stateOverride
|
|
833
|
+
stateOverride,
|
|
802
834
|
});
|
|
803
835
|
|
|
804
836
|
return {
|
|
@@ -834,6 +866,151 @@ export class RollupContract {
|
|
|
834
866
|
];
|
|
835
867
|
}
|
|
836
868
|
|
|
869
|
+
/**
|
|
870
|
+
* Returns a state override that sets tempCheckpointLogs[checkpointNumber].feeHeader to the compressed fee header.
|
|
871
|
+
* Used when simulating a propose call where the parent checkpoint hasn't landed on L1 yet (pipelining).
|
|
872
|
+
*/
|
|
873
|
+
public async makeFeeHeaderOverride(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader): Promise<StateOverride> {
|
|
874
|
+
const { epochDuration, proofSubmissionEpochs } = await this.getRollupConstants();
|
|
875
|
+
const roundaboutSize = BigInt(epochDuration * (proofSubmissionEpochs + 1) + 1);
|
|
876
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
877
|
+
|
|
878
|
+
// tempCheckpointLogs is at offset 2 in RollupStore
|
|
879
|
+
const tempCheckpointLogsMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 2n;
|
|
880
|
+
|
|
881
|
+
// Solidity mapping slot: keccak256(abi.encode(key, baseSlot))
|
|
882
|
+
const structBaseSlot = hexToBigInt(
|
|
883
|
+
keccak256(
|
|
884
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
|
|
885
|
+
),
|
|
886
|
+
);
|
|
887
|
+
|
|
888
|
+
// feeHeader is the 7th field (offset 6) in CompressedTempCheckpointLog
|
|
889
|
+
const feeHeaderSlot = structBaseSlot + 6n;
|
|
890
|
+
const compressed = RollupContract.compressFeeHeader(feeHeader);
|
|
891
|
+
|
|
892
|
+
return [
|
|
893
|
+
{
|
|
894
|
+
address: this.address,
|
|
895
|
+
stateDiff: [
|
|
896
|
+
{
|
|
897
|
+
slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
|
|
898
|
+
value: `0x${compressed.toString(16).padStart(64, '0')}`,
|
|
899
|
+
},
|
|
900
|
+
],
|
|
901
|
+
},
|
|
902
|
+
];
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
907
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
908
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
909
|
+
*/
|
|
910
|
+
public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
|
|
911
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
912
|
+
const archiveSlot = hexToBigInt(
|
|
913
|
+
keccak256(
|
|
914
|
+
encodeAbiParameters(
|
|
915
|
+
[{ type: 'uint256' }, { type: 'uint256' }],
|
|
916
|
+
[BigInt(checkpointNumber), archivesMappingBase],
|
|
917
|
+
),
|
|
918
|
+
),
|
|
919
|
+
);
|
|
920
|
+
return [
|
|
921
|
+
{
|
|
922
|
+
address: this.address,
|
|
923
|
+
stateDiff: [
|
|
924
|
+
{
|
|
925
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
926
|
+
value: archive.toString(),
|
|
927
|
+
},
|
|
928
|
+
],
|
|
929
|
+
},
|
|
930
|
+
];
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
|
|
934
|
+
public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
|
|
935
|
+
type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
|
|
936
|
+
const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
|
|
937
|
+
for (const override of overrides) {
|
|
938
|
+
for (const entry of override) {
|
|
939
|
+
const key = entry.address.toLowerCase();
|
|
940
|
+
const existing = byAddress.get(key);
|
|
941
|
+
if (existing) {
|
|
942
|
+
existing.stateDiff.push(...(entry.stateDiff ?? []));
|
|
943
|
+
if (entry.balance !== undefined) {
|
|
944
|
+
existing.balance = entry.balance;
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
byAddress.set(key, {
|
|
948
|
+
address: entry.address,
|
|
949
|
+
balance: entry.balance,
|
|
950
|
+
stateDiff: [...(entry.stateDiff ?? [])],
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return [...byAddress.values()];
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
|
|
959
|
+
public static compressFeeHeader(feeHeader: FeeHeader): bigint {
|
|
960
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
961
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
962
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
963
|
+
|
|
964
|
+
let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
|
|
965
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
966
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
967
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
968
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
969
|
+
value |= 1n << 255n; // preheat flag
|
|
970
|
+
return value;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
974
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
|
|
975
|
+
public static computeChildFeeHeader(
|
|
976
|
+
parentFeeHeader: FeeHeader,
|
|
977
|
+
childManaUsed: bigint,
|
|
978
|
+
feeAssetPriceModifier: bigint,
|
|
979
|
+
manaTarget: bigint,
|
|
980
|
+
): FeeHeader {
|
|
981
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
982
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
983
|
+
|
|
984
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
985
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
986
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
987
|
+
|
|
988
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
989
|
+
const parentPrice =
|
|
990
|
+
parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
991
|
+
let newPrice: bigint;
|
|
992
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
993
|
+
newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
|
|
994
|
+
} else {
|
|
995
|
+
const absMod = -feeAssetPriceModifier;
|
|
996
|
+
newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
|
|
997
|
+
}
|
|
998
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
999
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
1000
|
+
}
|
|
1001
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
1002
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return {
|
|
1006
|
+
excessMana,
|
|
1007
|
+
manaUsed: childManaUsed,
|
|
1008
|
+
ethPerFeeAsset: newPrice,
|
|
1009
|
+
congestionCost: 0n,
|
|
1010
|
+
proverCost: 0n,
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
|
|
837
1014
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
838
1015
|
public buildInvalidateBadAttestationRequest(
|
|
839
1016
|
checkpointNumber: CheckpointNumber,
|
|
@@ -882,8 +1059,8 @@ export class RollupContract {
|
|
|
882
1059
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
883
1060
|
}
|
|
884
1061
|
|
|
885
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
886
|
-
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
1062
|
+
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
|
|
1063
|
+
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
|
|
887
1064
|
}
|
|
888
1065
|
|
|
889
1066
|
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
@@ -939,11 +1116,10 @@ export class RollupContract {
|
|
|
939
1116
|
return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
|
|
940
1117
|
}
|
|
941
1118
|
|
|
942
|
-
async getAttesters(): Promise<EthAddress[]> {
|
|
1119
|
+
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
943
1120
|
const attesterSize = await this.getActiveAttesterCount();
|
|
944
1121
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
945
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
946
|
-
|
|
1122
|
+
const ts = timestamp ?? (await this.client.getBlock()).timestamp;
|
|
947
1123
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
948
1124
|
const chunks = chunk(indices, 1000);
|
|
949
1125
|
|
|
@@ -1049,7 +1225,7 @@ export class RollupContract {
|
|
|
1049
1225
|
}
|
|
1050
1226
|
|
|
1051
1227
|
public listenToCheckpointInvalidated(
|
|
1052
|
-
callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
|
|
1228
|
+
callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
|
|
1053
1229
|
): WatchContractEventReturnType {
|
|
1054
1230
|
return this.rollup.watchEvent.CheckpointInvalidated(
|
|
1055
1231
|
{},
|
|
@@ -1058,7 +1234,7 @@ export class RollupContract {
|
|
|
1058
1234
|
for (const log of logs) {
|
|
1059
1235
|
const args = log.args;
|
|
1060
1236
|
if (args.checkpointNumber !== undefined) {
|
|
1061
|
-
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
|
|
1237
|
+
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
|
|
1062
1238
|
}
|
|
1063
1239
|
}
|
|
1064
1240
|
},
|
|
@@ -1091,6 +1267,17 @@ export class RollupContract {
|
|
|
1091
1267
|
);
|
|
1092
1268
|
}
|
|
1093
1269
|
|
|
1270
|
+
/**
|
|
1271
|
+
* Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
|
|
1272
|
+
* The Rollup inherits from Ownable and emits this event in its constructor, so the event
|
|
1273
|
+
* is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
|
|
1274
|
+
* probe to detect RPC nodes that prune historical logs.
|
|
1275
|
+
*/
|
|
1276
|
+
async getOwnershipTransferredEventsAtDeploy() {
|
|
1277
|
+
const l1StartBlock = await this.getL1StartBlock();
|
|
1278
|
+
return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1094
1281
|
/** Fetches CheckpointProposed events within the given block range. */
|
|
1095
1282
|
async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
|
|
1096
1283
|
const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
|
|
@@ -1123,4 +1310,39 @@ export class RollupContract {
|
|
|
1123
1310
|
},
|
|
1124
1311
|
}));
|
|
1125
1312
|
}
|
|
1313
|
+
|
|
1314
|
+
/** Packs pending and proven checkpoint numbers into the chain tips storage format. */
|
|
1315
|
+
static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
|
|
1316
|
+
return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/** Storage slot for the chain tips (offset 0 within the STF storage struct). */
|
|
1320
|
+
static get chainTipsStorageSlot(): bigint {
|
|
1321
|
+
return BigInt(RollupContract.stfStorageSlot);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Computes the storage slot for a field within a tempCheckpointLog entry.
|
|
1326
|
+
* @param checkpointNumber - The checkpoint number
|
|
1327
|
+
* @param field - The field within the CompressedTempCheckpointLog struct
|
|
1328
|
+
*/
|
|
1329
|
+
async getTempCheckpointLogStorageSlot(
|
|
1330
|
+
checkpointNumber: CheckpointNumber,
|
|
1331
|
+
field: TempCheckpointLogField,
|
|
1332
|
+
): Promise<bigint> {
|
|
1333
|
+
const fieldOffset = BigInt(field);
|
|
1334
|
+
const [epochDuration, proofSubmissionEpochs] = await Promise.all([
|
|
1335
|
+
this.getEpochDuration(),
|
|
1336
|
+
this.getProofSubmissionEpochs(),
|
|
1337
|
+
]);
|
|
1338
|
+
const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
|
|
1339
|
+
const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
|
|
1340
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
1341
|
+
const entryBase = BigInt(
|
|
1342
|
+
keccak256(
|
|
1343
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
|
|
1344
|
+
),
|
|
1345
|
+
);
|
|
1346
|
+
return entryBase + fieldOffset;
|
|
1347
|
+
}
|
|
1126
1348
|
}
|
|
@@ -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,
|