@aztec/ethereum 0.0.1-commit.e0f15ab9b → 0.0.1-commit.e304674f1
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/config.d.ts +6 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +5 -9
- package/dest/contracts/inbox.d.ts +3 -3
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +5 -6
- package/dest/contracts/index.d.ts +2 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +1 -2
- package/dest/contracts/rollup.d.ts +28 -11
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +152 -41
- 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 +8176 -15179
- 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_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/queries.js +3 -3
- package/package.json +5 -5
- package/src/config.ts +9 -13
- package/src/contracts/inbox.ts +4 -4
- package/src/contracts/index.ts +1 -2
- package/src/contracts/rollup.ts +171 -35
- 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_tx_utils/l1_tx_utils.ts +24 -14
- package/src/queries.ts +3 -3
- 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
|
@@ -2,6 +2,7 @@ import { maxBigint } from '@aztec/foundation/bigint';
|
|
|
2
2
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
3
3
|
import { InterruptError, TimeoutError } from '@aztec/foundation/error';
|
|
4
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import { Semaphore } from '@aztec/foundation/queue';
|
|
5
6
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
6
7
|
import { sleep } from '@aztec/foundation/sleep';
|
|
7
8
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
@@ -23,10 +24,11 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
|
|
|
23
24
|
metrics;
|
|
24
25
|
txs;
|
|
25
26
|
/** Last nonce successfully sent to the chain. Used as a lower bound when a fallback RPC node returns a stale count. */ lastSentNonce;
|
|
27
|
+
/** Mutex to prevent concurrent sendTransaction calls from racing on the same nonce. */ sendMutex;
|
|
26
28
|
/** Tx delayer for testing. Only set when enableDelayer config is true. */ delayer;
|
|
27
29
|
/** KZG instance for blob operations. */ kzg;
|
|
28
30
|
constructor(client, address, signer, logger = createLogger('ethereum:publisher'), dateProvider = new DateProvider(), config, debugMaxGasLimit = false, store, metrics, kzg, delayer){
|
|
29
|
-
super(client, logger, dateProvider, config, debugMaxGasLimit), this.client = client, this.address = address, this.signer = signer, this.store = store, this.metrics = metrics, this.txs = [];
|
|
31
|
+
super(client, logger, dateProvider, config, debugMaxGasLimit), this.client = client, this.address = address, this.signer = signer, this.store = store, this.metrics = metrics, this.txs = [], this.sendMutex = new Semaphore(1);
|
|
30
32
|
this.kzg = kzg;
|
|
31
33
|
// Set up delayer: use provided one or create new
|
|
32
34
|
if (config?.enableDelayer && config?.ethereumSlotDuration) {
|
|
@@ -171,34 +173,40 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
|
|
|
171
173
|
if (gasConfig.txTimeoutAt && now > gasConfig.txTimeoutAt) {
|
|
172
174
|
throw new TimeoutError(`Transaction timed out before sending (now ${now.toISOString()} > timeoutAt ${gasConfig.txTimeoutAt.toISOString()})`);
|
|
173
175
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
176
|
+
let txHash;
|
|
177
|
+
let nonce;
|
|
178
|
+
let baseState;
|
|
179
|
+
await this.sendMutex.acquire();
|
|
180
|
+
try {
|
|
181
|
+
const chainNonce = await this.client.getTransactionCount({
|
|
182
|
+
address: account,
|
|
183
|
+
blockTag: 'pending'
|
|
184
|
+
});
|
|
185
|
+
// If a fallback RPC node returns a stale count (lower than what we last sent), use our
|
|
186
|
+
// local lower bound to avoid sending a duplicate of an already-pending transaction.
|
|
187
|
+
nonce = this.lastSentNonce !== undefined && chainNonce <= this.lastSentNonce ? this.lastSentNonce + 1 : chainNonce;
|
|
188
|
+
baseState = {
|
|
189
|
+
request,
|
|
190
|
+
gasLimit,
|
|
191
|
+
blobInputs,
|
|
192
|
+
gasPrice,
|
|
193
|
+
nonce
|
|
194
|
+
};
|
|
195
|
+
const txData = this.makeTxData(baseState, {
|
|
196
|
+
isCancelTx: false
|
|
197
|
+
});
|
|
198
|
+
const signedRequest = await this.prepareSignedTransaction(txData);
|
|
199
|
+
txHash = await this.client.sendRawTransaction({
|
|
200
|
+
serializedTransaction: signedRequest
|
|
201
|
+
});
|
|
202
|
+
this.lastSentNonce = nonce;
|
|
203
|
+
} finally{
|
|
204
|
+
this.sendMutex.release();
|
|
205
|
+
}
|
|
198
206
|
// Create the new state for monitoring
|
|
199
207
|
const l1TxState = {
|
|
200
208
|
...baseState,
|
|
201
|
-
id: await this.store?.consumeNextStateId(account) ?? Math.max(...this.txs.map((tx)=>tx.id),
|
|
209
|
+
id: await this.store?.consumeNextStateId(account) ?? Math.max(...this.txs.map((tx)=>tx.id), -1) + 1,
|
|
202
210
|
txHashes: [
|
|
203
211
|
txHash
|
|
204
212
|
],
|
package/dest/queries.js
CHANGED
|
@@ -33,8 +33,8 @@ import { RollupContract } from './contracts/rollup.js';
|
|
|
33
33
|
slasherProposer?.getRoundSize() ?? 0n,
|
|
34
34
|
slasherProposer?.getLifetimeInRounds() ?? 0n,
|
|
35
35
|
slasherProposer?.getExecutionDelayInRounds() ?? 0n,
|
|
36
|
-
slasherProposer?.
|
|
37
|
-
slasherProposer?.
|
|
36
|
+
slasherProposer?.getSlashOffsetInRounds() ?? 0n,
|
|
37
|
+
slasherProposer?.getSlashingAmounts() ?? [
|
|
38
38
|
0n,
|
|
39
39
|
0n,
|
|
40
40
|
0n
|
|
@@ -74,7 +74,7 @@ import { RollupContract } from './contracts/rollup.js';
|
|
|
74
74
|
rollupVersion: Number(rollupVersion),
|
|
75
75
|
genesisArchiveTreeRoot: genesisArchiveTreeRoot.toString(),
|
|
76
76
|
exitDelaySeconds: Number(exitDelay),
|
|
77
|
-
|
|
77
|
+
slasherEnabled: !!slasherProposer,
|
|
78
78
|
slashingOffsetInRounds: Number(slashingOffsetInRounds),
|
|
79
79
|
slashAmountSmall: slashingAmounts[0],
|
|
80
80
|
slashAmountMedium: slashingAmounts[1],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/ethereum",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.e304674f1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./account": "./dest/account.js",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"../package.common.json"
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
54
|
-
"@aztec/constants": "0.0.1-commit.
|
|
55
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
56
|
-
"@aztec/l1-artifacts": "0.0.1-commit.
|
|
53
|
+
"@aztec/blob-lib": "0.0.1-commit.e304674f1",
|
|
54
|
+
"@aztec/constants": "0.0.1-commit.e304674f1",
|
|
55
|
+
"@aztec/foundation": "0.0.1-commit.e304674f1",
|
|
56
|
+
"@aztec/l1-artifacts": "0.0.1-commit.e304674f1",
|
|
57
57
|
"dotenv": "^16.0.3",
|
|
58
58
|
"lodash.chunk": "^4.2.0",
|
|
59
59
|
"lodash.pickby": "^4.5.0",
|
package/src/config.ts
CHANGED
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
type ConfigMappingsType,
|
|
3
3
|
bigintConfigHelper,
|
|
4
4
|
booleanConfigHelper,
|
|
5
|
-
enumConfigHelper,
|
|
6
5
|
getConfigFromMappings,
|
|
7
6
|
getDefaultConfig,
|
|
8
7
|
numberConfigHelper,
|
|
@@ -60,13 +59,13 @@ export type L1ContractsConfig = {
|
|
|
60
59
|
slashingOffsetInRounds: number;
|
|
61
60
|
/** How long slashing can be disabled for in seconds when vetoer disables it */
|
|
62
61
|
slashingDisableDuration: number;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
/** Minimum amount that can be slashed
|
|
62
|
+
/** Whether to deploy a slasher proposer */
|
|
63
|
+
slasherEnabled: boolean;
|
|
64
|
+
/** Minimum amount that can be slashed */
|
|
66
65
|
slashAmountSmall: bigint;
|
|
67
|
-
/** Medium amount to slash
|
|
66
|
+
/** Medium amount to slash */
|
|
68
67
|
slashAmountMedium: bigint;
|
|
69
|
-
/** Largest amount that can be slashed per round
|
|
68
|
+
/** Largest amount that can be slashed per round */
|
|
70
69
|
slashAmountLarge: bigint;
|
|
71
70
|
/** Governance proposing quorum (defaults to roundSize/2 + 1) */
|
|
72
71
|
governanceProposerQuorum?: number;
|
|
@@ -152,13 +151,10 @@ export const l1ContractsConfigMappings: ConfigMappingsType<L1ContractsConfig> =
|
|
|
152
151
|
'How many slashing rounds back we slash (ie when slashing in round N, we slash for offenses committed during epochs of round N-offset)',
|
|
153
152
|
...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_SLASHING_OFFSET_IN_ROUNDS),
|
|
154
153
|
},
|
|
155
|
-
|
|
156
|
-
env: '
|
|
157
|
-
description: '
|
|
158
|
-
...
|
|
159
|
-
['empire', 'tally', 'none'] as const,
|
|
160
|
-
l1ContractsDefaultEnv.AZTEC_SLASHER_FLAVOR as 'empire' | 'tally' | 'none',
|
|
161
|
-
),
|
|
154
|
+
slasherEnabled: {
|
|
155
|
+
env: 'AZTEC_SLASHER_ENABLED',
|
|
156
|
+
description: 'Whether to deploy a slasher proposer',
|
|
157
|
+
...booleanConfigHelper(true),
|
|
162
158
|
},
|
|
163
159
|
slashAmountSmall: {
|
|
164
160
|
env: 'AZTEC_SLASH_AMOUNT_SMALL',
|
package/src/contracts/inbox.ts
CHANGED
|
@@ -82,10 +82,10 @@ export class InboxContract {
|
|
|
82
82
|
.map(log => this.mapMessageSentLog(log));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
/** Fetches MessageSent events for a specific message hash
|
|
86
|
-
async getMessageSentEventByHash(
|
|
87
|
-
const
|
|
88
|
-
return
|
|
85
|
+
/** Fetches MessageSent events for a specific message hash at a specific block. */
|
|
86
|
+
async getMessageSentEventByHash(msgHash: Hex, l1BlockHash: Hex): Promise<MessageSentLog> {
|
|
87
|
+
const [log] = await this.inbox.getEvents.MessageSent({ hash: msgHash }, { blockHash: l1BlockHash });
|
|
88
|
+
return log && this.mapMessageSentLog(log);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
private mapMessageSentLog(log: {
|
package/src/contracts/index.ts
CHANGED
|
@@ -12,6 +12,5 @@ export * from './multicall.js';
|
|
|
12
12
|
export * from './outbox.js';
|
|
13
13
|
export * from './registry.js';
|
|
14
14
|
export * from './rollup.js';
|
|
15
|
-
export * from './
|
|
16
|
-
export * from './tally_slashing_proposer.js';
|
|
15
|
+
export * from './slashing_proposer.js';
|
|
17
16
|
export * from './slasher_contract.js';
|
package/src/contracts/rollup.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
4
4
|
import { memoize } from '@aztec/foundation/decorators';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import type { ViemSignature } from '@aztec/foundation/eth-signature';
|
|
7
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
7
8
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
8
9
|
import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
|
|
9
10
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
type Hex,
|
|
17
18
|
type StateOverride,
|
|
18
19
|
type WatchContractEventReturnType,
|
|
20
|
+
encodeAbiParameters,
|
|
19
21
|
encodeFunctionData,
|
|
20
22
|
getContract,
|
|
21
23
|
hexToBigInt,
|
|
@@ -29,11 +31,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
|
|
|
29
31
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
30
32
|
import type { ViemClient } from '../types.js';
|
|
31
33
|
import { formatViemError } from '../utils.js';
|
|
32
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
33
34
|
import { GSEContract } from './gse.js';
|
|
34
35
|
import type { L1EventLog } from './log.js';
|
|
35
36
|
import { SlasherContract } from './slasher_contract.js';
|
|
36
|
-
import {
|
|
37
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
37
38
|
import { checkBlockTag } from './utils.js';
|
|
38
39
|
|
|
39
40
|
export type ViemCommitteeAttestation = {
|
|
@@ -55,7 +56,6 @@ export type L1RollupContractAddresses = Pick<
|
|
|
55
56
|
| 'feeJuiceAddress'
|
|
56
57
|
| 'stakingAssetAddress'
|
|
57
58
|
| 'rewardDistributorAddress'
|
|
58
|
-
| 'slashFactoryAddress'
|
|
59
59
|
| 'gseAddress'
|
|
60
60
|
>;
|
|
61
61
|
|
|
@@ -85,12 +85,6 @@ export type ViemGasFees = {
|
|
|
85
85
|
feePerL2Gas: bigint;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
export enum SlashingProposerType {
|
|
89
|
-
None = 0,
|
|
90
|
-
Tally = 1,
|
|
91
|
-
Empire = 2,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
88
|
/**
|
|
95
89
|
* Status of a validator/attester in the staking system.
|
|
96
90
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -212,6 +206,7 @@ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
|
212
206
|
|
|
213
207
|
export class RollupContract {
|
|
214
208
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
209
|
+
private readonly logger = createLogger('ethereum:rollup');
|
|
215
210
|
|
|
216
211
|
private static cachedStfStorageSlot: Hex | undefined;
|
|
217
212
|
private cachedEscapeHatch?: {
|
|
@@ -267,34 +262,18 @@ export class RollupContract {
|
|
|
267
262
|
return this.rollup;
|
|
268
263
|
}
|
|
269
264
|
|
|
270
|
-
public async getSlashingProposer(): Promise<
|
|
271
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
272
|
-
> {
|
|
265
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
273
266
|
const slasher = await this.getSlasherContract();
|
|
274
267
|
if (!slasher) {
|
|
275
268
|
return undefined;
|
|
276
269
|
}
|
|
277
270
|
|
|
278
271
|
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}`);
|
|
272
|
+
if (proposerAddress.isZero()) {
|
|
273
|
+
return undefined;
|
|
297
274
|
}
|
|
275
|
+
|
|
276
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
298
277
|
}
|
|
299
278
|
|
|
300
279
|
@memoize
|
|
@@ -495,7 +474,11 @@ export class RollupContract {
|
|
|
495
474
|
|
|
496
475
|
const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
|
|
497
476
|
return isOpen;
|
|
498
|
-
} catch {
|
|
477
|
+
} catch (err) {
|
|
478
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
479
|
+
epoch: Number(epoch),
|
|
480
|
+
error: err,
|
|
481
|
+
});
|
|
499
482
|
return false;
|
|
500
483
|
}
|
|
501
484
|
}
|
|
@@ -782,11 +765,19 @@ export class RollupContract {
|
|
|
782
765
|
archive: Buffer,
|
|
783
766
|
account: `0x${string}` | Account,
|
|
784
767
|
timestamp: bigint,
|
|
785
|
-
opts: {
|
|
768
|
+
opts: {
|
|
769
|
+
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
770
|
+
forceArchive?: { checkpointNumber: CheckpointNumber; archive: Fr };
|
|
771
|
+
} = {},
|
|
786
772
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
787
773
|
const timeOfNextL1Slot = timestamp;
|
|
788
774
|
const who = typeof account === 'string' ? account : account.address;
|
|
789
775
|
|
|
776
|
+
const stateOverride = RollupContract.mergeStateOverrides(
|
|
777
|
+
await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber),
|
|
778
|
+
opts.forceArchive ? this.makeArchiveOverride(opts.forceArchive.checkpointNumber, opts.forceArchive.archive) : [],
|
|
779
|
+
);
|
|
780
|
+
|
|
790
781
|
try {
|
|
791
782
|
const {
|
|
792
783
|
result: [slot, checkpointNumber],
|
|
@@ -796,7 +787,7 @@ export class RollupContract {
|
|
|
796
787
|
functionName: 'canProposeAtTime',
|
|
797
788
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
798
789
|
account,
|
|
799
|
-
stateOverride
|
|
790
|
+
stateOverride,
|
|
800
791
|
});
|
|
801
792
|
|
|
802
793
|
return {
|
|
@@ -832,6 +823,151 @@ export class RollupContract {
|
|
|
832
823
|
];
|
|
833
824
|
}
|
|
834
825
|
|
|
826
|
+
/**
|
|
827
|
+
* Returns a state override that sets tempCheckpointLogs[checkpointNumber].feeHeader to the compressed fee header.
|
|
828
|
+
* Used when simulating a propose call where the parent checkpoint hasn't landed on L1 yet (pipelining).
|
|
829
|
+
*/
|
|
830
|
+
public async makeFeeHeaderOverride(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader): Promise<StateOverride> {
|
|
831
|
+
const { epochDuration, proofSubmissionEpochs } = await this.getRollupConstants();
|
|
832
|
+
const roundaboutSize = BigInt(epochDuration * (proofSubmissionEpochs + 1) + 1);
|
|
833
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
834
|
+
|
|
835
|
+
// tempCheckpointLogs is at offset 2 in RollupStore
|
|
836
|
+
const tempCheckpointLogsMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 2n;
|
|
837
|
+
|
|
838
|
+
// Solidity mapping slot: keccak256(abi.encode(key, baseSlot))
|
|
839
|
+
const structBaseSlot = hexToBigInt(
|
|
840
|
+
keccak256(
|
|
841
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
|
|
842
|
+
),
|
|
843
|
+
);
|
|
844
|
+
|
|
845
|
+
// feeHeader is the 7th field (offset 6) in CompressedTempCheckpointLog
|
|
846
|
+
const feeHeaderSlot = structBaseSlot + 6n;
|
|
847
|
+
const compressed = RollupContract.compressFeeHeader(feeHeader);
|
|
848
|
+
|
|
849
|
+
return [
|
|
850
|
+
{
|
|
851
|
+
address: this.address,
|
|
852
|
+
stateDiff: [
|
|
853
|
+
{
|
|
854
|
+
slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
|
|
855
|
+
value: `0x${compressed.toString(16).padStart(64, '0')}`,
|
|
856
|
+
},
|
|
857
|
+
],
|
|
858
|
+
},
|
|
859
|
+
];
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
864
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
865
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
866
|
+
*/
|
|
867
|
+
public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
|
|
868
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
869
|
+
const archiveSlot = hexToBigInt(
|
|
870
|
+
keccak256(
|
|
871
|
+
encodeAbiParameters(
|
|
872
|
+
[{ type: 'uint256' }, { type: 'uint256' }],
|
|
873
|
+
[BigInt(checkpointNumber), archivesMappingBase],
|
|
874
|
+
),
|
|
875
|
+
),
|
|
876
|
+
);
|
|
877
|
+
return [
|
|
878
|
+
{
|
|
879
|
+
address: this.address,
|
|
880
|
+
stateDiff: [
|
|
881
|
+
{
|
|
882
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
883
|
+
value: archive.toString(),
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
},
|
|
887
|
+
];
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
|
|
891
|
+
public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
|
|
892
|
+
type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
|
|
893
|
+
const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
|
|
894
|
+
for (const override of overrides) {
|
|
895
|
+
for (const entry of override) {
|
|
896
|
+
const key = entry.address.toLowerCase();
|
|
897
|
+
const existing = byAddress.get(key);
|
|
898
|
+
if (existing) {
|
|
899
|
+
existing.stateDiff.push(...(entry.stateDiff ?? []));
|
|
900
|
+
if (entry.balance !== undefined) {
|
|
901
|
+
existing.balance = entry.balance;
|
|
902
|
+
}
|
|
903
|
+
} else {
|
|
904
|
+
byAddress.set(key, {
|
|
905
|
+
address: entry.address,
|
|
906
|
+
balance: entry.balance,
|
|
907
|
+
stateDiff: [...(entry.stateDiff ?? [])],
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return [...byAddress.values()];
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
|
|
916
|
+
public static compressFeeHeader(feeHeader: FeeHeader): bigint {
|
|
917
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
918
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
919
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
920
|
+
|
|
921
|
+
let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
|
|
922
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
923
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
924
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
925
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
926
|
+
value |= 1n << 255n; // preheat flag
|
|
927
|
+
return value;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
931
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
|
|
932
|
+
public static computeChildFeeHeader(
|
|
933
|
+
parentFeeHeader: FeeHeader,
|
|
934
|
+
childManaUsed: bigint,
|
|
935
|
+
feeAssetPriceModifier: bigint,
|
|
936
|
+
manaTarget: bigint,
|
|
937
|
+
): FeeHeader {
|
|
938
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
939
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
940
|
+
|
|
941
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
942
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
943
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
944
|
+
|
|
945
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
946
|
+
const parentPrice =
|
|
947
|
+
parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
948
|
+
let newPrice: bigint;
|
|
949
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
950
|
+
newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
|
|
951
|
+
} else {
|
|
952
|
+
const absMod = -feeAssetPriceModifier;
|
|
953
|
+
newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
|
|
954
|
+
}
|
|
955
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
956
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
957
|
+
}
|
|
958
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
959
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
return {
|
|
963
|
+
excessMana,
|
|
964
|
+
manaUsed: childManaUsed,
|
|
965
|
+
ethPerFeeAsset: newPrice,
|
|
966
|
+
congestionCost: 0n,
|
|
967
|
+
proverCost: 0n,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
|
|
835
971
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
836
972
|
public buildInvalidateBadAttestationRequest(
|
|
837
973
|
checkpointNumber: CheckpointNumber,
|
|
@@ -880,8 +1016,8 @@ export class RollupContract {
|
|
|
880
1016
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
881
1017
|
}
|
|
882
1018
|
|
|
883
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
884
|
-
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
1019
|
+
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
|
|
1020
|
+
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
|
|
885
1021
|
}
|
|
886
1022
|
|
|
887
1023
|
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
@@ -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,
|