Skip to content
This repository has been archived by the owner on Apr 12, 2021. It is now read-only.

feat: Use standard RLP transactions #300

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ pragma experimental ABIEncoderV2;
import { iOVM_ECDSAContractAccount } from "../../iOVM/accounts/iOVM_ECDSAContractAccount.sol";

/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
import { Lib_EIP155Tx } from "../../libraries/codec/Lib_EIP155Tx.sol";
import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol";
import { Lib_SafeMathWrapper } from "../../libraries/wrappers/Lib_SafeMathWrapper.sol";

Expand All @@ -21,6 +20,8 @@ import { Lib_SafeMathWrapper } from "../../libraries/wrappers/Lib_SafeMathWrappe
* Runtime target: OVM
*/
contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
using Lib_EIP155Tx for Lib_EIP155Tx.EIP155Tx;


/*************
* Constants *
Expand All @@ -38,20 +39,12 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {

/**
* 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.
* @param _encodedTransaction Signed EIP155 transaction.
* @return Whether or not the call returned (rather than reverted).
* @return Data returned by the call.
*/
function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
bytes memory _encodedTransaction
)
override
public
Expand All @@ -60,34 +53,32 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
bytes memory
)
{
bool isEthSign = _signatureType == Lib_OVMCodec.EOASignatureType.ETH_SIGNED_MESSAGE;
Lib_EIP155Tx.EIP155Tx memory transaction = Lib_EIP155Tx.decode(
_encodedTransaction,
Lib_SafeExecutionManagerWrapper.safeCHAINID()
);

// Recovery parameter being something other than 0 or 1 indicates that this transaction was
// signed using the wrong chain ID. We really should have this logic inside of Lib_EIP155Tx
// but I'd prefer not to add the "safeREQUIRE" logic into that library. So it'll live here
// for now.
Lib_SafeExecutionManagerWrapper.safeREQUIRE(
transaction.recoveryParam < 2,
"OVM_ECDSAContractAccount: Transaction was signed with the wrong chain ID."
);

// 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.
// Need to make sure that the transaction nonce is right and bump it if so.
Lib_SafeExecutionManagerWrapper.safeREQUIRE(
Lib_ECDSAUtils.recover(
_transaction,
isEthSign,
_v,
_r,
_s
) == Lib_SafeExecutionManagerWrapper.safeADDRESS(),
transaction.sender() == Lib_SafeExecutionManagerWrapper.safeADDRESS(),
"Signature provided for EOA transaction execution is invalid."
);

Lib_OVMCodec.EIP155Transaction memory decodedTx = Lib_OVMCodec.decodeEIP155Transaction(_transaction, isEthSign);

// Need to make sure that the transaction chainId is correct.
Lib_SafeExecutionManagerWrapper.safeREQUIRE(
decodedTx.chainId == Lib_SafeExecutionManagerWrapper.safeCHAINID(),
"Transaction chainId does not match expected OVM chainId."
);

// Need to make sure that the transaction nonce is right.
Lib_SafeExecutionManagerWrapper.safeREQUIRE(
decodedTx.nonce == Lib_SafeExecutionManagerWrapper.safeGETNONCE(),
transaction.nonce == Lib_SafeExecutionManagerWrapper.safeGETNONCE(),
"Transaction nonce does not match the expected nonce."
);

Expand All @@ -98,24 +89,30 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
// "Gas is not sufficient to execute the transaction."
// );

// Transfer fee to relayer.
// Transfer fee to relayer. We assume that whoever called this function is the relayer,
// hence the usage of CALLER. Fee is computed as gasLimit * gasPrice.
address relayer = Lib_SafeExecutionManagerWrapper.safeCALLER();
uint256 fee = Lib_SafeMathWrapper.mul(decodedTx.gasLimit, decodedTx.gasPrice);
uint256 fee = Lib_SafeMathWrapper.mul(transaction.gasLimit, transaction.gasPrice);
(bool success, ) = Lib_SafeExecutionManagerWrapper.safeCALL(
gasleft(),
ETH_ERC20_ADDRESS,
abi.encodeWithSignature("transfer(address,uint256)", relayer, fee)
abi.encodeWithSignature(
"transfer(address,uint256)",
relayer,
fee
)
);

// Make sure the transfer was actually successful.
Lib_SafeExecutionManagerWrapper.safeREQUIRE(
success == true,
"Fee was not transferred to relayer."
);

// Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.to == address(0)) {
if (transaction.isCreate) {
address created = Lib_SafeExecutionManagerWrapper.safeCREATE(
decodedTx.gasLimit,
decodedTx.data
transaction.gasLimit,
transaction.data
);

// EVM doesn't tell us whether a contract creation failed, even if it reverted during
Expand All @@ -125,12 +122,12 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
// We only want to bump the nonce for `ovmCALL` because `ovmCREATE` automatically bumps
// the nonce of the calling account. Normally an EOA would bump the nonce for both
// cases, but since this is a contract we'd end up bumping the nonce twice.
Lib_SafeExecutionManagerWrapper.safeSETNONCE(decodedTx.nonce + 1);
Lib_SafeExecutionManagerWrapper.safeSETNONCE(transaction.nonce + 1);

return Lib_SafeExecutionManagerWrapper.safeCALL(
decodedTx.gasLimit,
decodedTx.to,
decodedTx.data
transaction.gasLimit,
transaction.to,
transaction.data
);
}
}
Expand Down
2 changes: 0 additions & 2 deletions contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ pragma solidity >0.5.0 <0.8.0;

