UNPKG

@tokamak-zk-evm/synthesizer

Version:

Tokamak zk-EVM Synthesizer - Processes Ethereum transactions into wire maps for Tokamak zk-SNARK proof generation

482 lines 21.8 kB
import { TWO_POW256, bytesToBigInt, equalsBytes } from "@synthesizer-libs/util"; import { mod, fromTwos, toTwos } from "../util.js"; import { synthesizerArith, synthesizerEnvInf, synthesizerBlkInf, prepareEXTCodePt } from "../../tokamak/core/synthesizer.js"; import { copyMemoryRegion, simulateMemoryPt } from "../../tokamak/pointers/index.js"; import { BIGINT_0, BIGINT_1 } from "@synthesizer-libs/util"; export const synthesizerHandlers = new Map([ // 0x01: ADD [0x01, async function (runState) { console.log('synthesizer add go'); const [a, b] = runState.stackPt.popN(2); const r = mod(a.value + b.value, TWO_POW256); synthesizerArith('ADD', [a.value, b.value], r, runState); }], // 0x02: MUL [0x02, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = mod(a.value * b.value, TWO_POW256); synthesizerArith('MUL', [a.value, b.value], r, runState); }], // 0x03: SUB [0x03, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = mod(a.value - b.value, TWO_POW256); synthesizerArith('SUB', [a.value, b.value], r, runState); }], // 0x04: DIV [0x04, async function (runState) { const [a, b] = runState.stackPt.popN(2); let r; if (b.value === BIGINT_0) { r = BIGINT_0; } else { r = mod(a.value / b.value, TWO_POW256); } synthesizerArith('DIV', [a.value, b.value], r, runState); }], // 0x05: SDIV [0x05, async function (runState) { const [a, b] = runState.stackPt.popN(2); let r; if (b.value === BIGINT_0) { r = BIGINT_0; } else { r = toTwos(fromTwos(a.value) / fromTwos(b.value)); } synthesizerArith('SDIV', [a.value, b.value], r, runState); }], // 0x06: MOD [0x06, async function (runState) { const [a, b] = runState.stackPt.popN(2); let r; if (b.value === BIGINT_0) { r = b.value; } else { r = mod(a.value, b.value); } synthesizerArith('MOD', [a.value, b.value], r, runState); }], // 0x07: SMOD [0x07, async function (runState) { const [a, b] = runState.stackPt.popN(2); let r; if (b.value === BIGINT_0) { r = b.value; } else { r = fromTwos(a.value) % fromTwos(b.value); } // Convert SMOD operation result to two's complement representation for EVM's 256-bit unsigned integer // Required to properly handle negative results since EVM processes all values as unsigned 256-bit integers // Using toTwos(r) to convert negative results to two's complement ensures SMOD operation works as expected synthesizerArith('SMOD', [a.value, b.value], toTwos(r), runState); }], // 0x08: ADDMOD [0x08, async function (runState) { const [a, b, c] = runState.stackPt.popN(3); let r; if (c.value === BIGINT_0) { r = BIGINT_0; } else { r = mod(a.value + b.value, c.value); } synthesizerArith('ADDMOD', [a.value, b.value, c.value], r, runState); }], // 0x09: MULMOD [0x09, async function (runState) { const [a, b, c] = runState.stackPt.popN(3); let r; if (c.value === BIGINT_0) { r = BIGINT_0; } else { r = mod(a.value * b.value, c.value); } synthesizerArith('MULMOD', [a.value, b.value, c.value], r, runState); }], // 0x0a: EXP [0x0a, async function (runState) { const [base, exponent] = runState.stackPt.popN(2); let r; if (exponent.value === BIGINT_0) { r = BIGINT_1; } else if (base.value === BIGINT_0) { r = base.value; } else { r = base.value ** exponent.value % TWO_POW256; } synthesizerArith('EXP', [base.value, exponent.value], r, runState); }], // 0x0b: SIGNEXTEND [0x0b, async function (runState) { const [k, val] = runState.stackPt.popN(2); synthesizerArith('SIGNEXTEND', [k.value, val.value], val.value, runState); }], // 0x10: LT [0x10, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = a.value < b.value ? BIGINT_1 : BIGINT_0; synthesizerArith('LT', [a.value, b.value], r, runState); }], // 0x11: GT [0x11, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = a.value > b.value ? BIGINT_1 : BIGINT_0; synthesizerArith('GT', [a.value, b.value], r, runState); }], // 0x12: SLT [0x12, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = fromTwos(a.value) < fromTwos(b.value) ? BIGINT_1 : BIGINT_0; await synthesizerArith('SLT', [a.value, b.value], r, runState); }], // 0x13: SGT [0x13, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = fromTwos(a.value) > fromTwos(b.value) ? BIGINT_1 : BIGINT_0; await synthesizerArith('SGT', [a.value, b.value], r, runState); }], // 0x14: EQ [0x14, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = a.value === b.value ? BIGINT_1 : BIGINT_0; await synthesizerArith('EQ', [a.value, b.value], r, runState); }], // 0x15: ISZERO [0x15, async function (runState) { const a = runState.stackPt.pop(); const r = a.value === BIGINT_0 ? BIGINT_1 : BIGINT_0; await synthesizerArith('ISZERO', [a.value], r, runState); }], // 0x16: AND [0x16, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = a.value & b.value; await synthesizerArith('AND', [a.value, b.value], r, runState); }], // 0x17: OR [0x17, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = a.value | b.value; await synthesizerArith('OR', [a.value, b.value], r, runState); }], // 0x18: XOR [0x18, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = a.value ^ b.value; await synthesizerArith('XOR', [a.value, b.value], r, runState); }], // 0x19: NOT [0x19, async function (runState) { const a = runState.stackPt.pop(); const r = BigInt.asUintN(256, ~a.value); await synthesizerArith('NOT', [a.value], r, runState); }], // 0x1a: BYTE [0x1a, async function (runState) { const [pos, word] = runState.stackPt.popN(2); const r = (word.value >> ((BigInt(31) - pos.value) * BigInt(8))) & BigInt(255); await synthesizerArith('BYTE', [pos.value, word.value], r, runState); }], // 0x1b: SHL [0x1b, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = (b.value << a.value) & ((BigInt(1) << BigInt(256)) - BigInt(1)); await synthesizerArith('SHL', [a.value, b.value], r, runState); }], // 0x1c: SHR [0x1c, async function (runState) { const [a, b] = runState.stackPt.popN(2); const r = b.value >> a.value; await synthesizerArith('SHR', [a.value, b.value], r, runState); }], // 0x1d: SAR [0x1d, async function (runState) { const [a, b] = runState.stackPt.popN(2); let r; const isSigned = BigInt.asIntN(256, b.value) < 0; if (a.value > 256) { r = isSigned ? ((BigInt(1) << BigInt(256)) - BigInt(1)) : BIGINT_0; } else { const c = b.value >> a.value; if (isSigned) { const shiftedOutWidth = BigInt(255) - a.value; const mask = ((TWO_POW256 - BigInt(1)) >> shiftedOutWidth) << shiftedOutWidth; r = c | mask; } else { r = c; } } await synthesizerArith('SAR', [a.value, b.value], r, runState); }], // 0x20: KECCAK256 [0x20, async function (runState) { const [offsetPt, lengthPt] = runState.stackPt.popN(2); const offset = offsetPt.value; const length = lengthPt.value; if (length !== BIGINT_0) { const offsetNum = Number(offset); const lengthNum = Number(length); let nChunks = 1; if (lengthNum > 32) { nChunks = Math.ceil(lengthNum / 32); } const chunkDataPts = []; let dataRecovered = BIGINT_0; let lengthLeft = lengthNum; for (let i = 0; i < nChunks; i++) { const _offset = offsetNum + 32 * i; const _length = lengthLeft > 32 ? 32 : lengthLeft; lengthLeft -= _length; const dataAliasInfos = runState.memoryPt.getDataAlias(_offset, _length); if (dataAliasInfos.length > 0) { chunkDataPts[i] = runState.synthesizer.placeMemoryToStack(dataAliasInfos); } else { chunkDataPts[i] = runState.synthesizer.loadAuxin(BIGINT_0); } dataRecovered += chunkDataPts[i].value << BigInt((nChunks - i - 1) * 32 * 8); } const data = runState.memory.read(Number(offset), Number(length)); if (bytesToBigInt(data) !== dataRecovered) { throw new Error(`Synthesizer: KECCAK256: Data loaded to be hashed mismatch`); } const r = runState.stack.peek(1)[0]; runState.stackPt.push(runState.synthesizer.loadKeccak(chunkDataPts, r, length)); if (runState.stack.peek(1)[0] !== runState.stackPt.peek(1)[0].value) { throw new Error(`Synthesizer: KECCAK256: Output data mismatch`); } } }], // 0x30: ADDRESS [0x30, async function (runState) { await synthesizerEnvInf('ADDRESS', runState); }], // 0x31: BALANCE [0x31, async function (runState) { const addressBigInt = runState.stackPt.pop().value; await synthesizerEnvInf('BALANCE', runState, addressBigInt); }], // 0x32: ORIGIN [0x32, async function (runState) { await synthesizerEnvInf('ORIGIN', runState); }], // 0x33: CALLER [0x33, async function (runState) { await synthesizerEnvInf('CALLER', runState); }], // 0x34: CALLVALUE [0x34, async function (runState) { await synthesizerEnvInf('CALLVALUE', runState); }], // 0x35: CALLDATALOAD [0x35, async function (runState) { const pos = runState.stackPt.pop().value; await synthesizerEnvInf('CALLDATALOAD', runState, undefined, pos); }], // 0x36: CALLDATASIZE [0x36, async function (runState) { await synthesizerEnvInf('CALLDATASIZE', runState); }], // 0x37: CALLDATACOPY [0x37, async function (runState) { const [memOffset, dataOffset, dataLength] = runState.stackPt.popN(3); if (dataLength.value !== BIGINT_0) { const calldataMemoryPts = runState.interpreter._env.callMemoryPts; let memoryPtsToCopy = []; if (calldataMemoryPts.length > 0) { memoryPtsToCopy = copyMemoryRegion(runState, dataOffset.value, dataLength.value, calldataMemoryPts, memOffset.value); } else { const data = runState.interpreter.getCallData().subarray(Number(dataOffset.value), Number(dataOffset.value + dataLength.value)); const entryToCopy = { memOffset: Number(memOffset.value), containerSize: Number(dataLength.value), dataPt: runState.synthesizer.loadEnvInf(runState.env.address.toString(), 'Calldata', bytesToBigInt(data), Number(dataOffset.value), Number(dataLength.value)) }; memoryPtsToCopy.push(entryToCopy); } for (const entry of memoryPtsToCopy) { runState.memoryPt.write(entry.memOffset, entry.containerSize, entry.dataPt); } const _outData = runState.memoryPt.viewMemory(Number(memOffset.value), Number(dataLength.value)); const outData = runState.memory.read(Number(memOffset.value), Number(dataLength.value)); if (!equalsBytes(_outData, outData)) { throw new Error(`Synthesizer: CALLDATACOPY: Output data mismatch`); } } }], // 0x38: CODESIZE [0x38, async function (runState) { await synthesizerEnvInf('CODESIZE', runState); }], // 0x39: CODECOPY [0x39, async function (runState) { const [memOffset, codeOffset, dataLength] = runState.stackPt.popN(3); if (dataLength.value !== BIGINT_0) { const data = runState.interpreter.getCode().subarray(Number(codeOffset.value), Number(codeOffset.value + dataLength.value)); const dataBigint = bytesToBigInt(data); const dataPt = runState.synthesizer.loadEnvInf(runState.env.address.toString(), 'Code', dataBigint, Number(codeOffset.value), Number(dataLength.value)); runState.memoryPt.write(Number(memOffset.value), Number(dataLength.value), dataPt); const _outData = runState.memoryPt.viewMemory(Number(memOffset.value), Number(dataLength.value)); const outData = runState.memory.read(Number(memOffset.value), Number(dataLength.value)); if (!equalsBytes(_outData, outData)) { throw new Error(`Synthesizer: CODECOPY: Output data mismatch`); } } }], // 0x3b: EXTCODESIZE [0x3b, async function (runState) { const addressBigInt = runState.stackPt.pop().value; await synthesizerEnvInf('EXTCODESIZE', runState, addressBigInt); }], // 0x3c: EXTCODECOPY [0x3c, async function (runState) { const [addressPt, memOffsetPt, codeOffsetPt, dataLengthPt] = runState.stackPt.popN(4); if (dataLengthPt.value !== BIGINT_0) { const dataPt = await prepareEXTCodePt(runState, addressPt.value, codeOffsetPt.value, dataLengthPt.value); runState.memoryPt.write(Number(memOffsetPt.value), Number(dataLengthPt.value), dataPt); const _outData = runState.memoryPt.viewMemory(Number(memOffsetPt.value), Number(dataLengthPt.value)); const outData = runState.memory.read(Number(memOffsetPt.value), Number(dataLengthPt.value)); if (!equalsBytes(_outData, outData)) { throw new Error(`Synthesizer: EXTCODECOPY: Output data mismatch`); } } }], // 0x3f: EXTCODEHASH [0x3f, async function (runState) { const addressBigInt = runState.stackPt.pop().value; await synthesizerEnvInf('EXTCODEHASH', runState, addressBigInt); }], // 0x3d: RETURNDATASIZE [0x3d, async function (runState) { await synthesizerEnvInf('RETURNDATASIZE', runState); }], // 0x3e: RETURNDATACOPY [0x3e, async function (runState) { const [memOffset, returnDataOffset, dataLength] = runState.stackPt.popN(3); if (dataLength.value !== BIGINT_0) { const copiedMemoryPts = copyMemoryRegion(runState, returnDataOffset.value, dataLength.value, runState.returnMemoryPts, memOffset.value); for (const entry of copiedMemoryPts) { runState.memoryPt.write(entry.memOffset, entry.containerSize, entry.dataPt); } const _outData = runState.memoryPt.viewMemory(Number(memOffset.value), Number(dataLength.value)); const outData = runState.memory.read(Number(memOffset.value), Number(dataLength.value)); if (!equalsBytes(_outData, outData)) { throw new Error(`Synthesizer: RETURNDATACOPY: Output data mismatch`); } } }], // 0x3a: GASPRICE [0x3a, async function (runState) { await synthesizerEnvInf('GASPRICE', runState); }], // 0x40: BLOCKHASH [0x40, async function (runState) { const number = runState.stackPt.pop().value; await synthesizerBlkInf('BLOCKHASH', runState, number); }], // 0x41: COINBASE [0x41, async function (runState) { await synthesizerBlkInf('COINBASE', runState); }], // 0x42: TIMESTAMP [0x42, async function (runState) { await synthesizerBlkInf('TIMESTAMP', runState); }], // 0x43: NUMBER [0x43, async function (runState) { await synthesizerBlkInf('NUMBER', runState); }], // 0x44: DIFFICULTY (PREVRANDAO) [0x44, async function (runState) { await synthesizerBlkInf('DIFFICULTY', runState); }], // 0x45: GASLIMIT [0x45, async function (runState) { await synthesizerBlkInf('GASLIMIT', runState); }], // 0x46: CHAINID [0x46, async function (runState) { await synthesizerBlkInf('CHAINID', runState); }], // 0x47: SELFBALANCE [0x47, async function (runState) { await synthesizerBlkInf('SELFBALANCE', runState); }], // 0x48: BASEFEE [0x48, async function (runState) { await synthesizerBlkInf('BASEFEE', runState); }], // 0x5e: MCOPY [0x5e, async function (runState) { const [dst, src, length] = runState.stackPt.popN(3); const copiedMemoryPts = copyMemoryRegion(runState, src.value, length.value, undefined, dst.value); for (const entry of copiedMemoryPts) { runState.memoryPt.write(entry.memOffset, entry.containerSize, entry.dataPt); } const _outData = runState.memoryPt.viewMemory(Number(dst.value), Number(length.value)); const outData = runState.memory.read(Number(dst.value), Number(length.value)); if (!equalsBytes(_outData, outData)) { throw new Error(`Synthesizer: MCOPY: Output data mismatch`); } }], // 0x5f: PUSH0 [0x5f, async function (runState) { const dataPt = runState.synthesizer.loadPUSH(runState.env.address.toString(), runState.programCounterPrev, BIGINT_0, 1); runState.stackPt.push(dataPt); if (runState.stackPt.peek(1)[0].value !== runState.stack.peek(1)[0]) { throw new Error(`Synthesizer: PUSH0: Output data mismatch`); } }], // 0x60: PUSH [0x60, async function (runState) { const value = runState.stack.peek(1)[0]; const numToPush = runState.opCode - 0x5f; const dataPt = runState.synthesizer.loadPUSH(runState.env.address.toString(), runState.programCounterPrev, value, numToPush); runState.stackPt.push(dataPt); if (runState.stackPt.peek(1)[0].value !== runState.stack.peek(1)[0]) { throw new Error(`Synthesizer: PUSH${numToPush}: Output data mismatch`); } }], // 0x80: DUP [0x80, async function (runState) { const stackPos = runState.opCode - 0x7f; runState.stackPt.dup(stackPos); }], // 0x90: SWAP [0x90, async function (runState) { const stackPos = runState.opCode - 0x8f; runState.stackPt.swap(stackPos); }], // 0xa0: LOG [0xa0, async function (runState) { const [memOffsetPt, memLengthPt] = runState.stackPt.popN(2); const topicsCount = runState.opCode - 0xa0; const topicPts = runState.stackPt.popN(topicsCount); const dataAlias = runState.memoryPt.getDataAlias(Number(memOffsetPt.value), Number(memLengthPt.value)); const dataPt = runState.synthesizer.placeMemoryToStack(dataAlias); runState.synthesizer.storeLog(dataPt, topicPts); }], // 0xf3: RETURN [0xf3, async function (runState) { const [offset, length] = runState.stackPt.popN(2); const returnMemoryPts = copyMemoryRegion(runState, offset.value, length.value); runState.interpreter.finishPt(returnMemoryPts); const simMemoryPt = simulateMemoryPt(returnMemoryPts); const _returnData = simMemoryPt.viewMemory(0, Number(length.value)); const returnData = runState.memory.read(Number(offset.value), Number(length.value)); if (!equalsBytes(_returnData, returnData)) { throw new Error(`Synthesizer: RETURN: Output data mismatch`); } }], ]); //# sourceMappingURL=handlers.js.map