-
Notifications
You must be signed in to change notification settings - Fork 333
/
Copy pathcheat_codes.ts
249 lines (223 loc) · 8.52 KB
/
cheat_codes.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
import { type EpochProofClaim, type Note, type PXE } from '@aztec/circuit-types';
import { type AztecAddress, EthAddress, Fr } from '@aztec/circuits.js';
import { deriveStorageSlotInMap } from '@aztec/circuits.js/hash';
import { EthCheatCodes, type L1ContractAddresses } from '@aztec/ethereum';
import { createDebugLogger } from '@aztec/foundation/log';
import { RollupAbi } from '@aztec/l1-artifacts';
import {
type GetContractReturnType,
type Hex,
type PublicClient,
type WalletClient,
createWalletClient,
getContract,
http,
publicActions,
} from 'viem';
import { foundry } from 'viem/chains';
/**
* A class that provides utility functions for interacting with the chain.
*/
export class CheatCodes {
constructor(
/** Cheat codes for L1.*/
public eth: EthCheatCodes,
/** Cheat codes for Aztec L2. */
public aztec: AztecCheatCodes,
/** Cheat codes for the Aztec Rollup contract on L1. */
public rollup: RollupCheatCodes,
) {}
static async create(rpcUrl: string, pxe: PXE): Promise<CheatCodes> {
const ethCheatCodes = new EthCheatCodes(rpcUrl);
const aztecCheatCodes = new AztecCheatCodes(pxe, ethCheatCodes);
const rollupCheatCodes = new RollupCheatCodes(
ethCheatCodes,
await pxe.getNodeInfo().then(n => n.l1ContractAddresses),
);
return new CheatCodes(ethCheatCodes, aztecCheatCodes, rollupCheatCodes);
}
static createRollup(rpcUrl: string, addresses: Pick<L1ContractAddresses, 'rollupAddress'>): RollupCheatCodes {
const ethCheatCodes = new EthCheatCodes(rpcUrl);
return new RollupCheatCodes(ethCheatCodes, addresses);
}
}
/** Cheat codes for the L1 rollup contract. */
export class RollupCheatCodes {
private client: WalletClient & PublicClient;
private rollup: GetContractReturnType<typeof RollupAbi, WalletClient>;
private logger = createDebugLogger('aztec:js:cheat_codes');
constructor(private ethCheatCodes: EthCheatCodes, addresses: Pick<L1ContractAddresses, 'rollupAddress'>) {
this.client = createWalletClient({ chain: foundry, transport: http(ethCheatCodes.rpcUrl) }).extend(publicActions);
this.rollup = getContract({
abi: RollupAbi,
address: addresses.rollupAddress.toString(),
client: this.client,
});
}
/** Returns the current slot */
public async getSlot() {
const ts = BigInt((await this.client.getBlock()).timestamp);
return await this.rollup.read.getSlotAt([ts]);
}
/** Returns the current epoch */
public async getEpoch() {
const slotNumber = await this.getSlot();
return await this.rollup.read.getEpochAtSlot([slotNumber]);
}
/**
* Returns the pending and proven chain tips
* @returns The pending and proven chain tips
*/
public async getTips(): Promise<{
/** The pending chain tip */ pending: bigint;
/** The proven chain tip */ proven: bigint;
}> {
const [pending, proven] = await this.rollup.read.tips();
return { pending, proven };
}
/** Fetches the epoch and slot duration config from the rollup contract */
public async getConfig(): Promise<{
/** Epoch duration */ epochDuration: bigint;
/** Slot duration */ slotDuration: bigint;
}> {
const [epochDuration, slotDuration] = await Promise.all([
this.rollup.read.EPOCH_DURATION(),
this.rollup.read.SLOT_DURATION(),
]);
return { epochDuration, slotDuration };
}
/** Warps time in L1 until the next epoch */
public async advanceToNextEpoch() {
const slot = await this.getSlot();
const { epochDuration, slotDuration } = await this.getConfig();
const slotsUntilNextEpoch = epochDuration - (slot % epochDuration) + 1n;
const timeToNextEpoch = slotsUntilNextEpoch * slotDuration;
const l1Timestamp = BigInt((await this.client.getBlock()).timestamp);
await this.ethCheatCodes.warp(Number(l1Timestamp + timeToNextEpoch));
this.logger.verbose(`Advanced to next epoch`);
}
/**
* Warps time in L1 equivalent to however many slots.
* @param howMany - The number of slots to advance.
*/
public async advanceSlots(howMany: number) {
const l1Timestamp = (await this.client.getBlock()).timestamp;
const slotDuration = await this.rollup.read.SLOT_DURATION();
const timeToWarp = BigInt(howMany) * slotDuration;
await this.ethCheatCodes.warp(l1Timestamp + timeToWarp);
const [slot, epoch] = await Promise.all([this.getSlot(), this.getEpoch()]);
this.logger.verbose(`Advanced ${howMany} slots up to slot ${slot} in epoch ${epoch}`);
}
/** Returns the current proof claim (if any) */
public async getProofClaim(): Promise<EpochProofClaim | undefined> {
// REFACTOR: This code is duplicated from l1-publisher
const [epochToProve, basisPointFee, bondAmount, bondProviderHex, proposerClaimantHex] =
await this.rollup.read.proofClaim();
const bondProvider = EthAddress.fromString(bondProviderHex);
const proposerClaimant = EthAddress.fromString(proposerClaimantHex);
if (bondProvider.isZero() && proposerClaimant.isZero() && epochToProve === 0n) {
return undefined;
}
return {
epochToProve,
basisPointFee,
bondAmount,
bondProvider,
proposerClaimant,
};
}
/**
* Marks the specified block (or latest if none) as proven
* @param maybeBlockNumber - The block number to mark as proven (defaults to latest pending)
*/
public async markAsProven(maybeBlockNumber?: number | bigint) {
const blockNumber = maybeBlockNumber
? BigInt(maybeBlockNumber)
: await this.rollup.read.tips().then(([pending]) => pending);
await this.asOwner(async account => {
await this.rollup.write.setAssumeProvenThroughBlockNumber([blockNumber], { account, chain: this.client.chain });
this.logger.verbose(`Marked ${blockNumber} as proven`);
});
}
/**
* Executes an action impersonated as the owner of the Rollup contract.
* @param action - The action to execute
*/
public async asOwner(
action: (owner: Hex, rollup: GetContractReturnType<typeof RollupAbi, WalletClient>) => Promise<void>,
) {
const owner = await this.rollup.read.owner();
await this.ethCheatCodes.startImpersonating(owner);
await action(owner, this.rollup);
await this.ethCheatCodes.stopImpersonating(owner);
}
}
/**
* A class that provides utility functions for interacting with the aztec chain.
*/
export class AztecCheatCodes {
constructor(
/**
* The PXE Service to use for interacting with the chain
*/
public pxe: PXE,
/**
* The eth cheat codes.
*/
public eth: EthCheatCodes,
/**
* The logger to use for the aztec cheatcodes
*/
public logger = createDebugLogger('aztec:cheat_codes:aztec'),
) {}
/**
* Computes the slot value for a given map and key.
* @param mapSlot - The slot of the map (specified in Aztec.nr contract)
* @param key - The key to lookup in the map
* @returns The storage slot of the value in the map
*/
public computeSlotInMap(mapSlot: Fr | bigint, key: Fr | bigint | AztecAddress): Fr {
const keyFr = typeof key === 'bigint' ? new Fr(key) : key.toField();
return deriveStorageSlotInMap(mapSlot, keyFr);
}
/**
* Get the current blocknumber
* @returns The current block number
*/
public async blockNumber(): Promise<number> {
return await this.pxe.getBlockNumber();
}
/**
* Get the current timestamp
* @returns The current timestamp
*/
public async timestamp(): Promise<number> {
const res = await this.pxe.getBlock(await this.blockNumber());
return res?.header.globalVariables.timestamp.toNumber() ?? 0;
}
/**
* Loads the value stored at the given slot in the public storage of the given contract.
* @param who - The address of the contract
* @param slot - The storage slot to lookup
* @returns The value stored at the given slot
*/
public async loadPublic(who: AztecAddress, slot: Fr | bigint): Promise<Fr> {
const storageValue = await this.pxe.getPublicStorageAt(who, new Fr(slot));
return storageValue;
}
/**
* Loads the value stored at the given slot in the private storage of the given contract.
* @param contract - The address of the contract
* @param owner - The owner for whom the notes are encrypted
* @param slot - The storage slot to lookup
* @returns The notes stored at the given slot
*/
public async loadPrivate(owner: AztecAddress, contract: AztecAddress, slot: Fr | bigint): Promise<Note[]> {
const extendedNotes = await this.pxe.getIncomingNotes({
owner,
contractAddress: contract,
storageSlot: new Fr(slot),
});
return extendedNotes.map(extendedNote => extendedNote.note);
}
}