/* Library Imports */
import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Files were unused, I removed them.

import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
pragma solidity >0.5.0 <0.8.0;

/* Library Imports */
import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
import { Lib_EIP155Tx } from "../../libraries/codec/Lib_EIP155Tx.sol";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This file gets way simpler.

import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol";

/**
Expand All @@ -19,109 +17,55 @@ import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_Sa
* Runtime target: OVM
*/
contract OVM_SequencerEntrypoint {

/*********
* Enums *
*********/

enum TransactionType {
NATIVE_ETH_TRANSACTION,
ETH_SIGNED_MESSAGE
}
using Lib_EIP155Tx for Lib_EIP155Tx.EIP155Tx;


/*********************
* Fallback Function *
*********************/

/**
* Uses a custom "compressed" format to save on calldata gas:
* calldata[00:01]: transaction type (0 == EIP 155, 2 == Eth Sign Message)
* calldata[01:33]: signature "r" parameter
* calldata[33:65]: signature "s" parameter
* calldata[65:66]: signature "v" parameter
* calldata[66:69]: transaction gas limit
* calldata[69:72]: transaction gas price
* calldata[72:75]: transaction nonce
* calldata[75:95]: transaction target address
* calldata[95:XX]: transaction data
* Expects an RLP-encoded EIP155 transaction as input. See the EIP for a more detailed
* description of this transaction format:
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
*/
fallback()
external
{
TransactionType transactionType = _getTransactionType(Lib_BytesUtils.toUint8(msg.data, 0));

bytes32 r = Lib_BytesUtils.toBytes32(Lib_BytesUtils.slice(msg.data, 1, 32));
bytes32 s = Lib_BytesUtils.toBytes32(Lib_BytesUtils.slice(msg.data, 33, 32));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Removes the calls to slice which is good

uint8 v = Lib_BytesUtils.toUint8(msg.data, 65);

// Remainder is the transaction to execute.
bytes memory compressedTx = Lib_BytesUtils.slice(msg.data, 66);
bool isEthSignedMessage = transactionType == TransactionType.ETH_SIGNED_MESSAGE;

// Need to decompress and then re-encode the transaction based on the original encoding.
bytes memory encodedTx = Lib_OVMCodec.encodeEIP155Transaction(
Lib_OVMCodec.decompressEIP155Transaction(compressedTx),
isEthSignedMessage
Lib_EIP155Tx.EIP155Tx memory transaction = Lib_EIP155Tx.decode(
msg.data,
Lib_SafeExecutionManagerWrapper.safeCHAINID()
);

address target = Lib_ECDSAUtils.recover(
encodedTx,
isEthSignedMessage,
uint8(v),
r,
s
// Recovery parameter being something other than 0 or 1 indicates that this transaction was
// signed using the wrong chain ID. We really should have this logic inside of the
Lib_SafeExecutionManagerWrapper.safeREQUIRE(
transaction.recoveryParam < 2,
"OVM_SequencerEntrypoint: Transaction was signed with the wrong chain ID."
);

if (Lib_SafeExecutionManagerWrapper.safeEXTCODESIZE(target) == 0) {
// ProxyEOA has not yet been deployed for this EOA.
bytes32 messageHash = Lib_ECDSAUtils.getMessageHash(encodedTx, isEthSignedMessage);
Lib_SafeExecutionManagerWrapper.safeCREATEEOA(messageHash, uint8(v), r, s);
}
// Cache this result since we use it twice. Maybe we could move this caching into
// Lib_EIP155Tx but I'd rather not make optimizations like that right now.
address sender = transaction.sender();

// ProxyEOA has been deployed for this EOA, continue to CALL.
bytes memory callbytes = abi.encodeWithSignature(
"execute(bytes,uint8,uint8,bytes32,bytes32)",
encodedTx,
isEthSignedMessage,
uint8(v),
r,
s
);
// Create an EOA contract for this account if it doesn't already exist.
if (Lib_SafeExecutionManagerWrapper.safeEXTCODESIZE(sender) == 0) {
Lib_SafeExecutionManagerWrapper.safeCREATEEOA(
transaction.hash(),
transaction.recoveryParam,
transaction.r,
transaction.s
);
}

// Now call into the EOA contract (which should definitely exist).
Lib_SafeExecutionManagerWrapper.safeCALL(
gasleft(),
target,
callbytes
sender,
abi.encodeWithSignature(
"execute(bytes)",
msg.data
)
);
}


/**********************
* Internal Functions *
**********************/

/**
* Converts a uint256 into a TransactionType enum.
* @param _transactionType Transaction type index.
* @return Transaction type enum value.
*/
function _getTransactionType(
uint8 _transactionType
)
internal
returns (
TransactionType
)
{
if (_transactionType == 0) {
return TransactionType.NATIVE_ETH_TRANSACTION;
} if (_transactionType == 2) {
return TransactionType.ETH_SIGNED_MESSAGE;
} else {
Lib_SafeExecutionManagerWrapper.safeREVERT(
"Transaction type must be 0 or 2"
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;

/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";

/**
* @title iOVM_ECDSAContractAccount
*/
Expand All @@ -15,10 +12,11 @@ interface iOVM_ECDSAContractAccount {
********************/

function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
) external returns (bool _success, bytes memory _returndata);
bytes memory _encodedTransaction
)
external
returns (
bool,
bytes memory
);
}
Loading