diff --git a/packages/evm/examples/simple.ts b/packages/evm/examples/simple.ts index 5fec489591..3fc8cd6920 100644 --- a/packages/evm/examples/simple.ts +++ b/packages/evm/examples/simple.ts @@ -1,10 +1,12 @@ +// DEBUG=ethjs,evm:*,evm:*:* tsx simple.ts + import { hexToBytes } from '@ethereumjs/util' import { createEVM } from '../src/constructors.js' const main = async () => { const evm = await createEVM() - const res = await evm.runCode({ code: hexToBytes('0x6001600201') }) // PUSH1 01 -- simple bytecode to push 1 onto the stack + const res = await evm.runCode({ code: hexToBytes('0x605A60050A610120526101406000F3') }) // PUSH1 01 -- simple bytecode to push 1 onto the stack console.log(res.executionGasUsed) // 3n } diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index 5d2c438548..7bbaf8614c 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -538,7 +538,7 @@ export class Interpreter { console.log(`"memory": ${stringMemory.slice(0, 256)}\n`) console.log(`"stackPt": ${JSON.stringify(this._runState.stackPt.getStack(), arrToStr, 2)}\n`) console.log(`"memoryPt": ${JSON.stringify(stringMemoryPt, null, 1)}\n`) - //console.log(`"placements": ${JSON.stringify(stringPlacements, null, 1)}`) + console.log(`"placements": ${JSON.stringify(stringPlacements, null, 1)}`) if (!(name in this.opDebuggers)) { this.opDebuggers[name] = debugDefault(`evm:ops:${name}`) } diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 1d969b5a35..5b57d0fbed 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -51,7 +51,7 @@ import { import type { RunState } from '../interpreter.js' import type { MemoryPtEntry, MemoryPts } from '../tokamak/memoryPt.js' -import type { DataPt } from '../tokamak/synthesizer.js' +import type { DataPt } from '../tokamak/type.js' import type { Common } from '@ethereumjs/common' export interface SyncOpHandler { @@ -286,8 +286,8 @@ export const handlers: Map = new Map([ // For Synthesizer // const inPts = runState.stackPt.popN(2) - const outPts = runState.synthesizer.placeArith('EXP', inPts) - runState.stackPt.push(outPts[0]) + const outPt = runState.synthesizer.placeEXP(inPts) + runState.stackPt.push(outPt) }, ], // 0x0b: SIGNEXTEND diff --git a/packages/evm/src/tokamak/arithmetic.ts b/packages/evm/src/tokamak/arithmetic.ts index 521808bae5..1a1b216c08 100644 --- a/packages/evm/src/tokamak/arithmetic.ts +++ b/packages/evm/src/tokamak/arithmetic.ts @@ -27,8 +27,10 @@ export type ArithmeticOperator = | 'SAR' | 'BYTE' | 'SIGNEXTEND' + | 'DecToBit' + | 'SubEXP' -export type ArithmeticFunction = (...args: bigint[]) => bigint +export type ArithmeticFunction = (...args: bigint[]) => bigint | bigint[] /** * Synthesizer 산술 연산을 처리하는 유틸리티 클래스 @@ -90,6 +92,7 @@ export class ArithmeticOperations { } /** + * @deprecated * 지수 연산 */ static exp(base: bigint, exponent: bigint): bigint { @@ -206,6 +209,28 @@ export class ArithmeticOperations { return value & mask } } + + /** + * Decimal to Bit + */ + static decToBit(dec: bigint): bigint[] { + const binaryString = dec.toString(2) + const paddedBinaryString = binaryString.padStart(256, '0') + const bits = Array.from(paddedBinaryString, bit => BigInt(bit)) + return bits + } + + /** + * Subroutine for EXP + */ + static subEXP(c: bigint, a: bigint, b: bigint): bigint[] { + if (!(b === 0n || b === 1n) ){ + throw new Error(`Synthesizer: ArithmeticOperations: subEXP: b is not binary`) + } + const aOut = a ** 2n + const cOut = c * ( b * a + (1n - b) ) // <=> c * (b ? aOut : 1) + return [cOut, aOut] + } } // 연산자와 함수 매핑 @@ -235,4 +260,6 @@ export const OPERATION_MAPPING: Record = SAR: ArithmeticOperations.sar, BYTE: ArithmeticOperations.byte, SIGNEXTEND: ArithmeticOperations.signextend, + DecToBit: ArithmeticOperations.decToBit, + SubEXP: ArithmeticOperations.subEXP, } as const diff --git a/packages/evm/src/tokamak/subcircuit_info.ts b/packages/evm/src/tokamak/subcircuit_info.ts index 66fa5411e8..b217607d93 100644 --- a/packages/evm/src/tokamak/subcircuit_info.ts +++ b/packages/evm/src/tokamak/subcircuit_info.ts @@ -246,4 +246,20 @@ export const subcircuits = [ Out_idx: [1, 32], In_idx: [33, 1], }, + { + id: 30, + opcode: '', + name: 'DecToBit', + Nwires: 0, + Out_idx: [1, 256], + In_idx: [257, 1], + }, + { + id: 31, + opcode: '', + name: 'SubEXP', + Nwires: 0, + Out_idx: [1, 2], + In_idx: [3, 3], + }, ] diff --git a/packages/evm/src/tokamak/synthesizer.ts b/packages/evm/src/tokamak/synthesizer.ts index 67b8262cd2..6732591065 100644 --- a/packages/evm/src/tokamak/synthesizer.ts +++ b/packages/evm/src/tokamak/synthesizer.ts @@ -9,6 +9,7 @@ import type { RunState } from '../interpreter.js' import type { ArithmeticOperator } from './arithmetic.js' import type { DataAliasInfoEntry, DataAliasInfos } from './memoryPt.js' import type { DataPt, Placements } from './type.js' +import { BIGINT_0, BIGINT_1 } from '@ethereumjs/util' /** * @TODO @@ -331,11 +332,14 @@ export class Synthesizer { } // 기본값(2)과 다른 입력 개수를 가진 연산들만 정의 + // To do: subcircuit_info.ts 에서 입출력 갯수 가져오기 private static readonly REQUIRED_INPUTS: Partial> = { ADDMOD: 3, MULMOD: 3, ISZERO: 1, NOT: 1, + DecToBit: 1, + SubEXP: 3, } as const private validateOperation(name: ArithmeticOperator, inPts: DataPt[]): void { @@ -345,18 +349,32 @@ export class Synthesizer { SynthesizerValidator.validateInputs(inPts) } - private executeOperation(name: ArithmeticOperator, values: bigint[]): bigint { + private executeOperation(name: ArithmeticOperator, values: bigint[]): bigint | bigint[] { const operation = OPERATION_MAPPING[name] return operation(...values) } - private createOutputPoint(value: bigint): DataPt { - return this.createNewDataPoint({ - sourceId: this.placementIndex, - sourceIndex: 0, - value, - sourceSize: 32, - }) + private createOutputPoints(value: bigint | bigint[]): DataPt[] { + if (Array.isArray(value)) { + const outPts: DataPt[] = [] + for (let ind = 0; ind < value.length; ind++){ + outPts.push( this.createNewDataPoint({ + sourceId: this.placementIndex, + sourceIndex: ind, + value: value[ind], + sourceSize: 32, + })) + } + return outPts + + } else { + return [this.createNewDataPoint({ + sourceId: this.placementIndex, + sourceIndex: 0, + value, + sourceSize: 32, + })] + } } private handleBinaryOp(name: ArithmeticOperator, inPts: DataPt[]): DataPt[] { @@ -369,7 +387,7 @@ export class Synthesizer { const outValue = this.executeOperation(name, values) // 3. 출력값 생성 - const outPts = [this.createOutputPoint(outValue)] + const outPts = this.createOutputPoints(outValue) // 4. 배치 추가 this._place(name, inPts, outPts) @@ -401,6 +419,32 @@ export class Synthesizer { return this.handleBinaryOp(name, inPts) } + public placeEXP(inPts: DataPt[]): DataPt { + SynthesizerValidator.validateSubcircuitName('EXP', this.subcircuitNames) + // a^b + const aPt = inPts[0] + const bPt = inPts[1] + const bNum = Number(bPt.value) + const k = Math.floor(Math.log2(bNum)) + 1 //bit length of b + + const bitifyOutPts = this.placeArith('DecToBit', [bPt]).reverse() + // LSB at index 0 + + const chPts: DataPt[] = [] + const ahPts: DataPt[] = [] + chPts.push(this.loadAuxin(BIGINT_1)) + ahPts.push(aPt) + + for ( let i = 1; i <= k; i++ ){ + const _inPts = [chPts[i-1], ahPts[i-1], bitifyOutPts[i-1]] + const _outPts = this.placeArith('SubEXP', _inPts) + chPts.push(_outPts[0]) + ahPts.push(_outPts[1]) + } + + return chPts[chPts.length-1] + } + /** * MLOAD는 고정적으로 32바이트를 읽지만, offset이 1바이트 단위이기 때문에 데이터 변형이 발생할 수 있습니다. * 데이터 변형을 추적하기 위해 데이터 변형 여부를 확인하는 함수를 구현합니다.