Commit 9b464f1d authored by Kelvin Fichter's avatar Kelvin Fichter

Added comments to more contracts, added account abstraction

parent b439747a
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_ECDSAContractAccount } from "../../iOVM/accounts/iOVM_ECDSAContractAccount.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
/**
* @title OVM_ECDSAContractAccount
*/
contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
/********************
* Public Functions *
********************/
/**
* Executes a signed transaction.
* @param _transaction Signed EOA transaction.
* @param _signatureType Hashing scheme used for the transaction (e.g., ETH signed message).
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
)
override
public
returns (
bool _success,
bytes memory _returndata
)
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
// Address of this contract within the ovm (ovmADDRESS) should be the same as the
// recovered address of the user who signed this message. This is how we manage to shim
// account abstraction even though the user isn't a contract.
require(
Lib_ECDSAUtils.recover(
_transaction,
_signatureType == Lib_OVMCodec.EOASignatureType.ETH_SIGNED_MESSAGE,
_v,
_r,
_s,
ovmExecutionManager.ovmCHAINID()
) == ovmExecutionManager.ovmADDRESS(),
"Signature provided for EOA transaction execution is invalid."
);
Lib_OVMCodec.EOATransaction memory decodedTx = Lib_OVMCodec.decodeEOATransaction(_transaction);
// Need to make sure that the transaction nonce is right and bump it if so.
require(
decodedTx.nonce == ovmExecutionManager.ovmGETNONCE() + 1,
"Transaction nonce does not match the expected nonce."
);
ovmExecutionManager.ovmSETNONCE(decodedTx.nonce);
// Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.target == address(0)) {
address created = ovmExecutionManager.ovmCREATE{gas: decodedTx.gasLimit}(
decodedTx.data
);
// EVM doesn't tell us whether a contract creation failed, even if it reverted during
// initialization. Always return `true` for our success value here.
return (true, abi.encode(created));
} else {
return ovmExecutionManager.ovmCALL(
decodedTx.gasLimit,
decodedTx.target,
decodedTx.data
);
}
}
}
...@@ -3,14 +3,17 @@ pragma solidity ^0.7.0; ...@@ -3,14 +3,17 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Library Imports */ /* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol"; import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_DataTypes } from "../../iOVM/codec/iOVM_DataTypes.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol"; import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol"; import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol"; import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol";
/* Contract Imports */
import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol";
/** /**
* @title OVM_ExecutionManager * @title OVM_ExecutionManager
*/ */
...@@ -93,7 +96,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -93,7 +96,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
* @param _ovmStateManager iOVM_StateManager implementation providing account state. * @param _ovmStateManager iOVM_StateManager implementation providing account state.
*/ */
function run( function run(
iOVM_DataTypes.OVMTransactionData memory _transaction, Lib_OVMCodec.Transaction memory _transaction,
address _ovmStateManager address _ovmStateManager
) )
override override
...@@ -235,7 +238,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -235,7 +238,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// Generate the correct CREATE address. // Generate the correct CREATE address.
address contractAddress = Lib_EthUtils.getAddressForCREATE( address contractAddress = Lib_EthUtils.getAddressForCREATE(
creator, creator,
_getAccount(creator).nonce _getAccountNonce(creator)
); );
_createContract( _createContract(
...@@ -282,6 +285,101 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -282,6 +285,101 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
} }
/*******************************
* Account Abstraction Opcodes *
******************************/
/**
* Retrieves the nonce of the current ovmADDRESS.
* @return _nonce Nonce of the current contract.
*/
function ovmGETNONCE()
override
public
returns (
uint256 _nonce
)
{
return _getAccountNonce(ovmADDRESS());
}
/**
* Sets the nonce of the current ovmADDRESS.
* @param _nonce New nonce for the current contract.
*/
function ovmSETNONCE(
uint256 _nonce
)
override
public
{
if (_nonce <= ovmGETNONCE()) {
return;
}
_setAccountNonce(ovmADDRESS(), _nonce);
}
/**
* Creates a new EOA contract account, for account abstraction.
* @dev Essentially functions like ovmCREATE or ovmCREATE2, but we can bypass a lot of checks
* because the contract we're creating is trusted (no need to do safety checking or to
* handle unexpected reverts). Doesn't need to return an address because the address is
* assumed to be the user's actual address.
* @param _messageHash Hash of a message signed by some user, for verification.
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
*/
function ovmCREATEEOA(
bytes32 _messageHash,
uint8 _v,
bytes32 _r,
bytes32 _s
)
override
public
{
// Recover the EOA address from the message hash and signature parameters. Since we do the
// hashing in advance, we don't have handle different message hashing schemes. Even if this
// function were to return the wrong address (rather than explicitly returning the zero
// address), the rest of the transaction would simply fail (since there's no EOA account to
// actually execute the transaction).
address eoa = ecrecover(
_messageHash,
(_v - uint8(ovmCHAINID()) * 2) - 8,
_r,
_s
);
// Invalid signature is a case we proactively handle with a revert. We could alternatively
// have this function return a `success` boolean, but this is just easier.
if (eoa == address(0)) {
ovmREVERT(bytes("Signature provided for EOA contract creation is invalid."));
}
// If the user already has an EOA account, then there's no need to perform this operation.
if (_hasAccount(eoa) == true) {
return;
}
// We always need to initialize the contract with the default account values.
_initPendingAccount(eoa);
// Now actually create the account and get its bytecode. We're not worried about reverts
// (other than out of gas, which we can't capture anyway) because this contract is trusted.
OVM_ECDSAContractAccount eoaContractAccount = new OVM_ECDSAContractAccount();
bytes memory deployedCode = Lib_EthUtils.getCode(address(eoaContractAccount));
// Commit the account with its final values.
_commitPendingAccount(
eoa,
address(eoaContractAccount),
keccak256(deployedCode)
);
}
/********************************* /*********************************
* Opcodes: Contract Interaction * * Opcodes: Contract Interaction *
*********************************/ *********************************/
...@@ -470,7 +568,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -470,7 +568,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
uint256 length = _length == 1 ? 2 : _length; uint256 length = _length == 1 ? 2 : _length;
return Lib_EthUtils.getCode( return Lib_EthUtils.getCode(
_getAccount(_contract).ethAddress, _getAccountEthAddress(_contract),
_offset, _offset,
_length _length
); );
...@@ -491,7 +589,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -491,7 +589,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
{ {
return Lib_EthUtils.getCodeSize( return Lib_EthUtils.getCodeSize(
_getAccount(_contract).ethAddress _getAccountEthAddress(_contract)
); );
} }
...@@ -510,7 +608,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -510,7 +608,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
{ {
return Lib_EthUtils.getCodeHash( return Lib_EthUtils.getCodeHash(
_getAccount(_contract).ethAddress _getAccountEthAddress(_contract)
); );
} }
...@@ -599,7 +697,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -599,7 +697,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
internal internal
{ {
// We always update the nonce of the creating account, even if the creation fails. // We always update the nonce of the creating account, even if the creation fails.
_incrementAccountNonce(ovmADDRESS()); _setAccountNonce(ovmADDRESS(), 1);
// We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point // We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point
// to the contract's associated address. // to the contract's associated address.
...@@ -648,7 +746,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -648,7 +746,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
return _handleExternalInteraction( return _handleExternalInteraction(
_nextMessageContext, _nextMessageContext,
_gasLimit, _gasLimit,
_getAccount(_contract).ethAddress, _getAccountEthAddress(_contract),
_calldata _calldata
); );
} }
...@@ -749,54 +847,69 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -749,54 +847,69 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
******************************************/ ******************************************/
/** /**
* Retrieves an account from the OVM_StateManager. * Checks whether an account exists within the OVM_StateManager.
* @param _address Address of the account to retrieve. * @param _address Address of the account to check.
* @return _account Retrieved account object. * @return _exists Whether or not the account exists.
*/ */
function _getAccount( function _hasAccount(
address _address address _address
) )
internal internal
returns ( returns (
iOVM_DataTypes.OVMAccount memory _account bool _exists
) )
{ {
// We need to make sure that the transaction isn't trying to access an account that hasn't _checkAccountLoad(_address);
// been provided to the OVM_StateManager. We'll immediately abort if this is the case. return ovmStateManager.hasAccount(_address);
_checkInvalidStateAccess( }
ovmStateManager.hasAccount(_address)
);
// 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.
(
bool _wasAccountAlreadyLoaded
) = ovmStateManager.testAndSetAccountLoaded(_address);
// Actually retrieve the account.
iOVM_DataTypes.OVMAccount memory account = ovmStateManager.getAccount(_address);
// If we hadn't already loaded the account, then we'll need to charge "nuisance gas" based /**
// on the size of the contract code. * Sets the nonce of an account.
if (_wasAccountAlreadyLoaded == false) { * @param _address Address of the account to modify.
_useNuisanceGas( * @param _nonce New account nonce.
Lib_EthUtils.getCodeSize(account.ethAddress) * NUISANCE_GAS_PER_CONTRACT_BYTE */
); function _setAccountNonce(
address _address,
uint256 _nonce
)
internal
{
_checkAccountChange(_address);
ovmStateManager.setAccountNonce(_address, _nonce);
} }
return account; /**
* Gets the nonce of an account.
* @param _address Address of the account to access.
* @return _nonce Nonce of the account.
*/
function _getAccountNonce(
address _address
)
internal
returns (
uint256 _nonce
)
{
_checkAccountLoad(_address);
return ovmStateManager.getAccountNonce(_address);
} }
/** /**
* Increments the nonce of an account. * Retrieves the Ethereum address of an account.
* @param _address Address of the account to bump. * @param _address Address of the account to access.
* @return _ethAddress Corresponding Ethereum address.
*/ */
function _incrementAccountNonce( function _getAccountEthAddress(
address _address address _address
) )
internal internal
returns (
address _ethAddress
)
{ {
ovmStateManager.incrementAccountNonce(_address); _checkAccountLoad(_address);
ovmStateManager.getAccountEthAddress(_address);
} }
/** /**
...@@ -808,6 +921,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -808,6 +921,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal internal
{ {
_checkAccountChange(_address);
ovmStateManager.initPendingAccount(_address); ovmStateManager.initPendingAccount(_address);
} }
...@@ -826,21 +940,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -826,21 +940,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal internal
{ {
// Check whether the account has been changed before and mark it as changed if not. We need _checkAccountChange(_address);
// this because "nuisance gas" only applies to the first time that an account is changed.
(
bool _wasAccountAlreadyChanged
) = ovmStateManager.testAndSetAccountChanged(_address);
// If we hadn't already changed the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code.
if (_wasAccountAlreadyChanged == false) {
_useNuisanceGas(
Lib_EthUtils.getCodeSize(_ethAddress) * NUISANCE_GAS_PER_CONTRACT_BYTE
);
}
// Actually commit the contract.
ovmStateManager.commitPendingAccount( ovmStateManager.commitPendingAccount(
_address, _address,
_ethAddress, _ethAddress,
...@@ -862,6 +962,95 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -862,6 +962,95 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
returns ( returns (
bytes32 _value bytes32 _value
) )
{
_checkContractStorageLoad(_contract, _key);
return ovmStateManager.getContractStorage(_contract, _key);
}
/**
* Sets the value of a storage slot.
* @param _contract Address of the contract to modify.
* @param _key 32 byte key of the storage slot.
* @param _value 32 byte storage slot value.
*/
function _putContractStorage(
address _contract,
bytes32 _key,
bytes32 _value
)
internal
{
_checkContractStorageChange(_contract, _key);
ovmStateManager.putContractStorage(_contract, _key, _value);
}
/**
* Validation whenever a contract needs to be loaded. Checks that the account exists, charges
* nuisance gas if the account hasn't been loaded before.
* @param _address Address of the account to load.
*/
function _checkAccountLoad(
address _address
)
internal
{
// We need to make sure that the transaction isn't trying to access an account that hasn't
// been provided to the OVM_StateManager. We'll immediately abort if this is the case.
_checkInvalidStateAccess(
ovmStateManager.hasAccount(_address)
);
// 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.
(
bool _wasAccountAlreadyLoaded
) = ovmStateManager.testAndSetAccountLoaded(_address);
// If we hadn't already loaded the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code.
if (_wasAccountAlreadyLoaded == false) {
_useNuisanceGas(
Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE
);
}
}
/**
* Validation whenever a contract needs to be changed. Checks that the account exists, charges
* nuisance gas if the account hasn't been changed before.
* @param _address Address of the account to change.
*/
function _checkAccountChange(
address _address
)
internal
{
// Check whether the account has been changed before and mark it as changed if not. We need
// this because "nuisance gas" only applies to the first time that an account is changed.
(
bool _wasAccountAlreadyChanged
) = ovmStateManager.testAndSetAccountChanged(_address);
// If we hadn't already changed the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code.
if (_wasAccountAlreadyChanged == false) {
_useNuisanceGas(
Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE
);
}
}
/**
* Validation whenever a slot needs to be loaded. Checks that the account exists, charges
* nuisance gas if the slot hasn't been loaded before.
* @param _contract Address of the account to load from.
* @param _key 32 byte key to load.
*/
function _checkContractStorageLoad(
address _contract,
bytes32 _key
)
internal
{ {
// 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.
...@@ -880,21 +1069,17 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -880,21 +1069,17 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
if (_wasContractStorageAlreadyLoaded == false) { if (_wasContractStorageAlreadyLoaded == false) {
_useNuisanceGas(NUISANCE_GAS_SLOAD); _useNuisanceGas(NUISANCE_GAS_SLOAD);
} }
// Actually retrieve the storage slot.
return ovmStateManager.getContractStorage(_contract, _key);
} }
/** /**
* Sets the value of a storage slot. * Validation whenever a slot needs to be changed. Checks that the account exists, charges
* @param _contract Address of the contract to modify. * nuisance gas if the slot hasn't been changed before.
* @param _key 32 byte key of the storage slot. * @param _contract Address of the account to change.
* @param _value 32 byte storage slot value. * @param _key 32 byte key to change.
*/ */
function _putContractStorage( function _checkContractStorageChange(
address _contract, address _contract,
bytes32 _key, bytes32 _key
bytes32 _value
) )
internal internal
{ {
...@@ -909,9 +1094,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -909,9 +1094,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
if (_wasContractStorageAlreadyChanged == false) { if (_wasContractStorageAlreadyChanged == false) {
_useNuisanceGas(NUISANCE_GAS_SSTORE); _useNuisanceGas(NUISANCE_GAS_SSTORE);
} }
// Actually modify the storage slot.
ovmStateManager.putContractStorage(_contract, _key, _value);
} }
......
...@@ -4,7 +4,20 @@ pragma solidity ^0.7.0; ...@@ -4,7 +4,20 @@ pragma solidity ^0.7.0;
/* Interface Imports */ /* Interface Imports */
import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol"; import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol";
/**
* @title OVM_SafetyChecker
*/
contract OVM_SafetyChecker is iOVM_SafetyChecker { contract OVM_SafetyChecker is iOVM_SafetyChecker {
/********************
* Public Functions *
********************/
/**
* Checks that a given bytecode string is considered safe.
* @param _bytecode Bytecode string to check.
* @return _safe Whether or not the bytecode is safe.
*/
function isBytecodeSafe( function isBytecodeSafe(
bytes memory _bytecode bytes memory _bytecode
) )
......
...@@ -2,26 +2,39 @@ ...@@ -2,26 +2,39 @@
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol"; import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_DataTypes } from "../../iOVM/codec/iOVM_DataTypes.sol";
/**
* @title OVM_StateManager
*/
contract OVM_StateManager is iOVM_StateManager { contract OVM_StateManager is iOVM_StateManager {
enum ItemState {
ITEM_UNTOUCHED,
ITEM_LOADED,
ITEM_CHANGED
}
mapping (address => iOVM_DataTypes.OVMAccount) public accounts; /****************************************
mapping (address => iOVM_DataTypes.OVMAccount) public pendingAccounts; * Contract Variables: Internal Storage *
mapping (address => mapping (bytes32 => bytes32)) public contractStorage; ****************************************/
mapping (address => mapping (bytes32 => bool)) public verifiedContractStorage;
mapping (bytes32 => ItemState) public itemStates; mapping (address => Lib_OVMCodec.Account) internal accounts;
mapping (address => mapping (bytes32 => bytes32)) internal contractStorage;
mapping (address => mapping (bytes32 => bool)) internal verifiedContractStorage;
mapping (bytes32 => ItemState) internal itemStates;
/************************************
* Public Functions: Account Access *
************************************/
/**
* Inserts an account into the state.
* @param _address Address of the account to insert.
* @param _account Account to insert for the given address.
*/
function putAccount( function putAccount(
address _address, address _address,
iOVM_DataTypes.OVMAccount memory _account Lib_OVMCodec.Account memory _account
) )
override override
public public
...@@ -29,16 +42,26 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -29,16 +42,26 @@ contract OVM_StateManager is iOVM_StateManager {
accounts[_address] = _account; accounts[_address] = _account;
} }
/**
* Retrieves an account from the state.
* @param _address Address of the account to retrieve.
* @return _account Account for the given address.
*/
function getAccount(address _address) function getAccount(address _address)
override override
public public
returns ( returns (
iOVM_DataTypes.OVMAccount memory _account Lib_OVMCodec.Account memory _account
) )
{ {
return accounts[_address]; return accounts[_address];
} }
/**
* Checks whether the state has a given account.
* @param _address Address of the account to check.
* @return _exists Whether or not the state has the account.
*/
function hasAccount( function hasAccount(
address _address address _address
) )
...@@ -48,29 +71,79 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -48,29 +71,79 @@ contract OVM_StateManager is iOVM_StateManager {
bool _exists bool _exists
) )
{ {
return getAccount(_address).codeHash != bytes32(0); return accounts[_address].codeHash != bytes32(0);
}
/**
* Sets the nonce of an account.
* @param _address Address of the account to modify.
* @param _nonce New account nonce.
*/
function setAccountNonce(
address _address,
uint256 _nonce
)
override
public
{
accounts[_address].nonce = _nonce;
}
/**
* Gets the nonce of an account.
* @param _address Address of the account to access.
* @return _nonce Nonce of the account.
*/
function getAccountNonce(
address _address
)
override
public
returns (
uint256 _nonce
)
{
return accounts[_address].nonce;
} }
function incrementAccountNonce( /**
* Retrieves the Ethereum address of an account.
* @param _address Address of the account to access.
* @return _ethAddress Corresponding Ethereum address.
*/
function getAccountEthAddress(
address _address address _address
) )
override override
public public
returns (
address _ethAddress
)
{ {
accounts[_address].nonce += 1; return accounts[_address].ethAddress;
} }
/**
* Initializes a pending account (during CREATE or CREATE2) with the default values.
* @param _address Address of the account to initialize.
*/
function initPendingAccount( function initPendingAccount(
address _address address _address
) )
override override
public public
{ {
iOVM_DataTypes.OVMAccount storage account = accounts[_address]; Lib_OVMCodec.Account storage account = accounts[_address];
account.nonce = 1; account.nonce = 1;
account.codeHash = keccak256(hex'80'); account.codeHash = keccak256(hex'80');
} }
/**
* Finalizes the creation of a pending account (during CREATE or CREATE2).
* @param _address Address of the account to finalize.
* @param _ethAddress Address of the account's associated contract on Ethereum.
* @param _codeHash Hash of the account's code.
*/
function commitPendingAccount( function commitPendingAccount(
address _address, address _address,
address _ethAddress, address _ethAddress,
...@@ -79,11 +152,62 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -79,11 +152,62 @@ contract OVM_StateManager is iOVM_StateManager {
override override
public public
{ {
iOVM_DataTypes.OVMAccount storage account = accounts[_address]; Lib_OVMCodec.Account storage account = accounts[_address];
account.ethAddress = _ethAddress; account.ethAddress = _ethAddress;
account.codeHash = _codeHash; account.codeHash = _codeHash;
} }
/**
* Checks whether an account has already been retrieved, and marks it as retrieved if not.
* @param _address Address of the account to check.
* @return _wasAccountAlreadyLoaded Whether or not the account was already loaded.
*/
function testAndSetAccountLoaded(
address _address
)
override
public
returns (
bool _wasAccountAlreadyLoaded
)
{
return _testItemState(
keccak256(abi.encodePacked(_address)),
ItemState.ITEM_LOADED
);
}
/**
* Checks whether an account has already been modified, and marks it as modified if not.
* @param _address Address of the account to check.
* @return _wasAccountAlreadyChanged Whether or not the account was already modified.
*/
function testAndSetAccountChanged(
address _address
)
override
public
returns (
bool _wasAccountAlreadyChanged
)
{
return _testItemState(
keccak256(abi.encodePacked(_address)),
ItemState.ITEM_CHANGED
);
}
/************************************
* Public Functions: Storage Access *
************************************/
/**
* Changes a contract storage slot value.
* @param _contract Address of the contract to modify.
* @param _key 32 byte storage slot key.
* @param _value 32 byte storage slot value.
*/
function putContractStorage( function putContractStorage(
address _contract, address _contract,
bytes32 _key, bytes32 _key,
...@@ -96,6 +220,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -96,6 +220,12 @@ contract OVM_StateManager is iOVM_StateManager {
verifiedContractStorage[_contract][_key] = true; verifiedContractStorage[_contract][_key] = true;
} }
/**
* Retrieves a contract storage slot value.
* @param _contract Address of the contract to access.
* @param _key 32 byte storage slot key.
* @return _value 32 byte storage slot value.
*/
function getContractStorage( function getContractStorage(
address _contract, address _contract,
bytes32 _key bytes32 _key
...@@ -109,6 +239,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -109,6 +239,12 @@ contract OVM_StateManager is iOVM_StateManager {
return contractStorage[_contract][_key]; return contractStorage[_contract][_key];
} }
/**
* Checks whether a contract storage slot exists in the state.
* @param _contract Address of the contract to access.
* @param _key 32 byte storage slot key.
* @return _exists Whether or not the key was set in the state.
*/
function hasContractStorage( function hasContractStorage(
address _contract, address _contract,
bytes32 _key bytes32 _key
...@@ -122,36 +258,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -122,36 +258,12 @@ contract OVM_StateManager is iOVM_StateManager {
return verifiedContractStorage[_contract][_key]; return verifiedContractStorage[_contract][_key];
} }
function testAndSetAccountLoaded( /**
address _address * Checks whether a storage slot has already been retrieved, and marks it as retrieved if not.
) * @param _contract Address of the contract to check.
override * @param _key 32 byte storage slot key.
public * @return _wasContractStorageAlreadyLoaded Whether or not the slot was already loaded.
returns ( */
bool _wasAccountAlreadyLoaded
)
{
return _testItemState(
keccak256(abi.encodePacked(_address)),
ItemState.ITEM_LOADED
);
}
function testAndSetAccountChanged(
address _address
)
override
public
returns (
bool _wasAccountAlreadyChanged
)
{
return _testItemState(
keccak256(abi.encodePacked(_address)),
ItemState.ITEM_CHANGED
);
}
function testAndSetContractStorageLoaded( function testAndSetContractStorageLoaded(
address _contract, address _contract,
bytes32 _key bytes32 _key
...@@ -168,6 +280,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -168,6 +280,12 @@ contract OVM_StateManager is iOVM_StateManager {
); );
} }
/**
* Checks whether a storage slot has already been modified, and marks it as modified if not.
* @param _contract Address of the contract to check.
* @param _key 32 byte storage slot key.
* @return _wasContractStorageAlreadyChanged Whether or not the slot was already modified.
*/
function testAndSetContractStorageChanged( function testAndSetContractStorageChanged(
address _contract, address _contract,
bytes32 _key bytes32 _key
...@@ -185,10 +303,17 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -185,10 +303,17 @@ contract OVM_StateManager is iOVM_StateManager {
} }
/* /**********************
* Internal Functions * Internal Functions *
*/ **********************/
/**
* Checks whether an item is in a particular state (ITEM_LOADED or ITEM_CHANGED) and sets the
* item to the provided state if not.
* @param _item 32 byte item ID to check.
* @param _minItemState Minumum state that must be satisfied by the item.
* @return _wasItemState Whether or not the item was already in the state.
*/
function _testItemState( function _testItemState(
bytes32 _item, bytes32 _item,
ItemState _minItemState ItemState _minItemState
......
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_ECDSAContractAccount
*/
interface iOVM_ECDSAContractAccount {
/********************
* Public Functions *
********************/
function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
) external returns (bool _success, bytes memory _returndata);
}
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Interface Imports */ /* Library Imports */
import { iOVM_DataTypes } from "../codec/iOVM_DataTypes.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
interface iOVM_ExecutionManager { interface iOVM_ExecutionManager {
/*******************
* Data Structures *
*******************/
enum RevertFlag { enum RevertFlag {
DID_NOT_REVERT, DID_NOT_REVERT,
OUT_OF_GAS, OUT_OF_GAS,
...@@ -42,8 +46,13 @@ interface iOVM_ExecutionManager { ...@@ -42,8 +46,13 @@ interface iOVM_ExecutionManager {
RevertFlag revertFlag; RevertFlag revertFlag;
} }
/************************************
* Transaction Execution Entrypoint *
************************************/
function run( function run(
iOVM_DataTypes.OVMTransactionData calldata _transaction, Lib_OVMCodec.Transaction calldata _transaction,
address _txStateManager address _txStateManager
) external; ) external;
...@@ -73,7 +82,15 @@ interface iOVM_ExecutionManager { ...@@ -73,7 +82,15 @@ interface iOVM_ExecutionManager {
function ovmCREATE(bytes memory _bytecode) external returns (address _contract); function ovmCREATE(bytes memory _bytecode) external returns (address _contract);
function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external returns (address _contract); function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external returns (address _contract);
function safeCREATE(address _address, bytes memory _bytecode) external;
/*******************************
* Account Abstraction Opcodes *
******************************/
function ovmGETNONCE() external returns (uint256 _nonce);
function ovmSETNONCE(uint256 _nonce) external;
function ovmCREATEEOA(bytes32 _messageHash, uint8 _v, bytes32 _r, bytes32 _s) external;
/**************************** /****************************
...@@ -100,4 +117,11 @@ interface iOVM_ExecutionManager { ...@@ -100,4 +117,11 @@ interface iOVM_ExecutionManager {
function ovmEXTCODECOPY(address _contract, uint256 _offset, uint256 _length) external returns (bytes memory _code); function ovmEXTCODECOPY(address _contract, uint256 _offset, uint256 _length) external returns (bytes memory _code);
function ovmEXTCODESIZE(address _contract) external returns (uint256 _size); function ovmEXTCODESIZE(address _contract) external returns (uint256 _size);
function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash); function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash);
/**************************************
* Public Functions: Execution Safety *
**************************************/
function safeCREATE(address _address, bytes memory _bytecode) external;
} }
// SPDX-License-Identifier: UNLICENSED // SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
/**
* @title iOVM_SafetyChecker
*/
interface iOVM_SafetyChecker { interface iOVM_SafetyChecker {
/********************
* Public Functions *
********************/
function isBytecodeSafe(bytes memory _bytecode) external view returns (bool _safe); function isBytecodeSafe(bytes memory _bytecode) external view returns (bool _safe);
} }
...@@ -2,25 +2,48 @@ ...@@ -2,25 +2,48 @@
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Interface Imports */ /* Library Imports */
import { iOVM_DataTypes } from "../codec/iOVM_DataTypes.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_StateManager
*/
interface iOVM_StateManager { interface iOVM_StateManager {
function putAccount(address _address, iOVM_DataTypes.OVMAccount memory _account) external;
function getAccount(address _address) external returns (iOVM_DataTypes.OVMAccount memory _account);
function hasAccount(address _address) external returns (bool _exists);
function incrementAccountNonce(address _address) external;
/*******************
* Data Structures *
*******************/
enum ItemState {
ITEM_UNTOUCHED,
ITEM_LOADED,
ITEM_CHANGED
}
/************************************
* Public Functions: Account Access *
************************************/
function putAccount(address _address, Lib_OVMCodec.Account memory _account) external;
function getAccount(address _address) external returns (Lib_OVMCodec.Account memory _account);
function hasAccount(address _address) external returns (bool _exists);
function setAccountNonce(address _address, uint256 _nonce) external;
function getAccountNonce(address _address) external returns (uint256 _nonce);
function getAccountEthAddress(address _address) external returns (address _ethAddress);
function initPendingAccount(address _address) external; function initPendingAccount(address _address) external;
function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash) external; function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash) external;
function testAndSetAccountLoaded(address _address) external returns (bool _wasAccountAlreadyLoaded);
function testAndSetAccountChanged(address _address) external returns (bool _wasAccountAlreadyChanged);
/************************************
* Public Functions: Storage Access *
************************************/
function putContractStorage(address _contract, bytes32 _key, bytes32 _value) external; function putContractStorage(address _contract, bytes32 _key, bytes32 _value) external;
function getContractStorage(address _contract, bytes32 _key) external returns (bytes32 _value); function getContractStorage(address _contract, bytes32 _key) external returns (bytes32 _value);
function hasContractStorage(address _contract, bytes32 _key) external returns (bool _exists); function hasContractStorage(address _contract, bytes32 _key) external returns (bool _exists);
function testAndSetAccountLoaded(address _address) external returns (bool _wasAccountAlreadyLoaded);
function testAndSetAccountChanged(address _address) external returns (bool _wasAccountAlreadyChanged);
function testAndSetContractStorageLoaded(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyLoaded); function testAndSetContractStorageLoaded(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyLoaded);
function testAndSetContractStorageChanged(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyChanged); function testAndSetContractStorageChanged(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyChanged);
} }
// SPDX-License-Identifier: UNLICENSED // SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
interface iOVM_DataTypes { /* Library Imports */
struct OVMAccount { import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
uint256 nonce;
uint256 balance;
bytes32 storageRoot;
bytes32 codeHash;
address ethAddress;
}
struct EVMAccount { /**
* @title Lib_OVMCodec
*/
library Lib_OVMCodec {
/*******************
* Data Structures *
*******************/
struct Account {
uint256 nonce; uint256 nonce;
uint256 balance; uint256 balance;
bytes32 storageRoot; bytes32 storageRoot;
bytes32 codeHash; bytes32 codeHash;
address ethAddress;
} }
struct OVMChainBatchHeader { struct ChainBatchHeader {
uint256 batchIndex; uint256 batchIndex;
bytes32 batchRoot; bytes32 batchRoot;
uint256 batchSize; uint256 batchSize;
...@@ -25,12 +30,12 @@ interface iOVM_DataTypes { ...@@ -25,12 +30,12 @@ interface iOVM_DataTypes {
bytes extraData; bytes extraData;
} }
struct OVMChainInclusionProof { struct ChainInclusionProof {
uint256 index; uint256 index;
bytes32[] siblings; bytes32[] siblings;
} }
struct OVMTransactionData { struct Transaction {
uint256 timestamp; uint256 timestamp;
uint256 queueOrigin; uint256 queueOrigin;
address entrypoint; address entrypoint;
...@@ -40,16 +45,57 @@ interface iOVM_DataTypes { ...@@ -40,16 +45,57 @@ interface iOVM_DataTypes {
bytes data; bytes data;
} }
struct OVMProofMatrix { struct ProofMatrix {
bool checkNonce; bool checkNonce;
bool checkBalance; bool checkBalance;
bool checkStorageRoot; bool checkStorageRoot;
bool checkCodeHash; bool checkCodeHash;
} }
struct OVMQueueElement { struct QueueElement {
uint256 timestamp; uint256 timestamp;
bytes32 batchRoot; bytes32 batchRoot;
bool isL1ToL2Batch; bool isL1ToL2Batch;
} }
struct EOATransaction {
address target;
uint256 nonce;
uint256 gasLimit;
bytes data;
}
enum EOASignatureType {
ETH_SIGNED_MESSAGE,
NATIVE_TRANSACTON
}
/*********************************************
* Internal Functions: Encoding and Decoding *
*********************************************/
/**
* Decodes an EOA transaction (i.e., native Ethereum RLP encoding).
* @param _transaction Encoded EOA transaction.
* @return _decoded Transaction decoded into a struct.
*/
function decodeEOATransaction(
bytes memory _transaction
)
internal
pure
returns (
EOATransaction memory _decoded
)
{
Lib_RLPReader.RLPItem[] memory decoded = Lib_RLPReader.toList(Lib_RLPReader.toRlpItem(_transaction));
return EOATransaction({
nonce: Lib_RLPReader.toUint(decoded[0]),
gasLimit: Lib_RLPReader.toUint(decoded[2]),
target: Lib_RLPReader.toAddress(decoded[3]),
data: Lib_RLPReader.toBytes(decoded[5])
});
}
} }
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title Lib_ECDSAUtils
*/
library Lib_ECDSAUtils {
/**************************************
* Internal Functions: ECDSA Recovery *
**************************************/
/**
* Recovers a signed address given a message and signature.
* @param _message Message that was originally signed.
* @param _isEthSignedMessage Whether or not the user used the `Ethereum Signed Message` prefix.
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
* @param _chainId Chain ID parameter.
* @return _sender Signer address.
*/
function recover(
bytes memory _message,
bool _isEthSignedMessage,
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _chainId
)
internal
pure
returns (
address _sender
)
{
bytes32 messageHash;
if (_isEthSignedMessage) {
messageHash = getEthSignedMessageHash(_message);
} else {
messageHash = getNativeMessageHash(_message);
}
return ecrecover(
messageHash,
(_v - uint8(_chainId) * 2) - 8,
_r,
_s
);
}
/*************************************
* Private Functions: ECDSA Recovery *
*************************************/
/**
* Gets the native message hash (simple keccak256) for a message.
* @param _message Message to hash.
* @return _messageHash Native message hash.
*/
function getNativeMessageHash(
bytes memory _message
)
private
pure
returns (
bytes32 _messageHash
)
{
return keccak256(_message);
}
/**
* Gets the hash of a message with the `Ethereum Signed Message` prefix.
* @param _message Message to hash.
* @return _messageHash Prefixed message hash.
*/
function getEthSignedMessageHash(
bytes memory _message
)
private
pure
returns (
bytes32 _messageHash
)
{
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 messageHash = keccak256(_message);
return keccak256(abi.encodePacked(prefix, messageHash));
}
}
\ No newline at end of file
...@@ -5,7 +5,22 @@ pragma experimental ABIEncoderV2; ...@@ -5,7 +5,22 @@ pragma experimental ABIEncoderV2;
/* Library Imports */ /* Library Imports */
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol"; import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
/**
* @title Lib_EthUtils
*/
library Lib_EthUtils { library Lib_EthUtils {
/***********************************
* Internal Functions: Code Access *
***********************************/
/**
* Gets the code for a given address.
* @param _address Address to get code for.
* @param _offset Offset to start reading from.
* @param _length Number of bytes to read.
* @return _code Code read from the contract.
*/
function getCode( function getCode(
address _address, address _address,
uint256 _offset, uint256 _offset,
...@@ -27,6 +42,11 @@ library Lib_EthUtils { ...@@ -27,6 +42,11 @@ library Lib_EthUtils {
return _code; return _code;
} }
/**
* Gets the full code for a given address.
* @param _address Address to get code for.
* @return _code Full code of the contract.
*/
function getCode( function getCode(
address _address address _address
) )
...@@ -43,6 +63,11 @@ library Lib_EthUtils { ...@@ -43,6 +63,11 @@ library Lib_EthUtils {
); );
} }
/**
* Gets the size of a contract's code in bytes.
* @param _address Address to get code size for.
* @return _codeSize Size of the contract's code in bytes.
*/
function getCodeSize( function getCodeSize(
address _address address _address
) )
...@@ -59,6 +84,11 @@ library Lib_EthUtils { ...@@ -59,6 +84,11 @@ library Lib_EthUtils {
return _codeSize; return _codeSize;
} }
/**
* Gets the hash of a contract's code.
* @param _address Address to get a code hash for.
* @return _codeHash Hash of the contract's code.
*/
function getCodeHash( function getCodeHash(
address _address address _address
) )
...@@ -75,6 +105,16 @@ library Lib_EthUtils { ...@@ -75,6 +105,16 @@ library Lib_EthUtils {
return _codeHash; return _codeHash;
} }
/*****************************************
* Internal Functions: Contract Creation *
*****************************************/
/**
* Creates a contract with some given initialization code.
* @param _code Contract initialization code.
* @return _created Address of the created contract.
*/
function createContract( function createContract(
bytes memory _code bytes memory _code
) )
...@@ -94,6 +134,12 @@ library Lib_EthUtils { ...@@ -94,6 +134,12 @@ library Lib_EthUtils {
return _created; return _created;
} }
/**
* Computes the address that would be generated by CREATE.
* @param _creator Address creating the contract.
* @param _nonce Creator's nonce.
* @return _address Address to be generated by CREATE.
*/
function getAddressForCREATE( function getAddressForCREATE(
address _creator, address _creator,
uint256 _nonce uint256 _nonce
...@@ -112,6 +158,13 @@ library Lib_EthUtils { ...@@ -112,6 +158,13 @@ library Lib_EthUtils {
return getAddressFromHash(keccak256(encodedList)); return getAddressFromHash(keccak256(encodedList));
} }
/**
* Computes the address that would be generated by CREATE2.
* @param _creator Address creating the contract.
* @param _bytecode Bytecode of the contract to be created.
* @param _salt 32 byte salt value mixed into the hash.
* @return _address Address to be generated by CREATE2.
*/
function getAddressForCREATE2( function getAddressForCREATE2(
address _creator, address _creator,
bytes memory _bytecode, bytes memory _bytecode,
...@@ -131,19 +184,26 @@ library Lib_EthUtils { ...@@ -131,19 +184,26 @@ library Lib_EthUtils {
return getAddressFromHash(hashedData); return getAddressFromHash(hashedData);
} }
/****************************************
* Private Functions: Contract Creation *
****************************************/
/** /**
* Determines an address from a 32 byte hash. Since addresses are only * Determines an address from a 32 byte hash. Since addresses are only
* 20 bytes, we need to retrieve the last 20 bytes from the original * 20 bytes, we need to retrieve the last 20 bytes from the original
* hash. Converting to uint256 and then uint160 gives us these bytes. * hash. Converting to uint256 and then uint160 gives us these bytes.
* @param _hash Hash to convert to an address. * @param _hash Hash to convert to an address.
* @return Hash converted to an address. * @return _address Hash converted to an address.
*/ */
function getAddressFromHash( function getAddressFromHash(
bytes32 _hash bytes32 _hash
) )
private private
pure pure
returns (address) returns (
address _address
)
{ {
return address(bytes20(uint160(uint256(_hash)))); return address(bytes20(uint160(uint256(_hash))));
} }
......
import { expect } from '../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
describe('Proxy_Forwarder', () => {
describe('fallback', () => {
})
})
import { expect } from '../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
describe('Proxy_Manager', () => {
describe('setProxy', () => {
})
describe('getProxy', () => {
})
describe('hasProxy', () => {
})
describe('isProxy', () => {
})
describe('setTarget', () => {
})
describe('getTarget', () => {
})
describe('hasTarget', () => {
})
describe('isTarget', () => {
})
})
import { expect } from '../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
describe('Proxy_Resolver', () => {
describe('resolve', () => {
})
})
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