scaffolding #1
@@ -0,0 +1,152 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
|
import "forge-std/console.sol";
|
||||||
|
import {Push3Interpreter} from "../src/Push3Interpreter.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Push3IntAdapter
|
||||||
|
* @dev An adapter contract for our Push3Interpreter that provides
|
||||||
|
* various integer-only I/O function overloads. We now create
|
||||||
|
* a SUBLIST descriptor for the entire code array, placing it
|
||||||
|
* on the EXEC stack. We ignore finalCodeStack/finalExecStack
|
||||||
|
* to avoid compiler warnings.
|
||||||
|
*/
|
||||||
|
contract Push3IntAdapter {
|
||||||
|
Push3Interpreter public interpreter;
|
||||||
|
|
||||||
|
constructor(address _interpreter) {
|
||||||
|
interpreter = Push3Interpreter(_interpreter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Utility function to build a single sublist descriptor
|
||||||
|
* covering the entire `code` array, offset=0, length=code.length.
|
||||||
|
*/
|
||||||
|
function _makeFullSublistDescriptor(bytes calldata code)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
// CodeTag.SUBLIST is typically enum = 3, but
|
||||||
|
// let's fetch it via
|
||||||
|
// (uint8)Push3Interpreter.CodeTag.SUBLIST
|
||||||
|
// or we can directly pass 3 if we know the numeric value.
|
||||||
|
|
||||||
|
// We'll do:
|
||||||
|
return interpreter.makeDescriptor(
|
||||||
|
Push3Interpreter.CodeTag.SUBLIST, // the tag
|
||||||
|
0, // offset
|
||||||
|
uint32(code.length), // length of entire code array
|
||||||
|
0 // leftover bits=0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Single int input, single int output.
|
||||||
|
* 1) Build a sublist descriptor for the entire code,
|
||||||
|
* 2) Place that descriptor in the EXEC stack,
|
||||||
|
* 3) Preload `in1` on the integer stack,
|
||||||
|
* 4) Run the interpreter,
|
||||||
|
* 5) Return top of final int stack.
|
||||||
|
*/
|
||||||
|
function run1In1Out(bytes calldata code, int256 in1)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (int256 out1)
|
||||||
|
{
|
||||||
|
// 1) Build the sublist descriptor
|
||||||
|
uint256 fullDesc = _makeFullSublistDescriptor(code);
|
||||||
|
|
||||||
|
// 2) EXEC stack of size 1
|
||||||
|
uint256[] memory initExecStack = new uint256[](1);
|
||||||
|
initExecStack[0] = fullDesc;
|
||||||
|
|
||||||
|
// No code stack
|
||||||
|
uint256[] memory initCodeStack = new uint256[](0);
|
||||||
|
|
||||||
|
// 3) Preload int stack
|
||||||
|
int256[] memory initIntStack = new int256[](1);
|
||||||
|
initIntStack[0] = in1;
|
||||||
|
|
||||||
|
// 4) Run
|
||||||
|
(, , int256[] memory finalIntStack) =
|
||||||
|
interpreter.runInterpreter(code, initCodeStack, initExecStack, initIntStack);
|
||||||
|
|
||||||
|
// 5) Return top of final int stack
|
||||||
|
require(finalIntStack.length > 0, "No integer output produced");
|
||||||
|
out1 = finalIntStack[finalIntStack.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Two ints input, single int output
|
||||||
|
*/
|
||||||
|
function run2In1Out(bytes calldata code, int256 in1, int256 in2)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (int256 out1)
|
||||||
|
{
|
||||||
|
uint256 fullDesc = _makeFullSublistDescriptor(code);
|
||||||
|
uint256[] memory initExecStack = new uint256[](1);
|
||||||
|
initExecStack[0] = fullDesc;
|
||||||
|
|
||||||
|
uint256[] memory initCodeStack = new uint256[](0);
|
||||||
|
|
||||||
|
int256[] memory initIntStack = new int256[](2);
|
||||||
|
initIntStack[0] = in1;
|
||||||
|
initIntStack[1] = in2;
|
||||||
|
|
||||||
|
(, , int256[] memory finalIntStack) =
|
||||||
|
interpreter.runInterpreter(code, initCodeStack, initExecStack, initIntStack);
|
||||||
|
|
||||||
|
require(finalIntStack.length > 0, "No integer output");
|
||||||
|
|
||||||
|
out1 = finalIntStack[finalIntStack.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Single int input, two int outputs
|
||||||
|
*/
|
||||||
|
function run1In2Out(bytes calldata code, int256 in1)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (int256 out1, int256 out2)
|
||||||
|
{
|
||||||
|
uint256 fullDesc = _makeFullSublistDescriptor(code);
|
||||||
|
uint256[] memory initExecStack = new uint256[](1);
|
||||||
|
initExecStack[0] = fullDesc;
|
||||||
|
|
||||||
|
uint256[] memory initCodeStack = new uint256[](0);
|
||||||
|
|
||||||
|
int256[] memory initIntStack = new int256[](1);
|
||||||
|
initIntStack[0] = in1;
|
||||||
|
|
||||||
|
(, , int256[] memory finalIntStack) =
|
||||||
|
interpreter.runInterpreter(code, initCodeStack, initExecStack, initIntStack);
|
||||||
|
|
||||||
|
require(finalIntStack.length >= 2, "Not enough outputs");
|
||||||
|
out1 = finalIntStack[finalIntStack.length - 1];
|
||||||
|
out2 = finalIntStack[finalIntStack.length - 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Zero int input, returns entire final int stack
|
||||||
|
*/
|
||||||
|
function run0InNOut(bytes calldata code)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (int256[] memory)
|
||||||
|
{
|
||||||
|
uint256 fullDesc = _makeFullSublistDescriptor(code);
|
||||||
|
uint256[] memory initExecStack = new uint256[](1);
|
||||||
|
initExecStack[0] = fullDesc;
|
||||||
|
|
||||||
|
uint256[] memory initCodeStack = new uint256[](0);
|
||||||
|
int256[] memory initIntStack = new int256[](0);
|
||||||
|
|
||||||
|
(, , int256[] memory finalIntStack) =
|
||||||
|
interpreter.runInterpreter(code, initCodeStack, initExecStack, initIntStack);
|
||||||
|
|
||||||
|
return finalIntStack;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
|
import {Test} from "forge-std/Test.sol";
|
||||||
|
import {Push3Interpreter} from "../src/Push3Interpreter.sol";
|
||||||
|
import {Push3IntAdapter} from "../src/Push3IntAdapter.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Push3IntAdapterTest
|
||||||
|
* @dev Foundry test for the Push3IntAdapter contract,
|
||||||
|
* which composes with the Push3Interpreter.
|
||||||
|
*/
|
||||||
|
contract Push3IntAdapterTest is Test {
|
||||||
|
Push3Interpreter public interpreter;
|
||||||
|
Push3IntAdapter public adapter;
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
// 1) Deploy the interpreter
|
||||||
|
interpreter = new Push3Interpreter();
|
||||||
|
|
||||||
|
// 2) Deploy the adapter, referencing the interpreter address
|
||||||
|
adapter = new Push3IntAdapter(address(interpreter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Test run1In1Out with a minimal code array.
|
||||||
|
* For example, we might do a program that just leaves its input unchanged
|
||||||
|
* (like a NOOP).
|
||||||
|
*/
|
||||||
|
function test_run1In1Out_noop() public {
|
||||||
|
bytes memory code = hex"03000100";
|
||||||
|
|
||||||
|
int256 inputVal = 123;
|
||||||
|
int256 result = adapter.run1In1Out(code, inputVal);
|
||||||
|
|
||||||
|
// We expect the code does nothing => output == input
|
||||||
|
assertEq(result, inputVal, "Expected same value out");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Test run2In1Out. We'll push 5, 7, do an op that leaves 12.
|
||||||
|
*/
|
||||||
|
function test_run2In1Out_plus() public {
|
||||||
|
bytes memory code = hex"03000101";
|
||||||
|
|
||||||
|
int256 in1 = 5;
|
||||||
|
int256 in2 = 7;
|
||||||
|
int256 result = adapter.run2In1Out(code, in1, in2);
|
||||||
|
|
||||||
|
assertEq(result, 12, "Expected 5 + 7 = 12");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Test run1In2Out. Push a constant to a stack with 1.
|
||||||
|
* For now, we do a placeholder code.
|
||||||
|
*/
|
||||||
|
function test_run1In2Out() public {
|
||||||
|
bytes memory code = hex"0300050200000003";
|
||||||
|
|
||||||
|
int256 inVal = 42;
|
||||||
|
(int256 top, int256 secondTop) = adapter.run1In2Out(code, inVal);
|
||||||
|
|
||||||
|
assertEq(top, 3, "top stack val mismatch");
|
||||||
|
assertEq(secondTop, 42, "second top stack val mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Test run0InNOut.
|
||||||
|
* We'll feed a code that pushes some constants, and see the final stack.
|
||||||
|
*/
|
||||||
|
function test_run0InNOut() public {
|
||||||
|
|
||||||
|
bytes memory code = hex"03000A02000000030200000005";
|
||||||
|
|
||||||
|
int256[] memory stackResult = adapter.run0InNOut(code);
|
||||||
|
|
||||||
|
assertEq(stackResult.length, 2, "Expected 2 final stack items");
|
||||||
|
assertEq(stackResult[0], 3, "Item 0 mismatch");
|
||||||
|
assertEq(stackResult[1], 5, "Item 1 mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user