Commit 8a10f31b authored by Kelvin Fichter's avatar Kelvin Fichter

Added XOR fix

parent c7921565
...@@ -79,14 +79,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -79,14 +79,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
modifier netGasCost( modifier netGasCost(
uint256 _cost uint256 _cost
) { ) {
uint256 preExecutionGas = gasleft(); uint256 gasProvided = gasleft();
_; _;
uint256 postExecutionGas = gasleft(); uint256 gasUsed = gasProvided - gasleft();
// We want to refund everything *except* the specified cost. // We want to refund everything *except* the specified cost.
transactionRecord.ovmGasRefund += ( if (_cost < gasUsed) {
(preExecutionGas - postExecutionGas) - _cost transactionRecord.ovmGasRefund += gasUsed - _cost;
); }
} }
/** /**
...@@ -133,13 +133,13 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -133,13 +133,13 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
_initContext(_transaction); _initContext(_transaction);
// Run the transaction, make sure to meter the gas usage. // Run the transaction, make sure to meter the gas usage.
uint256 gasLimit = gasleft(); uint256 gasProvided = gasleft();
ovmCALL( ovmCALL(
_transaction.gasLimit - gasMeterConfig.minTransactionGasLimit, _transaction.gasLimit - gasMeterConfig.minTransactionGasLimit,
_transaction.entrypoint, _transaction.entrypoint,
_transaction.data _transaction.data
); );
uint256 gasUsed = gasLimit - gasleft(); uint256 gasUsed = gasProvided - gasleft();
// Update the cumulative gas based on the amount of gas used. // Update the cumulative gas based on the amount of gas used.
_updateCumulativeGas(gasUsed, _transaction.queueOrigin); _updateCumulativeGas(gasUsed, _transaction.queueOrigin);
...@@ -1126,11 +1126,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1126,11 +1126,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal internal
{ {
// We need to make sure that the transaction isn't trying to access an account that hasn't // See `_checkContractStorageLoad` for more information.
// been provided to the OVM_StateManager. We'll immediately abort if this is the case. if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_checkInvalidStateAccess( _revertWithFlag(RevertFlag.OUT_OF_GAS);
ovmStateManager.hasAccount(_address) }
);
// See `_checkContractStorageLoad` for more information.
if (ovmStateManager.hasAccount(_address) == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the account has been loaded before and mark it as loaded if not. We need // Check whether the account has been loaded before and mark it as loaded if not. We need
// this because "nuisance gas" only applies to the first time that an account is loaded. // this because "nuisance gas" only applies to the first time that an account is loaded.
...@@ -1185,11 +1189,22 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1185,11 +1189,22 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal internal
{ {
// Another case of hidden complexity. If we didn't enforce this requirement, then a
// contract could pass in just enough gas to cause the INVALID_STATE_ACCESS check to fail
// on L1 but not on L2. A contract could use this behavior to prevent the
// OVM_ExecutionManager from detecting an invalid state access. Reverting with OUT_OF_GAS
// allows us to also charge for the full message nuisance gas, because you deserve that for
// trying to break the contract in this way.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// We need to make sure that the transaction isn't trying to access storage that hasn't // We need to make sure that the transaction isn't trying to access storage that hasn't
// been provided to the OVM_StateManager. We'll immediately abort if this is the case. // been provided to the OVM_StateManager. We'll immediately abort if this is the case.
_checkInvalidStateAccess( // We know that we have enough gas to do this check because of the above test.
ovmStateManager.hasContractStorage(_contract, _key) if (ovmStateManager.hasContractStorage(_contract, _key) == false) {
); _revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the slot has been loaded before and mark it as loaded if not. We need // Check whether the slot has been loaded before and mark it as loaded if not. We need
// this because "nuisance gas" only applies to the first time that a slot is loaded. // this because "nuisance gas" only applies to the first time that a slot is loaded.
...@@ -1216,11 +1231,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1216,11 +1231,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal internal
{ {
// We need to make sure that the transaction isn't trying to access storage that hasn't // See `_checkContractStorageLoad` for more information.
// been provided to the OVM_StateManager. We'll immediately abort if this is the case. if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_checkInvalidStateAccess( _revertWithFlag(RevertFlag.OUT_OF_GAS);
ovmStateManager.hasContractStorage(_contract, _key) }
);
// See `_checkContractStorageLoad` for more information.
if (ovmStateManager.hasContractStorage(_contract, _key) == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the slot has been changed before and mark it as changed if not. We need // Check whether the slot has been changed before and mark it as changed if not. We need
// this because "nuisance gas" only applies to the first time that a slot is changed. // this because "nuisance gas" only applies to the first time that a slot is changed.
...@@ -1361,30 +1380,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1361,30 +1380,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
_revertWithFlag(_flag, bytes('')); _revertWithFlag(_flag, bytes(''));
} }
/**
* Checks for an attempt to access some inaccessible state.
* @param _condition Result of some function that checks for bad access.
*/
function _checkInvalidStateAccess(
bool _condition
)
internal
{
// Another case of hidden complexity. If we didn't enforce this requirement, then a
// contract could pass in just enough gas to cause this to fail on L1 but not on L2.
// A contract could use this behavior to prevent the OVM_ExecutionManager from detecting
// an invalid state access. Reverting with OUT_OF_GAS allows us to also charge for the
// full message nuisance gas as to generally disincentivize this attack.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// We have enough gas to comfortably run this revert, so do it.
if (_condition == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
}
/****************************************** /******************************************
* Internal Functions: Nuisance Gas Logic * * Internal Functions: Nuisance Gas Logic *
......
...@@ -18,6 +18,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -18,6 +18,7 @@ contract OVM_StateManager is iOVM_StateManager {
**********************/ **********************/
bytes32 constant internal EMPTY_ACCOUNT_CODE_HASH = 0x00004B1DC0DE000000004B1DC0DE000000004B1DC0DE000000004B1DC0DE0000; bytes32 constant internal EMPTY_ACCOUNT_CODE_HASH = 0x00004B1DC0DE000000004B1DC0DE000000004B1DC0DE000000004B1DC0DE0000;
bytes32 constant internal STORAGE_XOR_VALUE = 0xFEEDFACECAFEBEEFFEEDFACECAFEBEEFFEEDFACECAFEBEEFFEEDFACECAFEBEEF;
/******************************************* /*******************************************
...@@ -365,7 +366,10 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -365,7 +366,10 @@ contract OVM_StateManager is iOVM_StateManager {
public public
authenticated authenticated
{ {
contractStorage[_contract][_key] = _value; // A hilarious optimization. `SSTORE`ing a value of `bytes32(0)` is common enough that it's
// worth populating this with a non-zero value in advance (during the fraud proof
// initialization phase) to cut the execution-time cost down to 5000 gas.
contractStorage[_contract][_key] = _value ^ STORAGE_XOR_VALUE;
// Only used when initially populating the contract storage. OVM_ExecutionManager will // Only used when initially populating the contract storage. OVM_ExecutionManager will
// perform a `hasContractStorage` INVALID_STATE_ACCESS check before putting any contract // perform a `hasContractStorage` INVALID_STATE_ACCESS check before putting any contract
...@@ -397,7 +401,8 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -397,7 +401,8 @@ contract OVM_StateManager is iOVM_StateManager {
bytes32 _value bytes32 _value
) )
{ {
return contractStorage[_contract][_key]; // See `putContractStorage` for more information about the XOR here.
return contractStorage[_contract][_key] ^ STORAGE_XOR_VALUE;
} }
/** /**
......
...@@ -18,7 +18,7 @@ enum GasMetadataKey { ...@@ -18,7 +18,7 @@ enum GasMetadataKey {
CUMULATIVE_SEQUENCER_QUEUE_GAS, CUMULATIVE_SEQUENCER_QUEUE_GAS,
CUMULATIVE_L1TOL2_QUEUE_GAS, CUMULATIVE_L1TOL2_QUEUE_GAS,
PREV_EPOCH_SEQUENCER_QUEUE_GAS, PREV_EPOCH_SEQUENCER_QUEUE_GAS,
PREV_EPOCH_L1TOL2_QUEUE_GAS PREV_EPOCH_L1TOL2_QUEUE_GAS,
} }
const keyToBytes32 = (key: GasMetadataKey): string => { const keyToBytes32 = (key: GasMetadataKey): string => {
...@@ -57,8 +57,8 @@ const test_run: TestDefinition = { ...@@ -57,8 +57,8 @@ const test_run: TestDefinition = {
[keyToBytes32(GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS)]: 0, [keyToBytes32(GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS)]: 0,
[keyToBytes32(GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS)]: 0, [keyToBytes32(GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS)]: 0,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS)]: 0, [keyToBytes32(GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS)]: 0,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS)]: 0 [keyToBytes32(GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS)]: 0,
} },
}, },
verifiedContractStorage: { verifiedContractStorage: {
[GAS_METADATA_ADDRESS]: { [GAS_METADATA_ADDRESS]: {
...@@ -67,8 +67,8 @@ const test_run: TestDefinition = { ...@@ -67,8 +67,8 @@ const test_run: TestDefinition = {
[keyToBytes32(GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS)]: true, [keyToBytes32(GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS)]: true,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS)]: true, [keyToBytes32(GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS)]: true,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS)]: true, [keyToBytes32(GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS)]: true,
} },
} },
}, },
}, },
parameters: [ parameters: [
...@@ -100,12 +100,12 @@ const test_run: TestDefinition = { ...@@ -100,12 +100,12 @@ const test_run: TestDefinition = {
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
}, },
] ],
} },
} },
], ],
}, },
] ],
} }
const runner = new ExecutionManagerTestRunner() const runner = new ExecutionManagerTestRunner()
......
...@@ -178,14 +178,14 @@ export const setContractStorage = async ( ...@@ -178,14 +178,14 @@ export const setContractStorage = async (
const baseSlotHash = getStorageSlotHash(slot, depth, { const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey1]: { [subKey1]: {
[subKey]: subValue, [subKey]: subValue,
} },
}) })
const slotValues = getFlattenedValues(depth, { const slotValues = getFlattenedValues(depth, {
[subKey1]: { [subKey1]: {
[subKey]: subValue, [subKey]: subValue,
} },
}) })
for (const slotValue of slotValues) { for (const slotValue of slotValues) {
const slotIndex = inputSlots.find((inputSlot) => { const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label return inputSlot.label === slotValue.label
...@@ -193,7 +193,7 @@ export const setContractStorage = async ( ...@@ -193,7 +193,7 @@ export const setContractStorage = async (
const slotHash = toHexString32( const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex) BigNumber.from(baseSlotHash).add(slotIndex)
) )
await contract.__setStorageSlot(slotHash, slotValue.value) await contract.__setStorageSlot(slotHash, slotValue.value)
} }
} }
...@@ -257,14 +257,14 @@ export const checkContractStorage = async ( ...@@ -257,14 +257,14 @@ export const checkContractStorage = async (
const baseSlotHash = getStorageSlotHash(slot, depth, { const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey1]: { [subKey1]: {
[subKey]: subValue, [subKey]: subValue,
} },
}) })
const slotValues = getFlattenedValues(depth, { const slotValues = getFlattenedValues(depth, {
[subKey1]: { [subKey1]: {
[subKey]: subValue, [subKey]: subValue,
} },
}) })
for (const slotValue of slotValues) { for (const slotValue of slotValues) {
const slotIndex = inputSlots.find((inputSlot) => { const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label return inputSlot.label === slotValue.label
...@@ -274,7 +274,7 @@ export const checkContractStorage = async ( ...@@ -274,7 +274,7 @@ export const checkContractStorage = async (
) )
const retSlotValue = await contract.__getStorageSlot(slotHash) const retSlotValue = await contract.__getStorageSlot(slotHash)
if (retSlotValue !== slotValue.value) { if (retSlotValue !== slotValue.value) {
throw new Error( throw new Error(
`Resulting state of ${slotValue.label} (${retSlotValue}) did not match expected state (${slotValue.value}).` `Resulting state of ${slotValue.label} (${retSlotValue}) did not match expected state (${slotValue.value}).`
......
...@@ -204,9 +204,9 @@ export class ExecutionManagerTestRunner { ...@@ -204,9 +204,9 @@ export class ExecutionManagerTestRunner {
functionParams: { functionParams: {
gasLimit: GAS_LIMIT, gasLimit: GAS_LIMIT,
target: this.contracts.Helper_TestRunner.address, target: this.contracts.Helper_TestRunner.address,
subSteps: step.functionParams.subSteps subSteps: step.functionParams.subSteps,
}, },
expectedReturnStatus: true expectedReturnStatus: true,
} }
calldata = this.encodeFunctionData(runStep) calldata = this.encodeFunctionData(runStep)
...@@ -220,7 +220,7 @@ export class ExecutionManagerTestRunner { ...@@ -220,7 +220,7 @@ export class ExecutionManagerTestRunner {
origin: step.functionParams.origin, origin: step.functionParams.origin,
msgSender: step.functionParams.msgSender, msgSender: step.functionParams.msgSender,
gasLimit: step.functionParams.gasLimit, gasLimit: step.functionParams.gasLimit,
data: calldata data: calldata,
}, },
this.contracts.OVM_StateManager.address this.contracts.OVM_StateManager.address
) )
......
...@@ -117,7 +117,7 @@ interface TestStep_CREATE2 { ...@@ -117,7 +117,7 @@ interface TestStep_CREATE2 {
} }
export interface TestStep_Run { export interface TestStep_Run {
functionName: 'run', functionName: 'run'
functionParams: { functionParams: {
timestamp: number timestamp: number
queueOrigin: number queueOrigin: number
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment