Saltar al contenido
TrautsLablab

Fixture determinista

Rollup de juguete

Seguir el flujo de un rollup: ejecutar un lote, publicar digest del lote y state root, y que un verificador re-ejecute para aceptar o rechazar la afirmación.

Duración
15–25 min
Nivel
Introductorio L2
Pasos
1
Versión
3.29.0-alpha.1

Modelo mental

El rollup ejecuta fuera y publica compromisos. Lo que da seguridad no es el hash en sí, sino que alguien pueda recalcular el estado a partir de los datos publicados y rechazar un state root falso.

  1. 1Estado inicial
  2. 2Lote
  3. 3Compromisos
  4. 4Verificación
  5. 5Manipulación

Antes de empezar

  • Node.js 20 o superior. No necesita Foundry.

Pasos

  1. Paso 1Ejecutar y verificar el lote

    Ver el estado antes y después, los compromisos publicados y el resultado del verificador.

    Simulación
    node tools/labs/ethereum-rollup-mock-lab/simulate.mjs

    Comprueba

    • — before es alice 100, bob 25, carol 0 y after es alice 75, bob 35, carol 15; la suma sigue en 125.
    • — batchDigest empieza con c9c2fda4 y stateRoot con f26f371b (el resultado es determinista).
    • — verified es true: el verificador re-ejecutó el lote y obtuvo el mismo state root.
    • — tamperRejected es true: un state root con carol = 1000 se rechaza aunque su mockProof sea consistente.
    • — El script termina con código 0.

    Evidencia esperada

    JSON completo del reporte.

El código del laboratorio

Estos son los archivos que ejecutan los pasos, tal como están en el repositorio .

tools/labs/ethereum-rollup-mock-lab/runbook.md
# Rollup mock lab

Version: `2.0.44-alpha.3`

Run `node tools/labs/ethereum-rollup-mock-lab/simulate.mjs`. Record state before/after, transaction batch digest, state root and proof verification result.

The verifier re-executes the published batch from the previous state and only accepts the claim if the recomputed state root matches (`verified: true`). A claim with a forged state root is rejected (`tamperRejected: true`).

The proof is intentionally a commitment mock. It demonstrates data flow and invariants, not cryptographic soundness. Only Node.js 20+ is required.
tools/labs/ethereum-rollup-mock-lab/simulate.mjs
import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';

const transactions = JSON.parse(readFileSync(new URL('./transactions.json', import.meta.url), 'utf8'));
const hash = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
const genesis = { alice: 100, bob: 25, carol: 0 };
const domain = 'trautslab-rollup-mock-v1';

function execute(initial, batch) {
  const state = new Map(Object.entries(initial));
  for (const tx of batch) {
    if ((state.get(tx.from) ?? 0) < tx.amount) throw new Error(`insufficient balance: ${tx.from}`);
    state.set(tx.from, state.get(tx.from) - tx.amount);
    state.set(tx.to, (state.get(tx.to) ?? 0) + tx.amount);
  }
  return state;
}
const rootOf = (state) => hash([...state.entries()].sort());
const proofOf = (batchDigest, stateRoot) => hash({ batchDigest, stateRoot, domain });

// Sequencer: executes the batch and publishes (batchDigest, stateRoot, mockProof).
const state = execute(genesis, transactions);
const claim = { batchDigest: hash(transactions), stateRoot: rootOf(state) };
claim.mockProof = proofOf(claim.batchDigest, claim.stateRoot);

// Verifier: re-executes the published batch from the previous state and checks the claim.
function verify(prev, batch, candidate) {
  const recomputedRoot = rootOf(execute(prev, batch));
  return (
    candidate.batchDigest === hash(batch) &&
    candidate.stateRoot === recomputedRoot &&
    candidate.mockProof === proofOf(candidate.batchDigest, candidate.stateRoot)
  );
}
const verified = verify(genesis, transactions, claim);
const tamperedRoot = hash([...execute(genesis, transactions).set('carol', 1000).entries()].sort());
const tampered = { ...claim, stateRoot: tamperedRoot, mockProof: proofOf(claim.batchDigest, tamperedRoot) };
const tamperRejected = !verify(genesis, transactions, tampered);

const report = {
  before: genesis,
  transactions,
  after: Object.fromEntries(state),
  batchDigest: claim.batchDigest,
  stateRoot: claim.stateRoot,
  mockProof: claim.mockProof,
  verified,
  tamperRejected,
  warning: 'Mock proof: not a validity or fraud proof.',
};
console.log(JSON.stringify(report, null, 2));
if (!verified || !tamperRejected || [...state.values()].reduce((sum, value) => sum + value, 0) !== 125) process.exit(1);
tools/labs/ethereum-rollup-mock-lab/transactions.json
[
  { "from": "alice", "to": "bob", "amount": 20 },
  { "from": "bob", "to": "carol", "amount": 10 },
  { "from": "alice", "to": "carol", "amount": 5 }
]

Criterios de aceptación

  • El script termina con código 0.
  • Puedes explicar por qué mockProof por sí solo no prueba nada.
  • Puedes explicar qué necesita el verificador además del state root.

Limpieza

Limpiar: No se genera estado persistente; descarta cambios hechos a transactions.json.

Si algo falla

Síntoma, causa probable y cómo corregirlo
SíntomaCausaQué hacer
Error "insufficient balance"transactions.json pide gastar más de lo que tiene una cuenta.Ajusta los montos o el orden del lote.
batchDigest o stateRoot distintos a los esperadosSe modificó transactions.json o el estado inicial.Es correcto si cambiaste el lote; anota los valores nuevos en la evidencia.