@aztec/ethereum 0.0.1-commit.f2ce05ee → 0.0.1-commit.f5a9928
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 +27 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +63 -8
- package/dest/config.d.ts +17 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +38 -10
- package/dest/contracts/chain_state_override.d.ts +78 -0
- package/dest/contracts/chain_state_override.d.ts.map +1 -0
- package/dest/contracts/chain_state_override.js +137 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
- package/dest/contracts/fee_asset_price_oracle.js +656 -0
- package/dest/contracts/governance.d.ts +135 -35
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +229 -18
- package/dest/contracts/governance_proposer.d.ts +33 -2
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +54 -6
- package/dest/contracts/gse.d.ts +4 -2
- package/dest/contracts/gse.d.ts.map +1 -1
- package/dest/contracts/gse.js +2 -2
- package/dest/contracts/inbox.d.ts +3 -3
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +12 -7
- package/dest/contracts/index.d.ts +4 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +3 -2
- package/dest/contracts/multicall.d.ts +127 -12
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +201 -94
- package/dest/contracts/outbox.d.ts +27 -4
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +34 -7
- package/dest/contracts/registry.d.ts +3 -1
- package/dest/contracts/registry.d.ts.map +1 -1
- package/dest/contracts/registry.js +30 -1
- package/dest/contracts/rollup.d.ts +5437 -34
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +448 -104
- package/dest/contracts/slashing_proposer.d.ts +171 -0
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/slashing_proposer.js +753 -0
- package/dest/contracts/watch_event.d.ts +36 -0
- package/dest/contracts/watch_event.d.ts.map +1 -0
- package/dest/contracts/watch_event.js +66 -0
- package/dest/deploy_aztec_l1_contracts.d.ts +5 -9
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +48 -27
- package/dest/deploy_l1_contract.js +3 -3
- package/dest/foundry_binary.d.ts +13 -0
- package/dest/foundry_binary.d.ts.map +1 -0
- package/dest/foundry_binary.js +52 -0
- package/dest/generated/l1-contracts-defaults.d.ts +3 -3
- package/dest/generated/l1-contracts-defaults.js +3 -3
- package/dest/l1_artifacts.d.ts +15524 -15847
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_artifacts.js +9 -24
- package/dest/l1_contract_addresses.d.ts +61 -65
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +101 -82
- package/dest/l1_reader.d.ts +5 -5
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +9 -7
- package/dest/l1_tx_utils/config.d.ts +7 -1
- package/dest/l1_tx_utils/config.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.js +17 -4
- package/dest/l1_tx_utils/factory.d.ts +18 -10
- package/dest/l1_tx_utils/factory.d.ts.map +1 -1
- package/dest/l1_tx_utils/factory.js +17 -7
- package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
- package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
- package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
- package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
- package/dest/l1_tx_utils/index-blobs.js +2 -2
- package/dest/l1_tx_utils/index.d.ts +2 -1
- package/dest/l1_tx_utils/index.d.ts.map +1 -1
- package/dest/l1_tx_utils/index.js +1 -0
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -7
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +156 -79
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +57 -23
- package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
- package/dest/{test → l1_tx_utils}/tx_delayer.js +67 -37
- package/dest/publisher_manager.d.ts +34 -8
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +119 -8
- package/dest/queries.d.ts +13 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +62 -3
- package/dest/test/blob_kzg_warmup.d.ts +19 -0
- package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
- package/dest/test/blob_kzg_warmup.js +64 -0
- package/dest/test/chain_monitor.d.ts +55 -4
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +104 -3
- package/dest/test/eth_cheat_codes.d.ts +31 -6
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +67 -44
- package/dest/test/index.d.ts +2 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -2
- package/dest/test/recording_rpc_server.d.ts +18 -0
- package/dest/test/recording_rpc_server.d.ts.map +1 -0
- package/dest/test/recording_rpc_server.js +68 -0
- package/dest/test/rollup_cheat_codes.d.ts +60 -3
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +86 -7
- package/dest/test/start_anvil.d.ts +23 -3
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +182 -32
- package/dest/test/upgrade_utils.js +2 -2
- package/dest/utils.d.ts +9 -2
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +59 -24
- package/package.json +6 -8
- package/src/client.ts +72 -2
- package/src/config.ts +58 -14
- package/src/contracts/chain_state_override.ts +200 -0
- package/src/contracts/fee_asset_price_oracle.ts +285 -0
- package/src/contracts/governance.ts +313 -13
- package/src/contracts/governance_proposer.ts +58 -9
- package/src/contracts/gse.ts +7 -2
- package/src/contracts/inbox.ts +13 -5
- package/src/contracts/index.ts +3 -2
- package/src/contracts/multicall.ts +243 -105
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/registry.ts +31 -1
- package/src/contracts/rollup.ts +507 -109
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +119 -45
- package/src/contracts/watch_event.ts +110 -0
- package/src/deploy_aztec_l1_contracts.ts +75 -39
- package/src/deploy_l1_contract.ts +3 -3
- package/src/foundry_binary.ts +57 -0
- package/src/generated/l1-contracts-defaults.ts +3 -3
- package/src/l1_artifacts.ts +12 -35
- package/src/l1_contract_addresses.ts +120 -94
- package/src/l1_reader.ts +17 -10
- package/src/l1_tx_utils/config.ts +23 -3
- package/src/l1_tx_utils/factory.ts +31 -31
- package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
- package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
- package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
- package/src/l1_tx_utils/index-blobs.ts +2 -2
- package/src/l1_tx_utils/index.ts +1 -0
- package/src/l1_tx_utils/l1_tx_utils.ts +149 -64
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +57 -17
- package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
- package/src/publisher_manager.ts +143 -12
- package/src/queries.ts +70 -4
- package/src/test/blob_kzg_warmup.ts +65 -0
- package/src/test/chain_monitor.ts +148 -3
- package/src/test/eth_cheat_codes.ts +63 -47
- package/src/test/index.ts +1 -2
- package/src/test/recording_rpc_server.ts +69 -0
- package/src/test/rollup_cheat_codes.ts +115 -6
- package/src/test/start_anvil.ts +212 -32
- package/src/test/upgrade_utils.ts +2 -2
- package/src/utils.ts +56 -29
- package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
- package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/empire_slashing_proposer.js +0 -207
- package/dest/contracts/tally_slashing_proposer.d.ts +0 -140
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/tally_slashing_proposer.js +0 -320
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
- package/dest/test/delayed_tx_utils.d.ts +0 -13
- package/dest/test/delayed_tx_utils.d.ts.map +0 -1
- package/dest/test/delayed_tx_utils.js +0 -28
- package/dest/test/tx_delayer.d.ts +0 -36
- package/dest/test/tx_delayer.d.ts.map +0 -1
- package/src/contracts/empire_slashing_proposer.ts +0 -259
- package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
- package/src/test/delayed_tx_utils.ts +0 -52
package/dest/contracts/rollup.js
CHANGED
|
@@ -376,25 +376,21 @@ import { Buffer32 } from '@aztec/foundation/buffer';
|
|
|
376
376
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
377
377
|
import { memoize } from '@aztec/foundation/decorators';
|
|
378
378
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
379
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
379
380
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
381
|
+
import { getErrorCause } from '@aztec/foundation/types';
|
|
380
382
|
import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
|
|
381
383
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
382
384
|
import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
|
|
383
385
|
import chunk from 'lodash.chunk';
|
|
384
|
-
import { encodeFunctionData, getContract, hexToBigInt, keccak256 } from 'viem';
|
|
386
|
+
import { ContractFunctionRevertedError, RpcRequestError, decodeErrorResult, encodeAbiParameters, encodeFunctionData, getContract, hexToBigInt, keccak256 } from 'viem';
|
|
385
387
|
import { getPublicClient } from '../client.js';
|
|
386
388
|
import { formatViemError } from '../utils.js';
|
|
387
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
388
389
|
import { GSEContract } from './gse.js';
|
|
389
390
|
import { SlasherContract } from './slasher_contract.js';
|
|
390
|
-
import {
|
|
391
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
391
392
|
import { checkBlockTag } from './utils.js';
|
|
392
|
-
|
|
393
|
-
SlashingProposerType[SlashingProposerType["None"] = 0] = "None";
|
|
394
|
-
SlashingProposerType[SlashingProposerType["Tally"] = 1] = "Tally";
|
|
395
|
-
SlashingProposerType[SlashingProposerType["Empire"] = 2] = "Empire";
|
|
396
|
-
return SlashingProposerType;
|
|
397
|
-
}({});
|
|
393
|
+
import { watchContractEvent } from './watch_event.js';
|
|
398
394
|
/**
|
|
399
395
|
* Status of a validator/attester in the staking system.
|
|
400
396
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -405,6 +401,38 @@ export var SlashingProposerType = /*#__PURE__*/ function(SlashingProposerType) {
|
|
|
405
401
|
AttesterStatus[AttesterStatus["EXITING"] = 3] = "EXITING";
|
|
406
402
|
return AttesterStatus;
|
|
407
403
|
}({});
|
|
404
|
+
/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */ export var TempCheckpointLogField = /*#__PURE__*/ function(TempCheckpointLogField) {
|
|
405
|
+
TempCheckpointLogField[TempCheckpointLogField["HeaderHash"] = 0] = "HeaderHash";
|
|
406
|
+
TempCheckpointLogField[TempCheckpointLogField["BlobCommitmentsHash"] = 1] = "BlobCommitmentsHash";
|
|
407
|
+
TempCheckpointLogField[TempCheckpointLogField["OutHash"] = 2] = "OutHash";
|
|
408
|
+
TempCheckpointLogField[TempCheckpointLogField["AttestationsHash"] = 3] = "AttestationsHash";
|
|
409
|
+
TempCheckpointLogField[TempCheckpointLogField["PayloadDigest"] = 4] = "PayloadDigest";
|
|
410
|
+
TempCheckpointLogField[TempCheckpointLogField["SlotNumber"] = 5] = "SlotNumber";
|
|
411
|
+
TempCheckpointLogField[TempCheckpointLogField["FeeHeader"] = 6] = "FeeHeader";
|
|
412
|
+
return TempCheckpointLogField;
|
|
413
|
+
}({});
|
|
414
|
+
const INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR = 'ValidatorSelection__InsufficientValidatorSetSize';
|
|
415
|
+
/** SlasherUpdated events are rare governance operations, so their watcher polls well below the client's interval. */ const SLASHER_UPDATED_POLLING_INTERVAL_MS = 60_000;
|
|
416
|
+
function isValidatorSelectionError(err, errorName) {
|
|
417
|
+
return getErrorCause(err, ContractFunctionRevertedError)?.data?.errorName === errorName || decodeRpcRequestErrorName(err) === errorName;
|
|
418
|
+
}
|
|
419
|
+
function decodeRpcRequestErrorName(err) {
|
|
420
|
+
const data = getErrorCause(err, RpcRequestError)?.data;
|
|
421
|
+
if (!isHexString(data)) {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
return decodeErrorResult({
|
|
426
|
+
abi: RollupAbi,
|
|
427
|
+
data
|
|
428
|
+
}).errorName;
|
|
429
|
+
} catch {
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function isHexString(value) {
|
|
434
|
+
return typeof value === 'string' && value.startsWith('0x');
|
|
435
|
+
}
|
|
408
436
|
export class RollupContract {
|
|
409
437
|
client;
|
|
410
438
|
static{
|
|
@@ -499,6 +527,16 @@ export class RollupContract {
|
|
|
499
527
|
2,
|
|
500
528
|
"getGenesisArchiveTreeRoot"
|
|
501
529
|
],
|
|
530
|
+
[
|
|
531
|
+
memoize,
|
|
532
|
+
2,
|
|
533
|
+
"getVkTreeRoot"
|
|
534
|
+
],
|
|
535
|
+
[
|
|
536
|
+
memoize,
|
|
537
|
+
2,
|
|
538
|
+
"getProtocolContractsHash"
|
|
539
|
+
],
|
|
502
540
|
[
|
|
503
541
|
memoize,
|
|
504
542
|
2,
|
|
@@ -507,6 +545,7 @@ export class RollupContract {
|
|
|
507
545
|
], []));
|
|
508
546
|
}
|
|
509
547
|
rollup;
|
|
548
|
+
logger;
|
|
510
549
|
static cachedStfStorageSlot;
|
|
511
550
|
cachedEscapeHatch;
|
|
512
551
|
static get checkBlobStorageSlot() {
|
|
@@ -525,12 +564,12 @@ export class RollupContract {
|
|
|
525
564
|
}
|
|
526
565
|
static getFromConfig(config) {
|
|
527
566
|
const client = getPublicClient(config);
|
|
528
|
-
const address = config.
|
|
567
|
+
const address = config.rollupAddress.toString();
|
|
529
568
|
return new RollupContract(client, address);
|
|
530
569
|
}
|
|
531
570
|
constructor(client, address){
|
|
532
571
|
this.client = client;
|
|
533
|
-
_initProto(this);
|
|
572
|
+
this.logger = (_initProto(this), createLogger('ethereum:rollup'));
|
|
534
573
|
if (address instanceof EthAddress) {
|
|
535
574
|
address = address.toString();
|
|
536
575
|
}
|
|
@@ -555,34 +594,10 @@ export class RollupContract {
|
|
|
555
594
|
return undefined;
|
|
556
595
|
}
|
|
557
596
|
const proposerAddress = await slasher.getProposer();
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
type: 'function',
|
|
561
|
-
name: 'SLASHING_PROPOSER_TYPE',
|
|
562
|
-
inputs: [],
|
|
563
|
-
outputs: [
|
|
564
|
-
{
|
|
565
|
-
name: '',
|
|
566
|
-
type: 'uint8',
|
|
567
|
-
internalType: 'enum SlasherFlavor'
|
|
568
|
-
}
|
|
569
|
-
],
|
|
570
|
-
stateMutability: 'view'
|
|
571
|
-
}
|
|
572
|
-
];
|
|
573
|
-
const proposer = getContract({
|
|
574
|
-
address: proposerAddress.toString(),
|
|
575
|
-
abi: proposerAbi,
|
|
576
|
-
client: this.client
|
|
577
|
-
});
|
|
578
|
-
const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
|
|
579
|
-
if (proposerType === 1..valueOf()) {
|
|
580
|
-
return new TallySlashingProposerContract(this.client, proposerAddress);
|
|
581
|
-
} else if (proposerType === 2..valueOf()) {
|
|
582
|
-
return new EmpireSlashingProposerContract(this.client, proposerAddress);
|
|
583
|
-
} else {
|
|
584
|
-
throw new Error(`Unknown slashing proposer type: ${proposerType}`);
|
|
597
|
+
if (proposerAddress.isZero()) {
|
|
598
|
+
return undefined;
|
|
585
599
|
}
|
|
600
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
586
601
|
}
|
|
587
602
|
getL1StartBlock() {
|
|
588
603
|
return this.rollup.read.L1_BLOCK_AT_GENESIS();
|
|
@@ -640,18 +655,35 @@ export class RollupContract {
|
|
|
640
655
|
0n
|
|
641
656
|
]));
|
|
642
657
|
}
|
|
658
|
+
async getVkTreeRoot() {
|
|
659
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
|
|
660
|
+
const value = await this.client.getStorageAt({
|
|
661
|
+
address: this.address,
|
|
662
|
+
slot: `0x${slot.toString(16)}`
|
|
663
|
+
});
|
|
664
|
+
return Fr.fromString(value ?? '0x0');
|
|
665
|
+
}
|
|
666
|
+
async getProtocolContractsHash() {
|
|
667
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
|
|
668
|
+
const value = await this.client.getStorageAt({
|
|
669
|
+
address: this.address,
|
|
670
|
+
slot: `0x${slot.toString(16)}`
|
|
671
|
+
});
|
|
672
|
+
return Fr.fromString(value ?? '0x0');
|
|
673
|
+
}
|
|
643
674
|
/**
|
|
644
675
|
* Returns rollup constants used for epoch queries.
|
|
645
676
|
* Return type is `L1RollupConstants` which is defined in stdlib,
|
|
646
677
|
* so we cant reference it until we move this contract to that package.
|
|
647
678
|
*/ async getRollupConstants() {
|
|
648
|
-
const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs, targetCommitteeSize] = await Promise.all([
|
|
679
|
+
const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs, targetCommitteeSize, rollupManaLimit] = await Promise.all([
|
|
649
680
|
this.getL1StartBlock(),
|
|
650
681
|
this.getL1GenesisTime(),
|
|
651
682
|
this.getSlotDuration(),
|
|
652
683
|
this.getEpochDuration(),
|
|
653
684
|
this.getProofSubmissionEpochs(),
|
|
654
|
-
this.getTargetCommitteeSize()
|
|
685
|
+
this.getTargetCommitteeSize(),
|
|
686
|
+
this.getManaLimit()
|
|
655
687
|
]);
|
|
656
688
|
return {
|
|
657
689
|
l1StartBlock,
|
|
@@ -659,7 +691,8 @@ export class RollupContract {
|
|
|
659
691
|
slotDuration,
|
|
660
692
|
epochDuration: Number(epochDuration),
|
|
661
693
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
662
|
-
targetCommitteeSize
|
|
694
|
+
targetCommitteeSize,
|
|
695
|
+
rollupManaLimit: Number(rollupManaLimit)
|
|
663
696
|
};
|
|
664
697
|
}
|
|
665
698
|
async getSlasherAddress() {
|
|
@@ -705,7 +738,11 @@ export class RollupContract {
|
|
|
705
738
|
BigInt(epoch)
|
|
706
739
|
]);
|
|
707
740
|
return isOpen;
|
|
708
|
-
} catch
|
|
741
|
+
} catch (err) {
|
|
742
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
743
|
+
epoch: Number(epoch),
|
|
744
|
+
error: err
|
|
745
|
+
});
|
|
709
746
|
return false;
|
|
710
747
|
}
|
|
711
748
|
}
|
|
@@ -721,8 +758,9 @@ export class RollupContract {
|
|
|
721
758
|
async getOwner() {
|
|
722
759
|
return EthAddress.fromString(await this.rollup.read.owner());
|
|
723
760
|
}
|
|
724
|
-
async getActiveAttesterCount() {
|
|
725
|
-
|
|
761
|
+
async getActiveAttesterCount(options) {
|
|
762
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
763
|
+
return Number(await this.rollup.read.getActiveAttesterCount(options));
|
|
726
764
|
}
|
|
727
765
|
async getSlashingProposerAddress() {
|
|
728
766
|
const slasher = await this.getSlasherContract();
|
|
@@ -734,24 +772,40 @@ export class RollupContract {
|
|
|
734
772
|
getCheckpointReward() {
|
|
735
773
|
return this.rollup.read.getCheckpointReward();
|
|
736
774
|
}
|
|
737
|
-
async getCheckpointNumber() {
|
|
738
|
-
|
|
775
|
+
async getCheckpointNumber(options) {
|
|
776
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
777
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
|
|
739
778
|
}
|
|
740
|
-
async getProvenCheckpointNumber() {
|
|
741
|
-
|
|
779
|
+
async getProvenCheckpointNumber(options) {
|
|
780
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
781
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
742
782
|
}
|
|
743
|
-
async getSlotNumber() {
|
|
744
|
-
|
|
783
|
+
async getSlotNumber(options) {
|
|
784
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
785
|
+
return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
|
|
745
786
|
}
|
|
746
|
-
async getL1FeesAt(timestamp) {
|
|
787
|
+
async getL1FeesAt(timestamp, options) {
|
|
788
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
747
789
|
const result = await this.rollup.read.getL1FeesAt([
|
|
748
790
|
timestamp
|
|
749
|
-
]);
|
|
791
|
+
], options);
|
|
750
792
|
return {
|
|
751
793
|
baseFee: result.baseFee,
|
|
752
794
|
blobFee: result.blobFee
|
|
753
795
|
};
|
|
754
796
|
}
|
|
797
|
+
async getFeeHeader(checkpointNumber) {
|
|
798
|
+
const result = await this.rollup.read.getFeeHeader([
|
|
799
|
+
checkpointNumber
|
|
800
|
+
]);
|
|
801
|
+
return {
|
|
802
|
+
excessMana: result.excessMana,
|
|
803
|
+
manaUsed: result.manaUsed,
|
|
804
|
+
ethPerFeeAsset: result.ethPerFeeAsset,
|
|
805
|
+
congestionCost: result.congestionCost,
|
|
806
|
+
proverCost: result.proverCost
|
|
807
|
+
};
|
|
808
|
+
}
|
|
755
809
|
getEthPerFeeAsset() {
|
|
756
810
|
return this.rollup.read.getEthPerFeeAsset();
|
|
757
811
|
}
|
|
@@ -764,7 +818,7 @@ export class RollupContract {
|
|
|
764
818
|
timestamp
|
|
765
819
|
]
|
|
766
820
|
}).catch((e)=>{
|
|
767
|
-
if (e
|
|
821
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
768
822
|
return {
|
|
769
823
|
result: undefined
|
|
770
824
|
};
|
|
@@ -791,7 +845,7 @@ export class RollupContract {
|
|
|
791
845
|
functionName: 'getCurrentEpochCommittee',
|
|
792
846
|
args: []
|
|
793
847
|
}).catch((e)=>{
|
|
794
|
-
if (e
|
|
848
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
795
849
|
return {
|
|
796
850
|
result: undefined
|
|
797
851
|
};
|
|
@@ -820,10 +874,11 @@ export class RollupContract {
|
|
|
820
874
|
});
|
|
821
875
|
return EthAddress.fromString(result);
|
|
822
876
|
}
|
|
823
|
-
async getCheckpoint(checkpointNumber) {
|
|
877
|
+
async getCheckpoint(checkpointNumber, options) {
|
|
878
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
824
879
|
const result = await this.rollup.read.getCheckpoint([
|
|
825
880
|
BigInt(checkpointNumber)
|
|
826
|
-
]);
|
|
881
|
+
], options);
|
|
827
882
|
return {
|
|
828
883
|
archive: Fr.fromString(result.archive),
|
|
829
884
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -853,6 +908,28 @@ export class RollupContract {
|
|
|
853
908
|
0.5
|
|
854
909
|
]));
|
|
855
910
|
}
|
|
911
|
+
/**
|
|
912
|
+
* Returns the effective pending checkpoint, accounting for potential prunes.
|
|
913
|
+
* When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
|
|
914
|
+
* This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
|
|
915
|
+
* @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
|
|
916
|
+
* @param options - Optional L1 block number to pin the queries to.
|
|
917
|
+
*/ getEffectivePendingCheckpoint(atTimestamp, options) {
|
|
918
|
+
return retry(async ()=>{
|
|
919
|
+
const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
|
|
920
|
+
const canPrune = await this.canPruneAtTime(timestamp, options);
|
|
921
|
+
if (canPrune) {
|
|
922
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
|
|
923
|
+
return await this.getCheckpoint(provenCheckpointNumber, options);
|
|
924
|
+
}
|
|
925
|
+
const pendingCheckpointNumber = await this.getCheckpointNumber(options);
|
|
926
|
+
return await this.getCheckpoint(pendingCheckpointNumber, options);
|
|
927
|
+
}, 'getting effective pending checkpoint', makeBackoff([
|
|
928
|
+
0.5,
|
|
929
|
+
0.5,
|
|
930
|
+
0.5
|
|
931
|
+
]));
|
|
932
|
+
}
|
|
856
933
|
async getTips() {
|
|
857
934
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
858
935
|
return {
|
|
@@ -937,9 +1014,8 @@ export class RollupContract {
|
|
|
937
1014
|
* @return [slot, checkpointNumber, timeOfNextL1Slot] - If you can propose, the L2 slot number, checkpoint number and
|
|
938
1015
|
* timestamp of the next L1 block
|
|
939
1016
|
* @throws otherwise
|
|
940
|
-
*/ async
|
|
941
|
-
const
|
|
942
|
-
const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
|
|
1017
|
+
*/ async canProposeAt(archive, account, timestamp, stateOverride = []) {
|
|
1018
|
+
const timeOfNextL1Slot = timestamp;
|
|
943
1019
|
const who = typeof account === 'string' ? account : account.address;
|
|
944
1020
|
try {
|
|
945
1021
|
const { result: [slot, checkpointNumber] } = await this.client.simulateContract({
|
|
@@ -952,7 +1028,7 @@ export class RollupContract {
|
|
|
952
1028
|
who
|
|
953
1029
|
],
|
|
954
1030
|
account,
|
|
955
|
-
stateOverride
|
|
1031
|
+
stateOverride
|
|
956
1032
|
});
|
|
957
1033
|
return {
|
|
958
1034
|
slot: SlotNumber.fromBigInt(slot),
|
|
@@ -964,11 +1040,15 @@ export class RollupContract {
|
|
|
964
1040
|
}
|
|
965
1041
|
}
|
|
966
1042
|
/**
|
|
967
|
-
* Returns a state override that sets the pending
|
|
968
|
-
*
|
|
969
|
-
*
|
|
970
|
-
|
|
971
|
-
|
|
1043
|
+
* Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
|
|
1044
|
+
* Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
|
|
1045
|
+
* a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
|
|
1046
|
+
* value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
|
|
1047
|
+
*
|
|
1048
|
+
* Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
|
|
1049
|
+
* `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
|
|
1050
|
+
*/ async makeChainTipsOverride(override) {
|
|
1051
|
+
if (override.pending === undefined && override.proven === undefined) {
|
|
972
1052
|
return [];
|
|
973
1053
|
}
|
|
974
1054
|
const slot = RollupContract.stfStorageSlot;
|
|
@@ -976,8 +1056,15 @@ export class RollupContract {
|
|
|
976
1056
|
address: this.address,
|
|
977
1057
|
slot
|
|
978
1058
|
});
|
|
979
|
-
const
|
|
980
|
-
const
|
|
1059
|
+
const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
|
|
1060
|
+
const currentPending = currentRaw >> 128n;
|
|
1061
|
+
const currentProven = currentRaw & (1n << 128n) - 1n;
|
|
1062
|
+
const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
|
|
1063
|
+
const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
|
|
1064
|
+
if (newProven > newPending) {
|
|
1065
|
+
throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
|
|
1066
|
+
}
|
|
1067
|
+
const newValue = newPending << 128n | newProven;
|
|
981
1068
|
return [
|
|
982
1069
|
{
|
|
983
1070
|
address: this.address,
|
|
@@ -990,6 +1077,161 @@ export class RollupContract {
|
|
|
990
1077
|
}
|
|
991
1078
|
];
|
|
992
1079
|
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
|
|
1082
|
+
* the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
|
|
1083
|
+
* pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
|
|
1084
|
+
*
|
|
1085
|
+
* `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
|
|
1086
|
+
* never asserts against them, so leaving them at storage zero is harmless.
|
|
1087
|
+
*/ async makeTempCheckpointLogOverride(checkpointNumber, fields) {
|
|
1088
|
+
const constants = await this.getRollupConstants();
|
|
1089
|
+
const slotAt = (field)=>`0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}`;
|
|
1090
|
+
const word = (v)=>`0x${v.toString(16).padStart(64, '0')}`;
|
|
1091
|
+
const stateDiff = [];
|
|
1092
|
+
if (fields.headerHash) {
|
|
1093
|
+
stateDiff.push({
|
|
1094
|
+
slot: slotAt(0),
|
|
1095
|
+
value: fields.headerHash.toString()
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
if (fields.outHash) {
|
|
1099
|
+
stateDiff.push({
|
|
1100
|
+
slot: slotAt(2),
|
|
1101
|
+
value: fields.outHash.toString()
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
if (fields.payloadDigest) {
|
|
1105
|
+
stateDiff.push({
|
|
1106
|
+
slot: slotAt(4),
|
|
1107
|
+
value: fields.payloadDigest.toString()
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
if (fields.slotNumber !== undefined) {
|
|
1111
|
+
// CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
|
|
1112
|
+
// so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
|
|
1113
|
+
const slotNumber = BigInt(fields.slotNumber);
|
|
1114
|
+
if (slotNumber < 0n || slotNumber > 0xffffffffn) {
|
|
1115
|
+
throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
|
|
1116
|
+
}
|
|
1117
|
+
stateDiff.push({
|
|
1118
|
+
slot: slotAt(5),
|
|
1119
|
+
value: word(slotNumber)
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
if (fields.feeHeader) {
|
|
1123
|
+
stateDiff.push({
|
|
1124
|
+
slot: slotAt(6),
|
|
1125
|
+
value: word(RollupContract.compressFeeHeader(fields.feeHeader))
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
if (stateDiff.length === 0) {
|
|
1129
|
+
return [];
|
|
1130
|
+
}
|
|
1131
|
+
return [
|
|
1132
|
+
{
|
|
1133
|
+
address: this.address,
|
|
1134
|
+
stateDiff
|
|
1135
|
+
}
|
|
1136
|
+
];
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
1140
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
1141
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
1142
|
+
*/ makeArchiveOverride(checkpointNumber, archive) {
|
|
1143
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
1144
|
+
const archiveSlot = hexToBigInt(keccak256(encodeAbiParameters([
|
|
1145
|
+
{
|
|
1146
|
+
type: 'uint256'
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
type: 'uint256'
|
|
1150
|
+
}
|
|
1151
|
+
], [
|
|
1152
|
+
BigInt(checkpointNumber),
|
|
1153
|
+
archivesMappingBase
|
|
1154
|
+
])));
|
|
1155
|
+
return [
|
|
1156
|
+
{
|
|
1157
|
+
address: this.address,
|
|
1158
|
+
stateDiff: [
|
|
1159
|
+
{
|
|
1160
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
1161
|
+
value: archive.toString()
|
|
1162
|
+
}
|
|
1163
|
+
]
|
|
1164
|
+
}
|
|
1165
|
+
];
|
|
1166
|
+
}
|
|
1167
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */ static mergeStateOverrides(...overrides) {
|
|
1168
|
+
const byAddress = new Map();
|
|
1169
|
+
for (const override of overrides){
|
|
1170
|
+
for (const entry of override){
|
|
1171
|
+
const key = entry.address.toLowerCase();
|
|
1172
|
+
const existing = byAddress.get(key);
|
|
1173
|
+
if (existing) {
|
|
1174
|
+
existing.stateDiff.push(...entry.stateDiff ?? []);
|
|
1175
|
+
if (entry.balance !== undefined) {
|
|
1176
|
+
existing.balance = entry.balance;
|
|
1177
|
+
}
|
|
1178
|
+
} else {
|
|
1179
|
+
byAddress.set(key, {
|
|
1180
|
+
address: entry.address,
|
|
1181
|
+
balance: entry.balance,
|
|
1182
|
+
stateDiff: [
|
|
1183
|
+
...entry.stateDiff ?? []
|
|
1184
|
+
]
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return [
|
|
1190
|
+
...byAddress.values()
|
|
1191
|
+
];
|
|
1192
|
+
}
|
|
1193
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */ static compressFeeHeader(feeHeader) {
|
|
1194
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
1195
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
1196
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
1197
|
+
let value = BigInt(feeHeader.manaUsed) & (1n << 32n) - 1n; // bits [0:31]
|
|
1198
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
1199
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
1200
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
1201
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
1202
|
+
value |= 1n << 255n; // preheat flag
|
|
1203
|
+
return value;
|
|
1204
|
+
}
|
|
1205
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
1206
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */ static computeChildFeeHeader(parentFeeHeader, childManaUsed, feeAssetPriceModifier, manaTarget) {
|
|
1207
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
1208
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
1209
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
1210
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
1211
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
1212
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
1213
|
+
const parentPrice = parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
1214
|
+
let newPrice;
|
|
1215
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
1216
|
+
newPrice = parentPrice * (10_000n + feeAssetPriceModifier) / 10_000n;
|
|
1217
|
+
} else {
|
|
1218
|
+
const absMod = -feeAssetPriceModifier;
|
|
1219
|
+
newPrice = parentPrice * (10_000n - absMod) / 10_000n;
|
|
1220
|
+
}
|
|
1221
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
1222
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
1223
|
+
}
|
|
1224
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
1225
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
1226
|
+
}
|
|
1227
|
+
return {
|
|
1228
|
+
excessMana,
|
|
1229
|
+
manaUsed: childManaUsed,
|
|
1230
|
+
ethPerFeeAsset: newPrice,
|
|
1231
|
+
congestionCost: 0n,
|
|
1232
|
+
proverCost: 0n
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
993
1235
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */ buildInvalidateBadAttestationRequest(checkpointNumber, attestationsAndSigners, committee, invalidIndex) {
|
|
994
1236
|
return {
|
|
995
1237
|
to: this.address,
|
|
@@ -1031,11 +1273,25 @@ export class RollupContract {
|
|
|
1031
1273
|
prover
|
|
1032
1274
|
]);
|
|
1033
1275
|
}
|
|
1034
|
-
getManaMinFeeAt(timestamp, inFeeAsset) {
|
|
1276
|
+
getManaMinFeeAt(timestamp, inFeeAsset, stateOverride) {
|
|
1035
1277
|
return this.rollup.read.getManaMinFeeAt([
|
|
1036
1278
|
timestamp,
|
|
1037
1279
|
inFeeAsset
|
|
1280
|
+
], {
|
|
1281
|
+
stateOverride
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
async getManaMinFeeComponentsAt(timestamp, inFeeAsset) {
|
|
1285
|
+
const result = await this.rollup.read.getManaMinFeeComponentsAt([
|
|
1286
|
+
timestamp,
|
|
1287
|
+
inFeeAsset
|
|
1038
1288
|
]);
|
|
1289
|
+
return {
|
|
1290
|
+
sequencerCost: result.sequencerCost,
|
|
1291
|
+
proverCost: result.proverCost,
|
|
1292
|
+
congestionCost: result.congestionCost,
|
|
1293
|
+
congestionMultiplier: result.congestionMultiplier
|
|
1294
|
+
};
|
|
1039
1295
|
}
|
|
1040
1296
|
async getSlotAt(timestamp) {
|
|
1041
1297
|
return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([
|
|
@@ -1086,15 +1342,24 @@ export class RollupContract {
|
|
|
1086
1342
|
prover
|
|
1087
1343
|
]);
|
|
1088
1344
|
}
|
|
1089
|
-
async getAttesters() {
|
|
1090
|
-
|
|
1345
|
+
async getAttesters(timestamp) {
|
|
1346
|
+
// Pin every read to a single L1 block so the attester count and the chunked index reads
|
|
1347
|
+
// observe a consistent set. Without this, the count and each chunk default to `latest` and
|
|
1348
|
+
// can straddle a block boundary (or reorg), yielding an inconsistent or truncated set.
|
|
1349
|
+
const block = await this.client.getBlock();
|
|
1350
|
+
const blockNumber = block.number ?? undefined;
|
|
1351
|
+
const ts = timestamp ?? block.timestamp;
|
|
1352
|
+
const attesterSize = await this.getActiveAttesterCount({
|
|
1353
|
+
blockNumber
|
|
1354
|
+
});
|
|
1091
1355
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
1092
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
1093
1356
|
const indices = Array.from({
|
|
1094
1357
|
length: attesterSize
|
|
1095
1358
|
}, (_, i)=>BigInt(i));
|
|
1096
1359
|
const chunks = chunk(indices, 1000);
|
|
1097
|
-
const results = await Promise.all(chunks.map((chunk)=>gse.getAttestersFromIndicesAtTime(this.address, ts, chunk
|
|
1360
|
+
const results = await Promise.all(chunks.map((chunk)=>gse.getAttestersFromIndicesAtTime(this.address, ts, chunk, {
|
|
1361
|
+
blockNumber
|
|
1362
|
+
})));
|
|
1098
1363
|
return results.flat().map((addr)=>EthAddress.fromString(addr));
|
|
1099
1364
|
}
|
|
1100
1365
|
async getAttesterView(address) {
|
|
@@ -1176,31 +1441,48 @@ export class RollupContract {
|
|
|
1176
1441
|
})
|
|
1177
1442
|
});
|
|
1178
1443
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1444
|
+
/**
|
|
1445
|
+
* Watches for SlasherUpdated events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
|
|
1446
|
+
* removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
|
|
1447
|
+
* Slasher rotations are rare governance operations, so by default this polls much slower than the other watchers.
|
|
1448
|
+
*/ listenToSlasherChanged(callback, options) {
|
|
1449
|
+
return watchContractEvent(this.client, this.logger, {
|
|
1450
|
+
address: this.address,
|
|
1451
|
+
abi: RollupAbi,
|
|
1452
|
+
eventName: 'SlasherUpdated',
|
|
1453
|
+
onLog: (log)=>{
|
|
1454
|
+
const { oldSlasher, newSlasher } = log.args;
|
|
1455
|
+
if (oldSlasher && newSlasher) {
|
|
1456
|
+
return callback({
|
|
1457
|
+
oldSlasher,
|
|
1458
|
+
newSlasher
|
|
1459
|
+
});
|
|
1187
1460
|
}
|
|
1188
1461
|
}
|
|
1462
|
+
}, {
|
|
1463
|
+
pollingIntervalMs: SLASHER_UPDATED_POLLING_INTERVAL_MS,
|
|
1464
|
+
...options
|
|
1189
1465
|
});
|
|
1190
1466
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1467
|
+
/**
|
|
1468
|
+
* Watches for CheckpointInvalidated events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit
|
|
1469
|
+
* them and removals are never reported, and events mined within roughly one polling interval of subscribing may
|
|
1470
|
+
* be missed.
|
|
1471
|
+
*/ listenToCheckpointInvalidated(callback, options) {
|
|
1472
|
+
return watchContractEvent(this.client, this.logger, {
|
|
1473
|
+
address: this.address,
|
|
1474
|
+
abi: RollupAbi,
|
|
1475
|
+
eventName: 'CheckpointInvalidated',
|
|
1476
|
+
onLog: (log)=>{
|
|
1477
|
+
const { checkpointNumber } = log.args;
|
|
1478
|
+
if (checkpointNumber !== undefined) {
|
|
1479
|
+
return callback({
|
|
1480
|
+
checkpointNumber: CheckpointNumber.fromBigInt(checkpointNumber),
|
|
1481
|
+
event: log
|
|
1482
|
+
});
|
|
1201
1483
|
}
|
|
1202
1484
|
}
|
|
1203
|
-
});
|
|
1485
|
+
}, options);
|
|
1204
1486
|
}
|
|
1205
1487
|
async getSlashEvents(l1BlockHash) {
|
|
1206
1488
|
const events = await this.rollup.getEvents.Slashed({}, {
|
|
@@ -1212,18 +1494,31 @@ export class RollupContract {
|
|
|
1212
1494
|
attester: EthAddress.fromString(event.args.attester)
|
|
1213
1495
|
}));
|
|
1214
1496
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1497
|
+
/**
|
|
1498
|
+
* Watches for Slashed events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
|
|
1499
|
+
* removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
|
|
1500
|
+
*/ listenToSlash(callback, options) {
|
|
1501
|
+
return watchContractEvent(this.client, this.logger, {
|
|
1502
|
+
address: this.address,
|
|
1503
|
+
abi: RollupAbi,
|
|
1504
|
+
eventName: 'Slashed',
|
|
1217
1505
|
strict: true,
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1506
|
+
onLog: (log)=>callback({
|
|
1507
|
+
amount: log.args.amount,
|
|
1508
|
+
attester: EthAddress.fromString(log.args.attester)
|
|
1509
|
+
})
|
|
1510
|
+
}, options);
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
|
|
1514
|
+
* The Rollup inherits from Ownable and emits this event in its constructor, so the event
|
|
1515
|
+
* is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
|
|
1516
|
+
* probe to detect RPC nodes that prune historical logs.
|
|
1517
|
+
*/ async getOwnershipTransferredEventsAtDeploy() {
|
|
1518
|
+
const l1StartBlock = await this.getL1StartBlock();
|
|
1519
|
+
return await this.rollup.getEvents.OwnershipTransferred({}, {
|
|
1520
|
+
fromBlock: l1StartBlock,
|
|
1521
|
+
toBlock: l1StartBlock
|
|
1227
1522
|
});
|
|
1228
1523
|
}
|
|
1229
1524
|
/** Fetches CheckpointProposed events within the given block range. */ async getCheckpointProposedEvents(fromBlock, toBlock) {
|
|
@@ -1239,9 +1534,58 @@ export class RollupContract {
|
|
|
1239
1534
|
checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber),
|
|
1240
1535
|
archive: Fr.fromString(log.args.archive),
|
|
1241
1536
|
versionedBlobHashes: log.args.versionedBlobHashes.map((h)=>Buffer.from(h.slice(2), 'hex')),
|
|
1242
|
-
attestationsHash:
|
|
1243
|
-
|
|
1537
|
+
attestationsHash: (()=>{
|
|
1538
|
+
if (!log.args.attestationsHash) {
|
|
1539
|
+
throw new Error(`CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`);
|
|
1540
|
+
}
|
|
1541
|
+
return Buffer32.fromString(log.args.attestationsHash);
|
|
1542
|
+
})(),
|
|
1543
|
+
payloadDigest: (()=>{
|
|
1544
|
+
if (!log.args.payloadDigest) {
|
|
1545
|
+
throw new Error(`CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`);
|
|
1546
|
+
}
|
|
1547
|
+
return Buffer32.fromString(log.args.payloadDigest);
|
|
1548
|
+
})()
|
|
1244
1549
|
}
|
|
1245
1550
|
}));
|
|
1246
1551
|
}
|
|
1552
|
+
/** Packs pending and proven checkpoint numbers into the chain tips storage format. */ static packChainTips(pendingCheckpointNumber, provenCheckpointNumber) {
|
|
1553
|
+
return pendingCheckpointNumber << 128n | provenCheckpointNumber & (1n << 128n) - 1n;
|
|
1554
|
+
}
|
|
1555
|
+
/** Storage slot for the chain tips (offset 0 within the STF storage struct). */ static get chainTipsStorageSlot() {
|
|
1556
|
+
return BigInt(RollupContract.stfStorageSlot);
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Computes the storage slot for a field within a tempCheckpointLog entry.
|
|
1560
|
+
* @param checkpointNumber - The checkpoint number
|
|
1561
|
+
* @param field - The field within the CompressedTempCheckpointLog struct
|
|
1562
|
+
*/ async getTempCheckpointLogStorageSlot(checkpointNumber, field) {
|
|
1563
|
+
const [epochDuration, proofSubmissionEpochs] = await Promise.all([
|
|
1564
|
+
this.getEpochDuration(),
|
|
1565
|
+
this.getProofSubmissionEpochs()
|
|
1566
|
+
]);
|
|
1567
|
+
return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, {
|
|
1568
|
+
epochDuration,
|
|
1569
|
+
proofSubmissionEpochs
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants) {
|
|
1573
|
+
const fieldOffset = BigInt(field);
|
|
1574
|
+
const { epochDuration, proofSubmissionEpochs } = constants;
|
|
1575
|
+
const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
|
|
1576
|
+
const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
|
|
1577
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
1578
|
+
const entryBase = BigInt(keccak256(encodeAbiParameters([
|
|
1579
|
+
{
|
|
1580
|
+
type: 'uint256'
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
type: 'uint256'
|
|
1584
|
+
}
|
|
1585
|
+
], [
|
|
1586
|
+
circularIndex,
|
|
1587
|
+
tempCheckpointLogsBase
|
|
1588
|
+
])));
|
|
1589
|
+
return entryBase + fieldOffset;
|
|
1590
|
+
}
|
|
1247
1591
|
}
|