Skip to content
This repository was archived by the owner on Nov 6, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arb_os/arbos-upgrade.mexe

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion arb_os/arbos.mexe

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion arb_os/arbowner.mini
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ view write func arbowner_finishCodeUploadAsArbosUpgrade(_topFrame: EvmCallFrame,
let upgradeCodePoint = unsafecast<view write func()>(avmCodeBuilder_finish(upgrade));
upgradeInProgress = None<AvmCodeBuilder>;
previousArbosUpgradeHash = newCodeHash;
let _ = evmCallStack_returnFromCall(true, 0, 0, Some(upgradeCodePoint));
let _ = evmCallStack_returnFromCall(const::TxResultCode_success, 0, 0, Some(upgradeCodePoint));
} else {
evmOp_revert_knownCodePc(address(const::Address_ArbOwner), 44, 0, 0);
}
Expand Down
1 change: 1 addition & 0 deletions arb_os/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@
"TxResultCode_cannotDeployAtAddress": 7,
"TxResultCode_congestion": 2,
"_TxResultCode_exceededTxGasLimit": 8,
"TxResultCode_executionRanOutOfGas": 16,
"TxResultCode_formatError": 6,
"TxResultCode_gasPriceTooLow": 11,
"TxResultCode_insufficientBalance": 4,
Expand Down
20 changes: 16 additions & 4 deletions arb_os/errorHandler.mini
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use evmCallStack::emptyAvmStack;
use evmCallStack::cleanAvmAuxStackAndCall;
use evmCallStack::evmCallStack_returnFromCall;
use evmCallStack::evmCallStack_isEmpty;
use evmCallStack::evmCallStack_callHitError;
use evmCallStack::evmCallStack_stackInfo;
Expand Down Expand Up @@ -40,21 +41,24 @@ public view write func errorHandler() {
// That's OK, because the only "harm" done is that the double-error situation of application error plus gas underflow
// gets detected as just a gas underflow. The effect on the application will be the same either way.

if (inErrorHandler) {
let outOfGas = xif (inErrorHandler) {
debug(666);
false
} else {
inErrorHandler = true;
if (getGas() > (~0 - 500)) {
xif (getGas() > (~0 - 500)) {
// application ran out of gas
// duplicate stackInfo call inside both arms of loop, to make sure it happens after the gas check
let (txid, addrs, parentAddrs) = evmCallStack_stackInfo();
debug((665, txid, addrs, parentAddrs)) ;
debug((665, txid, addrs, parentAddrs));
true
} else {
// some other error
let (txid, addrs, parentAddrs) = evmCallStack_stackInfo();
debug((666, txid, addrs, parentAddrs));
false
}
}
};

if (evmCallStack_isEmpty()) {
// Oh no, we must have hit an unexpected error in ArbOS.
Expand All @@ -66,6 +70,14 @@ public view write func errorHandler() {
} else {
// Application code must have hit an error.
inErrorHandler = false;
let _ = evmCallStack_returnFromCall(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that evmCallStack_returnFromCall may resolve the error handler, the debug((664, errInfo = 0)); from evmCallStack_callHitError may not occur. This shouldn't matter since we aren't consuming this on the node's side

xif (outOfGas) { const::TxResultCode_executionRanOutOfGas } else { const::TxResultCode_revert },
0,
0,
None<view write func()>
);

// throw a soft error, in case _returnFromCall didn't resolve the problem
evmCallStack_callHitError(0); // this will never return
}
//NOTREACHED
Expand Down
48 changes: 33 additions & 15 deletions arb_os/evmCallStack.mini
Original file line number Diff line number Diff line change
Expand Up @@ -759,13 +759,19 @@ public view write func evmCallStack_doCall(
}

public view write func evmCallStack_returnFromCall(
success: bool,
resultCode: uint,
returnOffset: uint,
returnLength: uint,
jumpTargetIfTxEnd: option<view write func()>,
) -> option<()> {
// A call just completed. Commit its effects as appropriate, and return control to its caller.

assert((
(resultCode == const::TxResultCode_success) || (resultCode == const::TxResultCode_revert)
|| (resultCode == const::TxResultCode_executionRanOutOfGas),
resultCode
));

let leftoverGas = gasAccounting_pauseTxCharges(); // amount of gas lent to call that wasn't used

let topFrame = globalCallStack?;
Expand All @@ -781,7 +787,7 @@ public view write func evmCallStack_returnFromCall(
let extraStorageCost = uint(storageIncrease) * gasAccounting_getStoragePriceInArbGas();
if (extraStorageCost > leftoverGas) {
// the call didn't save enough gas to cover its storage, so treat it as out-of-gas
success = false;
resultCode = const::TxResultCode_executionRanOutOfGas;
returnData = bytearray_new(0);
leftoverGas = 0;
}
Expand All @@ -795,15 +801,20 @@ public view write func evmCallStack_returnFromCall(
)
};

if (success && !topFrame.static) {
if ((resultCode == const::TxResultCode_success) && !topFrame.static) {
// copy relevant data back to parent frame
set newTopFrame.accountStore = topFrame.accountStore;
set newTopFrame.runningAsAccount = accountStore_get(topFrame.accountStore, newTopFrame.runningAs);
set newTopFrame.storageDelta = topFrame.storageDelta;
set newTopFrame.evmLogs = topFrame.evmLogs;
set newTopFrame.sendQueue = topFrame.sendQueue;
}
set newTopFrame.returnInfo = Some(struct{ data: returnData, status: success });
set newTopFrame.returnInfo = Some(
struct {
data: returnData,
status: resultCode == const::TxResultCode_success,
}
);

// copy returndata into caller's memory
let resumeInfo = newTopFrame.resumeInfo?;
Expand All @@ -819,15 +830,15 @@ public view write func evmCallStack_returnFromCall(
);

evmTracer_pushReturnRevert(
xif (success) { const::TxResultCode_success } else { const::TxResultCode_revert },
resultCode,
returnData,
resumeInfo.givenGas + resumeInfo.savedGas - leftoverGas,
);

// ready to return to caller
// save updated EVM callstack state; send sendOnFailure message if applicable; restore caller's stack; jump to resume address
globalCallStack = Some(newTopFrame);
if ( ! success) {
if (resultCode != const::TxResultCode_success) {
if let Some(msg) = topFrame.sendOnFailure {
// promote the send-on-failure message to the next level of the callstack
// note that it will still be discarded if the parent (or grandparent etc) reverts
Expand All @@ -836,7 +847,7 @@ public view write func evmCallStack_returnFromCall(
}
emptyAvmStack(); // throw away anything that callee left on the stack
restoreAvmStack(resumeInfo.stackContents); // restore the caller's stack
asm(success) { }; // push return code onto the stack, where caller expects it to be
asm(resultCode == const::TxResultCode_success) { }; // push return status onto the stack, where caller expects it to be
let _ = gasAccounting_resumeTxCharges(leftoverGas + resumeInfo.savedGas);
restoreAuxStackAndCall(resumeInfo.codePoint, resumeInfo.auxStackContents, false);

Expand All @@ -848,13 +859,17 @@ public view write func evmCallStack_returnFromCall(
// we call get/setGlobalAccountStore (without the safety checks) in here,
// because we want to write to the global store but we haven't yet cleared the last frame from the call stack

if (success && globalCurrentTxRequest.isConstructor && (bytearray_size(returnData) > 0) && (bytearray_getByte(returnData, 0) == 0xef)) {
if ( (resultCode == const::TxResultCode_success)
&& globalCurrentTxRequest.isConstructor
&& (bytearray_size(returnData) > 0)
&& (bytearray_getByte(returnData, 0) == 0xef)
) {
// per EIP-3541, fail the transaction because the resulting account code starts with 0xef
success = false;
resultCode = const::TxResultCode_revert;
returnData = bytearray_new(0);
}

if (success) {
if (resultCode == const::TxResultCode_success) {
let (entryPoint, evmJumpTable) = xif (globalCurrentTxRequest.isConstructor) {
// the call was a constructor, so we need to install the resulting EVM code as the new contract's deploy code
let _ = gasAccounting_resumeTxCharges(leftoverGas);
Expand Down Expand Up @@ -1017,15 +1032,18 @@ public view write func evmCallStack_returnFromCall(
);
}
} else {
if (resultCode == const::TxResultCode_success) {
resultCode = const::TxResultCode_executionRanOutOfGas;
}
evmTracer_pushReturnRevert(
const::TxResultCode_revert,
resultCode,
returnData,
gasUsage.gasUsed,
);
// Emit a log item reporting failure
emitTxReceipt(
globalCurrentTxRequest.incomingRequest,
const::TxResultCode_revert,
resultCode,
None<ByteArray>,
None<EvmLogs>,
Some(gasUsage),
Expand All @@ -1046,13 +1064,13 @@ public view write func evmCallStack_returnFromCall(
outbox_append(msg);
}
evmTracer_pushReturnRevert(
const::TxResultCode_revert,
resultCode,
xif (globalCurrentTxRequest.isConstructor) { bytearray_new(0) } else { returnData },
gasUsage.gasUsed,
);
emitTxReceipt(
globalCurrentTxRequest.incomingRequest,
const::TxResultCode_revert,
resultCode,
xif (globalCurrentTxRequest.isConstructor) { None<ByteArray> } else { Some(returnData) },
None<EvmLogs>,
Some(gasUsage),
Expand Down Expand Up @@ -1580,7 +1598,7 @@ public func _evmCallFrame_getParent(frame: EvmCallFrame) -> option<EvmCallFrame>

public view write func evmCallStack_callHitError(errInfo: any) {
debug((664, errInfo));
if (evmCallStack_returnFromCall(false, 0, 0, None<view write func()>) == None<()>) {
if (evmCallStack_returnFromCall(const::TxResultCode_revert, 0, 0, None<view write func()>) == None<()>) {
// something has gone seriously wrong with the evmCallStack
// recover by clearing the evmCallStack, refunding unused gas, and jumping to the error handler
globalCallStack = None<EvmCallFrame>;
Expand Down
4 changes: 2 additions & 2 deletions arb_os/evmOps.mini
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ public view write func evmOp_gas() -> uint {
}

public view write func evmOp_revert(memOffset: uint, memNbytes: uint) {
let _ = evmCallStack_returnFromCall(false, memOffset, memNbytes, None<view write func()>);
let _ = evmCallStack_returnFromCall(const::TxResultCode_revert, memOffset, memNbytes, None<view write func()>);

evm_runtimePanic(28);
}
Expand All @@ -956,7 +956,7 @@ public view write func evmOp_revert_knownPc(evmPc: uint, memOffset: uint, memNby
}

public view write func evmOp_return(memOffset: uint, memNbytes: uint) {
let _ = evmCallStack_returnFromCall(true, memOffset, memNbytes, None<view write func()>);
let _ = evmCallStack_returnFromCall(const::TxResultCode_success, memOffset, memNbytes, None<view write func()>);

evm_runtimePanic(29);
}
Expand Down
2 changes: 1 addition & 1 deletion arb_os/upgrade.json

Large diffs are not rendered by default.

Loading