Skip to content

Commit

Permalink
Merge pull request #1 from OffchainLabs/stylus-program-support
Browse files Browse the repository at this point in the history
User Programs (copied from stylus repo)
  • Loading branch information
rachel-bousfield authored May 24, 2023
2 parents f48ce45 + 9cfe51d commit 878586b
Show file tree
Hide file tree
Showing 19 changed files with 683 additions and 119 deletions.
2 changes: 2 additions & 0 deletions src/challenge/ChallengeLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ library ChallengeLib {
ValueStack memory values = ValueStack({proved: valuesArray, remainingHash: 0});
ValueStack memory internalStack;
StackFrameWindow memory frameStack;
GuardStack memory guardStack;

Machine memory mach = Machine({
status: MachineStatus.RUNNING,
valueStack: values,
internalStack: internalStack,
frameStack: frameStack,
guardStack: guardStack,
globalStateHash: globalStateHash,
moduleIdx: 0,
functionIdx: 0,
Expand Down
103 changes: 103 additions & 0 deletions src/mocks/Program.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;
import "../precompiles/ArbSys.sol";

contract ProgramTest {
event Hash(bytes32 result);

function callKeccak(address program, bytes calldata data) external {
// in keccak.rs
// the input is the # of hashings followed by a preimage
// the output is the iterated hash of the preimage
(bool success, bytes memory result) = address(program).call(data);
require(success, "call failed");
bytes32 hash = bytes32(result);
emit Hash(hash);
require(hash == keccak256(data[1:]));
}

function staticcallProgram(address program, bytes calldata data)
external
view
returns (bytes memory)
{
(bool success, bytes memory result) = address(program).staticcall(data);
require(success, "call failed");
return result;
}

function assert256(
bytes memory data,
string memory text,
uint256 expected
) internal pure returns (bytes memory) {
uint256 value = abi.decode(data, (uint256));
require(value == expected, text);

bytes memory rest = new bytes(data.length - 32);
for (uint256 i = 32; i < data.length; i++) {
rest[i - 32] = data[i];
}
return rest;
}

function staticcallEvmData(
address program,
address fundedAccount,
uint64 gas,
bytes calldata data
) external view returns (bytes memory) {
(bool success, bytes memory result) = address(program).staticcall{gas: gas}(data);

address arbPrecompile = address(0x69);
address ethPrecompile = address(0x01);

result = assert256(result, "block number ", block.number - 1);
result = assert256(result, "block hash ", uint256(blockhash(block.number - 1)));
result = assert256(result, "chain id ", block.chainid);
result = assert256(result, "base fee ", block.basefee);
result = assert256(result, "gas price ", tx.gasprice);
result = assert256(result, "gas limit ", block.gaslimit);
result = assert256(result, "value ", 0);
result = assert256(result, "difficulty ", block.difficulty);
result = assert256(result, "timestamp ", block.timestamp);
result = assert256(result, "balance ", fundedAccount.balance);
result = assert256(result, "rust address ", uint256(uint160(program)));
result = assert256(result, "sender ", uint256(uint160(address(this))));
result = assert256(result, "origin ", uint256(uint160(tx.origin)));
result = assert256(result, "coinbase ", uint256(uint160(address(block.coinbase))));
result = assert256(result, "rust codehash", uint256(program.codehash));
result = assert256(result, "arb codehash ", uint256(arbPrecompile.codehash));
result = assert256(result, "eth codehash ", uint256(ethPrecompile.codehash));

return result;
}

function checkRevertData(
address program,
bytes calldata data,
bytes calldata expected
) external payable returns (bytes memory) {
(bool success, bytes memory result) = address(program).call{value: msg.value}(data);
require(!success, "unexpected success");
require(result.length == expected.length, "wrong revert data length");
for (uint256 i = 0; i < result.length; i++) {
require(result[i] == expected[i], "revert data mismatch");
}
return result;
}

function fillBlock() external payable {
bytes32 bridgeToNova = 0xeddecf107b5740cef7f5a01e3ea7e287665c4e75a8eb6afae2fda2e3d4367786;
address cryptoIsCute = 0x361594F5429D23ECE0A88E4fBE529E1c49D524d8;
uint8 v = 27;
bytes32 r = 0xc6178c2de1078cd36c3bd302cde755340d7f17fcb3fcc0b9c333ba03b217029f;
bytes32 s = 0x5fdbcefe2675e96219cdae57a7894280bf80fd40d44ce146a35e169ea6a78fd3;
while (true) {
require(ecrecover(bridgeToNova, v, r, s) == cryptoIsCute, "WRONG_ARBINAUT");
}
}
}
26 changes: 23 additions & 3 deletions src/osp/OneStepProofEntry.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2021-2022, Offchain Labs, Inc.
// Copyright 2021-2023, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

Expand All @@ -14,6 +14,10 @@ contract OneStepProofEntry is IOneStepProofEntry {
using MerkleProofLib for MerkleProof;
using MachineLib for Machine;

using ValueStackLib for ValueStack;
using GuardStackLib for GuardStack;
using StackFrameLib for StackFrameWindow;

IOneStepProver public prover0;
IOneStepProver public proverMem;
IOneStepProver public proverMath;
Expand Down Expand Up @@ -113,7 +117,7 @@ contract OneStepProofEntry is IOneStepProofEntry {
} else if (
(opcode >= Instructions.GET_GLOBAL_STATE_BYTES32 &&
opcode <= Instructions.SET_GLOBAL_STATE_U64) ||
(opcode >= Instructions.READ_PRE_IMAGE && opcode <= Instructions.HALT_AND_SET_FINISHED)
(opcode >= Instructions.READ_PRE_IMAGE && opcode <= Instructions.POP_ERROR_GUARD)
) {
prover = proverHostIo;
} else {
Expand All @@ -122,7 +126,23 @@ contract OneStepProofEntry is IOneStepProofEntry {

(mach, mod) = prover.executeOneStep(execCtx, mach, mod, inst, proof);

mach.modulesRoot = modProof.computeRootFromModule(oldModIdx, mod);
bool updateRoot = !(opcode == Instructions.LINK_MODULE ||
opcode == Instructions.UNLINK_MODULE);
if (updateRoot) {
mach.modulesRoot = modProof.computeRootFromModule(oldModIdx, mod);
}

if (mach.status == MachineStatus.ERRORED && !mach.guardStack.empty()) {
ErrorGuard memory guard = mach.guardStack.pop();
mach.frameStack.overwrite(guard.frameStack);
mach.valueStack.overwrite(guard.valueStack);
mach.internalStack.overwrite(guard.interStack);
mach.setPc(guard.onErrorPc);

// indicate an error and continue
mach.valueStack.push(ValueLib.newI32(0));
mach.status = MachineStatus.RUNNING;
}

return mach.hash();
}
Expand Down
74 changes: 54 additions & 20 deletions src/osp/OneStepProver0.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2021-2022, Offchain Labs, Inc.
// Copyright 2021-2023, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

Expand All @@ -11,6 +11,7 @@ import "../state/Deserialize.sol";
import "./IOneStepProver.sol";

contract OneStepProver0 is IOneStepProver {
using MachineLib for Machine;
using MerkleProofLib for MerkleProof;
using StackFrameLib for StackFrameWindow;
using ValueLib for Value;
Expand Down Expand Up @@ -90,28 +91,11 @@ contract OneStepProver0 is IOneStepProver {
bytes calldata
) internal pure {
StackFrame memory frame = mach.frameStack.pop();
if (frame.returnPc.valueType == ValueType.REF_NULL) {
mach.status = MachineStatus.ERRORED;
return;
} else if (frame.returnPc.valueType != ValueType.INTERNAL_REF) {
revert("INVALID_RETURN_PC_TYPE");
}
uint256 data = frame.returnPc.contents;
uint32 pc = uint32(data);
uint32 func = uint32(data >> 32);
uint32 mod = uint32(data >> 64);
require(data >> 96 == 0, "INVALID_RETURN_PC_DATA");
mach.functionPc = pc;
mach.functionIdx = func;
mach.moduleIdx = mod;
mach.setPc(frame.returnPc);
}

function createReturnValue(Machine memory mach) internal pure returns (Value memory) {
uint256 returnData = 0;
returnData |= mach.functionPc;
returnData |= uint256(mach.functionIdx) << 32;
returnData |= uint256(mach.moduleIdx) << 64;
return Value({valueType: ValueType.INTERNAL_REF, contents: returnData});
return ValueLib.newPc(mach.functionPc, mach.functionIdx, mach.moduleIdx);
}

function executeCall(
Expand Down Expand Up @@ -157,6 +141,52 @@ contract OneStepProver0 is IOneStepProver {
mach.functionPc = 0;
}

function executeCrossModuleForward(
Machine memory mach,
Module memory mod,
Instruction calldata inst,
bytes calldata
) internal pure {
// Push the return pc to the stack
mach.valueStack.push(createReturnValue(mach));

// Push caller's caller module info to the stack
StackFrame memory frame = mach.frameStack.peek();
mach.valueStack.push(ValueLib.newI32(frame.callerModule));
mach.valueStack.push(ValueLib.newI32(frame.callerModuleInternals));

// Jump to the target
uint32 func = uint32(inst.argumentData);
uint32 module = uint32(inst.argumentData >> 32);
require(inst.argumentData >> 64 == 0, "BAD_CROSS_MODULE_CALL_DATA");
mach.moduleIdx = module;
mach.functionIdx = func;
mach.functionPc = 0;
}

function executeCrossModuleDynamicCall(
Machine memory mach,
Module memory mod,
Instruction calldata inst,
bytes calldata
) internal pure {
// Get the target from the stack
uint32 func = mach.valueStack.pop().assumeI32();
uint32 module = mach.valueStack.pop().assumeI32();

// Push the return pc to the stack
mach.valueStack.push(createReturnValue(mach));

// Push caller module info to the stack
mach.valueStack.push(ValueLib.newI32(mach.moduleIdx));
mach.valueStack.push(ValueLib.newI32(mod.internalsOffset));

// Jump to the target
mach.moduleIdx = module;
mach.functionIdx = func;
mach.functionPc = 0;
}

function executeCallerModuleInternalCall(
Machine memory mach,
Module memory mod,
Expand Down Expand Up @@ -454,6 +484,10 @@ contract OneStepProver0 is IOneStepProver {
impl = executeCall;
} else if (opcode == Instructions.CROSS_MODULE_CALL) {
impl = executeCrossModuleCall;
} else if (opcode == Instructions.CROSS_MODULE_FORWARD) {
impl = executeCrossModuleForward;
} else if (opcode == Instructions.CROSS_MODULE_DYNAMIC_CALL) {
impl = executeCrossModuleDynamicCall;
} else if (opcode == Instructions.CALLER_MODULE_INTERNAL_CALL) {
impl = executeCallerModuleInternalCall;
} else if (opcode == Instructions.CALL_INDIRECT) {
Expand Down
Loading

0 comments on commit 878586b

Please sign in to comment.