Skip to content

Commit 496b105

Browse files
committed
bugc: add block.prevrandao
1 parent 6c81c10 commit 496b105

14 files changed

Lines changed: 98 additions & 10 deletions

File tree

‎packages/bugc-react/CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ specification itself are tracked in the root
77

88
## Unreleased
99

10+
### Added
11+
12+
- The CFG view shows a `block.prevrandao` read as `block.prevrandao`
13+
([#PR]).
14+
1015
## 0.1.0-preview.1 — 2026-10-03
1116

1217
Updated `@ethdebug/bugc` to `0.1.0-preview.1`.

‎packages/bugc-react/src/components/CfgView.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ function CfgViewContent({ ir }: CfgViewProps): JSX.Element {
349349
return `${envInst.dest} = block.timestamp`;
350350
case "block_number":
351351
return `${envInst.dest} = block.number`;
352+
case "block_prevrandao":
353+
return `${envInst.dest} = block.prevrandao`;
352354
default:
353355
return `${envInst.dest} = ${envInst.op}`;
354356
}

‎packages/bugc/CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ support. Changes to the specification itself are tracked in the root
99

1010
### Added
1111

12+
- `block.prevrandao`, the previous block's RANDAO value (EIP-4399), as a
13+
`uint256`. It compiles to the EVM's `PREVRANDAO` (`0x44`) ([#PR]).
1214
- The `%` operator, with the precedence of `*` and `/`. It compiles to
1315
the EVM's `MOD` (`SMOD` for signed operands), so `x % 0` is `0`, as
1416
`x / 0` is ([#321]).

‎packages/bugc/src/ast/spec.ts‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,8 @@ export namespace Expression {
14391439
| Expression.Special.MsgValue
14401440
| Expression.Special.MsgData
14411441
| Expression.Special.BlockTimestamp
1442-
| Expression.Special.BlockNumber;
1442+
| Expression.Special.BlockNumber
1443+
| Expression.Special.BlockPrevrandao;
14431444

14441445
export const isSpecial = (
14451446
expression: Expression.Base,
@@ -1451,6 +1452,7 @@ export namespace Expression {
14511452
Expression.Special.isMsgSender,
14521453
Expression.Special.isBlockNumber,
14531454
Expression.Special.isBlockTimestamp,
1455+
Expression.Special.isBlockPrevrandao,
14541456
].some((guard) => guard(expression));
14551457

14561458
export namespace Special {
@@ -1532,6 +1534,26 @@ export namespace Expression {
15321534
export function blockNumber(id: Id, loc?: SourceLocation): BlockNumber {
15331535
return { id, kind: "expression:special:block.number", loc: loc ?? null };
15341536
}
1537+
1538+
export interface BlockPrevrandao extends Base {
1539+
kind: "expression:special:block.prevrandao";
1540+
}
1541+
1542+
export const isBlockPrevrandao = (
1543+
expression: Expression.Base,
1544+
): expression is BlockPrevrandao =>
1545+
expression.kind === "expression:special:block.prevrandao";
1546+
1547+
export function blockPrevrandao(
1548+
id: Id,
1549+
loc?: SourceLocation,
1550+
): BlockPrevrandao {
1551+
return {
1552+
id,
1553+
kind: "expression:special:block.prevrandao",
1554+
loc: loc ?? null,
1555+
};
1556+
}
15351557
}
15361558
}
15371559

‎packages/bugc/src/ast/visitor.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ export function visitDetailed<N extends Ast.Node, T, C = never>(
201201
case "expression:special:msg.data":
202202
case "expression:special:block.timestamp":
203203
case "expression:special:block.number":
204+
case "expression:special:block.prevrandao":
204205
return visitor.expressionSpecial(node as Ast.Expression.Special, context);
205206

206207
default:

‎packages/bugc/src/evmgen/behavioral.test.ts‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { describe, it, expect } from "vitest";
33
import { keccak256 } from "ethereum-cryptography/keccak";
44
import { bytesToHex } from "ethereum-cryptography/utils";
55

6+
import { compile } from "#compiler";
67
import { executeProgram } from "#test/evm/behavioral";
78

89
describe("behavioral tests", () => {
@@ -1289,6 +1290,38 @@ code {
12891290
}
12901291
});
12911292

1293+
describe("block.prevrandao", () => {
1294+
const source = `name Randao;
1295+
storage { [0] out: uint256; }
1296+
code {
1297+
out = block.prevrandao + 1;
1298+
}`;
1299+
1300+
for (const level of [0, 1, 2, 3] as const) {
1301+
it(`should read PREVRANDAO (level ${level})`, async () => {
1302+
const compiled = await compile({
1303+
to: "bytecode",
1304+
source,
1305+
optimizer: { level },
1306+
});
1307+
if (!compiled.success) throw new Error("compile failed");
1308+
const mnemonics =
1309+
compiled.value.bytecode.runtimeProgram.instructions.map(
1310+
(instruction) => instruction.operation?.mnemonic,
1311+
);
1312+
expect(mnemonics).toContain("PREVRANDAO");
1313+
1314+
// @ethdebug/evm runs each call in a block whose prevrandao is 0
1315+
const result = await executeProgram(source, {
1316+
calldata: "",
1317+
optimizationLevel: level,
1318+
});
1319+
expect(result.callSuccess).toBe(true);
1320+
expect(await result.getStorage(0n)).toBe(1n);
1321+
});
1322+
}
1323+
});
1324+
12921325
describe("modulo", () => {
12931326
const program = (expr: string) => `name Modulo;
12941327

‎packages/bugc/src/evmgen/generation/instructions/env.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { type Transition, pipe, operations } from "#evmgen/operations";
66

77
import { storeValueIfNeeded } from "../values/index.js";
88

9-
const { CALLER, CALLVALUE, PUSH0, TIMESTAMP, NUMBER } = operations;
9+
const { CALLER, CALLVALUE, PUSH0, TIMESTAMP, NUMBER, PREVRANDAO } = operations;
1010

1111
/**
1212
* Generate code for environment operations
@@ -26,6 +26,7 @@ export function generateEnvOp<S extends Stack>(
2626
msg_data: PUSH0({ debug }), // Returns calldata offset (0)
2727
block_timestamp: TIMESTAMP({ debug }),
2828
block_number: NUMBER({ debug }),
29+
block_prevrandao: PREVRANDAO({ debug }),
2930
};
3031

3132
return pipe<S>()

‎packages/bugc/src/ir/analysis/validator.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export class Validator {
212212
"msg_value",
213213
"block_number",
214214
"block_timestamp",
215+
"block_prevrandao",
215216
];
216217
if (!validOps.includes(inst.op)) {
217218
this.error(`Invalid env operation '${inst.op}'`);

‎packages/bugc/src/ir/spec/instruction.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,8 @@ export namespace Instruction {
383383
| "msg_value"
384384
| "msg_data"
385385
| "block_number"
386-
| "block_timestamp";
386+
| "block_timestamp"
387+
| "block_prevrandao";
387388

388389
dest: string;
389390
}

‎packages/bugc/src/irgen/generate/expressions/expression.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export function* buildExpression(
5050
case "expression:special:msg.data":
5151
case "expression:special:block.timestamp":
5252
case "expression:special:block.number":
53+
case "expression:special:block.prevrandao":
5354
return yield* buildSpecial(expr as Ast.Expression.Special);
5455
case "expression:array":
5556
return yield* buildArray(expr as Ast.Expression.Array, context);

0 commit comments

Comments
 (0)