@aztec/ethereum 0.0.1-commit.936cb2cae → 0.0.1-commit.949a33fd8
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/rollup.d.ts +4994 -19
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +246 -49
- 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.js +1 -1
- 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/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/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/rollup.ts +263 -44
- 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 +1 -1
- package/src/queries.ts +70 -4
- package/src/test/chain_monitor.ts +5 -0
- package/src/test/rollup_cheat_codes.ts +11 -1
- 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
|
@@ -15,8 +15,10 @@ import {
|
|
|
15
15
|
type Account,
|
|
16
16
|
type GetContractReturnType,
|
|
17
17
|
type Hex,
|
|
18
|
+
type Log,
|
|
18
19
|
type StateOverride,
|
|
19
20
|
type WatchContractEventReturnType,
|
|
21
|
+
encodeAbiParameters,
|
|
20
22
|
encodeFunctionData,
|
|
21
23
|
getContract,
|
|
22
24
|
hexToBigInt,
|
|
@@ -30,11 +32,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
|
|
|
30
32
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
31
33
|
import type { ViemClient } from '../types.js';
|
|
32
34
|
import { formatViemError } from '../utils.js';
|
|
33
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
34
35
|
import { GSEContract } from './gse.js';
|
|
35
36
|
import type { L1EventLog } from './log.js';
|
|
36
37
|
import { SlasherContract } from './slasher_contract.js';
|
|
37
|
-
import {
|
|
38
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
38
39
|
import { checkBlockTag } from './utils.js';
|
|
39
40
|
|
|
40
41
|
export type ViemCommitteeAttestation = {
|
|
@@ -56,7 +57,6 @@ export type L1RollupContractAddresses = Pick<
|
|
|
56
57
|
| 'feeJuiceAddress'
|
|
57
58
|
| 'stakingAssetAddress'
|
|
58
59
|
| 'rewardDistributorAddress'
|
|
59
|
-
| 'slashFactoryAddress'
|
|
60
60
|
| 'gseAddress'
|
|
61
61
|
>;
|
|
62
62
|
|
|
@@ -86,12 +86,6 @@ export type ViemGasFees = {
|
|
|
86
86
|
feePerL2Gas: bigint;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
export enum SlashingProposerType {
|
|
90
|
-
None = 0,
|
|
91
|
-
Tally = 1,
|
|
92
|
-
Empire = 2,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
89
|
/**
|
|
96
90
|
* Status of a validator/attester in the staking system.
|
|
97
91
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -135,6 +129,17 @@ export type L1FeeData = {
|
|
|
135
129
|
blobFee: bigint;
|
|
136
130
|
};
|
|
137
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
|
+
|
|
138
143
|
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
139
144
|
export type ManaMinFeeComponents = {
|
|
140
145
|
sequencerCost: bigint;
|
|
@@ -269,34 +274,18 @@ export class RollupContract {
|
|
|
269
274
|
return this.rollup;
|
|
270
275
|
}
|
|
271
276
|
|
|
272
|
-
public async getSlashingProposer(): Promise<
|
|
273
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
274
|
-
> {
|
|
277
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
275
278
|
const slasher = await this.getSlasherContract();
|
|
276
279
|
if (!slasher) {
|
|
277
280
|
return undefined;
|
|
278
281
|
}
|
|
279
282
|
|
|
280
283
|
const proposerAddress = await slasher.getProposer();
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
type: 'function',
|
|
284
|
-
name: 'SLASHING_PROPOSER_TYPE',
|
|
285
|
-
inputs: [],
|
|
286
|
-
outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
|
|
287
|
-
stateMutability: 'view',
|
|
288
|
-
},
|
|
289
|
-
] as const;
|
|
290
|
-
|
|
291
|
-
const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
|
|
292
|
-
const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
|
|
293
|
-
if (proposerType === SlashingProposerType.Tally.valueOf()) {
|
|
294
|
-
return new TallySlashingProposerContract(this.client, proposerAddress);
|
|
295
|
-
} else if (proposerType === SlashingProposerType.Empire.valueOf()) {
|
|
296
|
-
return new EmpireSlashingProposerContract(this.client, proposerAddress);
|
|
297
|
-
} else {
|
|
298
|
-
throw new Error(`Unknown slashing proposer type: ${proposerType}`);
|
|
284
|
+
if (proposerAddress.isZero()) {
|
|
285
|
+
return undefined;
|
|
299
286
|
}
|
|
287
|
+
|
|
288
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
300
289
|
}
|
|
301
290
|
|
|
302
291
|
@memoize
|
|
@@ -537,8 +526,9 @@ export class RollupContract {
|
|
|
537
526
|
return this.rollup.read.getCheckpointReward();
|
|
538
527
|
}
|
|
539
528
|
|
|
540
|
-
async getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
541
|
-
|
|
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));
|
|
542
532
|
}
|
|
543
533
|
|
|
544
534
|
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
@@ -546,18 +536,31 @@ export class RollupContract {
|
|
|
546
536
|
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
547
537
|
}
|
|
548
538
|
|
|
549
|
-
async getSlotNumber(): Promise<SlotNumber> {
|
|
550
|
-
|
|
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));
|
|
551
542
|
}
|
|
552
543
|
|
|
553
|
-
async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
|
|
554
|
-
|
|
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);
|
|
555
547
|
return {
|
|
556
548
|
baseFee: result.baseFee,
|
|
557
549
|
blobFee: result.blobFee,
|
|
558
550
|
};
|
|
559
551
|
}
|
|
560
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
|
+
|
|
561
564
|
getEthPerFeeAsset(): Promise<bigint> {
|
|
562
565
|
return this.rollup.read.getEthPerFeeAsset();
|
|
563
566
|
}
|
|
@@ -632,8 +635,9 @@ export class RollupContract {
|
|
|
632
635
|
return EthAddress.fromString(result);
|
|
633
636
|
}
|
|
634
637
|
|
|
635
|
-
async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
|
|
636
|
-
|
|
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);
|
|
637
641
|
return {
|
|
638
642
|
archive: Fr.fromString(result.archive),
|
|
639
643
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -666,6 +670,30 @@ export class RollupContract {
|
|
|
666
670
|
);
|
|
667
671
|
}
|
|
668
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
|
+
|
|
669
697
|
async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
|
|
670
698
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
671
699
|
return {
|
|
@@ -788,7 +816,7 @@ export class RollupContract {
|
|
|
788
816
|
archive: Buffer,
|
|
789
817
|
account: `0x${string}` | Account,
|
|
790
818
|
timestamp: bigint,
|
|
791
|
-
|
|
819
|
+
stateOverride: StateOverride = [],
|
|
792
820
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
793
821
|
const timeOfNextL1Slot = timestamp;
|
|
794
822
|
const who = typeof account === 'string' ? account : account.address;
|
|
@@ -802,7 +830,7 @@ export class RollupContract {
|
|
|
802
830
|
functionName: 'canProposeAtTime',
|
|
803
831
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
804
832
|
account,
|
|
805
|
-
stateOverride
|
|
833
|
+
stateOverride,
|
|
806
834
|
});
|
|
807
835
|
|
|
808
836
|
return {
|
|
@@ -838,6 +866,151 @@ export class RollupContract {
|
|
|
838
866
|
];
|
|
839
867
|
}
|
|
840
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
|
+
|
|
841
1014
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
842
1015
|
public buildInvalidateBadAttestationRequest(
|
|
843
1016
|
checkpointNumber: CheckpointNumber,
|
|
@@ -886,8 +1059,8 @@ export class RollupContract {
|
|
|
886
1059
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
887
1060
|
}
|
|
888
1061
|
|
|
889
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
890
|
-
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 });
|
|
891
1064
|
}
|
|
892
1065
|
|
|
893
1066
|
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
@@ -1052,7 +1225,7 @@ export class RollupContract {
|
|
|
1052
1225
|
}
|
|
1053
1226
|
|
|
1054
1227
|
public listenToCheckpointInvalidated(
|
|
1055
|
-
callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
|
|
1228
|
+
callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
|
|
1056
1229
|
): WatchContractEventReturnType {
|
|
1057
1230
|
return this.rollup.watchEvent.CheckpointInvalidated(
|
|
1058
1231
|
{},
|
|
@@ -1061,7 +1234,7 @@ export class RollupContract {
|
|
|
1061
1234
|
for (const log of logs) {
|
|
1062
1235
|
const args = log.args;
|
|
1063
1236
|
if (args.checkpointNumber !== undefined) {
|
|
1064
|
-
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
|
|
1237
|
+
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
|
|
1065
1238
|
}
|
|
1066
1239
|
}
|
|
1067
1240
|
},
|
|
@@ -1094,6 +1267,17 @@ export class RollupContract {
|
|
|
1094
1267
|
);
|
|
1095
1268
|
}
|
|
1096
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
|
+
|
|
1097
1281
|
/** Fetches CheckpointProposed events within the given block range. */
|
|
1098
1282
|
async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
|
|
1099
1283
|
const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
|
|
@@ -1126,4 +1310,39 @@ export class RollupContract {
|
|
|
1126
1310
|
},
|
|
1127
1311
|
}));
|
|
1128
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
|
+
}
|
|
1129
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,
|
package/src/l1_artifacts.ts
CHANGED
|
@@ -3,10 +3,6 @@ import {
|
|
|
3
3
|
CoinIssuerBytecode,
|
|
4
4
|
DateGatedRelayerAbi,
|
|
5
5
|
DateGatedRelayerBytecode,
|
|
6
|
-
EmpireSlasherDeploymentExtLibAbi,
|
|
7
|
-
EmpireSlasherDeploymentExtLibBytecode,
|
|
8
|
-
EmpireSlashingProposerAbi,
|
|
9
|
-
EmpireSlashingProposerBytecode,
|
|
10
6
|
FeeAssetHandlerAbi,
|
|
11
7
|
FeeAssetHandlerBytecode,
|
|
12
8
|
FeeJuicePortalAbi,
|
|
@@ -42,16 +38,14 @@ import {
|
|
|
42
38
|
RollupLinkReferences,
|
|
43
39
|
RollupOperationsExtLibAbi,
|
|
44
40
|
RollupOperationsExtLibBytecode,
|
|
45
|
-
SlashFactoryAbi,
|
|
46
|
-
SlashFactoryBytecode,
|
|
47
41
|
SlasherAbi,
|
|
48
42
|
SlasherBytecode,
|
|
43
|
+
SlasherDeploymentExtLibAbi,
|
|
44
|
+
SlasherDeploymentExtLibBytecode,
|
|
45
|
+
SlashingProposerAbi,
|
|
46
|
+
SlashingProposerBytecode,
|
|
49
47
|
StakingAssetHandlerAbi,
|
|
50
48
|
StakingAssetHandlerBytecode,
|
|
51
|
-
TallySlasherDeploymentExtLibAbi,
|
|
52
|
-
TallySlasherDeploymentExtLibBytecode,
|
|
53
|
-
TallySlashingProposerAbi,
|
|
54
|
-
TallySlashingProposerBytecode,
|
|
55
49
|
TestERC20Abi,
|
|
56
50
|
TestERC20Bytecode,
|
|
57
51
|
ValidatorOperationsExtLibAbi,
|
|
@@ -107,15 +101,10 @@ export const RollupArtifact = {
|
|
|
107
101
|
contractAbi: RewardExtLibAbi,
|
|
108
102
|
contractBytecode: RewardExtLibBytecode as Hex,
|
|
109
103
|
},
|
|
110
|
-
|
|
111
|
-
name: '
|
|
112
|
-
contractAbi:
|
|
113
|
-
contractBytecode:
|
|
114
|
-
},
|
|
115
|
-
EmpireSlasherDeploymentExtLib: {
|
|
116
|
-
name: 'EmpireSlasherDeploymentExtLib',
|
|
117
|
-
contractAbi: EmpireSlasherDeploymentExtLibAbi,
|
|
118
|
-
contractBytecode: EmpireSlasherDeploymentExtLibBytecode as Hex,
|
|
104
|
+
SlasherDeploymentExtLib: {
|
|
105
|
+
name: 'SlasherDeploymentExtLib',
|
|
106
|
+
contractAbi: SlasherDeploymentExtLibAbi,
|
|
107
|
+
contractBytecode: SlasherDeploymentExtLibBytecode as Hex,
|
|
119
108
|
},
|
|
120
109
|
},
|
|
121
110
|
},
|
|
@@ -175,22 +164,10 @@ export const SlasherArtifact = {
|
|
|
175
164
|
contractBytecode: SlasherBytecode as Hex,
|
|
176
165
|
};
|
|
177
166
|
|
|
178
|
-
export const
|
|
179
|
-
name: '
|
|
180
|
-
contractAbi:
|
|
181
|
-
contractBytecode:
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
export const TallySlashingProposerArtifact = {
|
|
185
|
-
name: 'TallySlashingProposer',
|
|
186
|
-
contractAbi: TallySlashingProposerAbi,
|
|
187
|
-
contractBytecode: TallySlashingProposerBytecode as Hex,
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
export const SlashFactoryArtifact = {
|
|
191
|
-
name: 'SlashFactory',
|
|
192
|
-
contractAbi: SlashFactoryAbi,
|
|
193
|
-
contractBytecode: SlashFactoryBytecode as Hex,
|
|
167
|
+
export const SlashingProposerArtifact = {
|
|
168
|
+
name: 'SlashingProposer',
|
|
169
|
+
contractAbi: SlashingProposerAbi,
|
|
170
|
+
contractBytecode: SlashingProposerBytecode as Hex,
|
|
194
171
|
};
|
|
195
172
|
|
|
196
173
|
export const RegisterNewRollupVersionPayloadArtifact = {
|