@@ -101,16 +101,35 @@ contract OneStepProverHostIo is IOneStepProver {
101101 state.u64Vals[idx] = val;
102102 }
103103
104+ uint256 internal constant BLS_MODULUS =
105+ 52435875175126190479447740508185965837690552500527637822603658699938581184513 ;
106+ uint256 internal constant PRIMITIVE_ROOT_OF_UNITY =
107+ 10238227357739495823651030575849232062558860180284477541189508159991286009131 ;
108+
109+ // Computes b**e % m
110+ // Really pure but the Solidity compiler sees the staticcall and requires view
111+ function modExp256 (
112+ uint256 b ,
113+ uint256 e ,
114+ uint256 m
115+ ) internal view returns (uint256 ) {
116+ bytes memory modExpInput = abi.encode (32 , 32 , 32 , b, e, m);
117+ (bool modexpSuccess , bytes memory modExpOutput ) = address (0x05 ).staticcall (modExpInput);
118+ require (modexpSuccess, "MODEXP_FAILED " );
119+ require (modExpOutput.length == 32 , "MODEXP_WRONG_LENGTH " );
120+ return uint256 (bytes32 (modExpOutput));
121+ }
122+
104123 function executeReadPreImage (
105124 ExecutionContext calldata ,
106125 Machine memory mach ,
107126 Module memory mod ,
108127 Instruction calldata inst ,
109128 bytes calldata proof
110- ) internal pure {
129+ ) internal view {
111130 uint256 preimageOffset = mach.valueStack.pop ().assumeI32 ();
112131 uint256 ptr = mach.valueStack.pop ().assumeI32 ();
113- if (ptr + 32 > mod.moduleMemory.size || ptr % LEAF_SIZE != 0 ) {
132+ if (preimageOffset % 32 != 0 || ptr + 32 > mod.moduleMemory.size || ptr % LEAF_SIZE != 0 ) {
114133 mach.status = MachineStatus.ERRORED;
115134 return ;
116135 }
@@ -158,6 +177,60 @@ contract OneStepProverHostIo is IOneStepProver {
158177 preimageEnd = preimage.length ;
159178 }
160179 extracted = preimage[preimageOffset:preimageEnd];
180+ } else if (inst.argumentData == 2 ) {
181+ // The machine is asking for an Ethereum versioned hash preimage
182+
183+ require (proofType == 0 , "UNKNOWN_PREIMAGE_PROOF " );
184+
185+ // kzgProof should be a valid input to the EIP-4844 point evaluation precompile at address 0x0A.
186+ // It should prove the preimageOffset/32'th word of the machine's requested KZG commitment.
187+ bytes calldata kzgProof = proof[proofOffset:];
188+
189+ require (bytes32 (kzgProof[:32 ]) == leafContents, "KZG_PROOF_WRONG_HASH " );
190+
191+ uint256 fieldElementsPerBlob;
192+ uint256 blsModulus;
193+ {
194+ (bool success , bytes memory kzgParams ) = address (0x0A ).staticcall (kzgProof);
195+ require (success, "INVALID_KZG_PROOF " );
196+ require (kzgParams.length > 0 , "KZG_PRECOMPILE_MISSING " );
197+ (fieldElementsPerBlob, blsModulus) = abi.decode (kzgParams, (uint256 , uint256 ));
198+ }
199+
200+ // With a hardcoded PRIMITIVE_ROOT_OF_UNITY, we can only support this BLS modulus.
201+ // It may be worth in the future supporting arbitrary BLS moduli, but we would likely need to
202+ // validate a user-supplied root of unity.
203+ require (blsModulus == BLS_MODULUS, "UNKNOWN_BLS_MODULUS " );
204+
205+ // If preimageOffset is greater than or equal to the blob size, leave extracted empty and call it here.
206+ if (preimageOffset < fieldElementsPerBlob * 32 ) {
207+ // We need to compute what point the polynomial should be evaluated at to get the right part of the preimage.
208+ // KZG commitments use a bit reversal permutation to order the roots of unity.
209+ // To account for that, we reverse the bit order of the index.
210+ uint256 bitReversedIndex = 0 ;
211+ // preimageOffset was required to be 32 byte aligned above
212+ uint256 tmp = preimageOffset / 32 ;
213+ for (uint256 i = 1 ; i < fieldElementsPerBlob; i <<= 1 ) {
214+ bitReversedIndex <<= 1 ;
215+ if (tmp & 1 == 1 ) {
216+ bitReversedIndex |= 1 ;
217+ }
218+ tmp >>= 1 ;
219+ }
220+
221+ // First, we get the root of unity of order 2**fieldElementsPerBlob.
222+ // We start with a root of unity of order 2**32 and then raise it to
223+ // the power of (2**32)/fieldElementsPerBlob to get root of unity we need.
224+ uint256 rootOfUnityPower = (1 << 32 ) / fieldElementsPerBlob;
225+ // Then, we raise the root of unity to the power of bitReversedIndex,
226+ // to retrieve this word of the KZG commitment.
227+ rootOfUnityPower *= bitReversedIndex;
228+ // z is the point the polynomial is evaluated at to retrieve this word of data
229+ uint256 z = modExp256 (PRIMITIVE_ROOT_OF_UNITY, rootOfUnityPower, blsModulus);
230+ require (bytes32 (kzgProof[32 :64 ]) == bytes32 (z), "KZG_PROOF_WRONG_Z " );
231+
232+ extracted = kzgProof[64 :96 ];
233+ }
161234 } else {
162235 revert ("UNKNOWN_PREIMAGE_TYPE " );
163236 }
0 commit comments