Skip to content

Commit

Permalink
feat[contracts]: Use standard RLP transaction format (#566)
Browse files Browse the repository at this point in the history
* feat[contracts]: Use standard RLP transaction format

* fix[l2geth]: Encode transaction as RLP

* fix: Correct gas estimation in integration tests

* fix: Correct gas estimation in integration tests

* Update packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_SequencerEntrypoint.sol

Co-authored-by: ben-chain <ben@pseudonym.party>

* fix[contracts]: Use isCreate instead of checking target address

* fix[contracts]: Minor optimization in SequencerEntrypoint

* fix[contracts]: Pass max gas to contract call in EOA contract

Co-authored-by: ben-chain <ben@pseudonym.party>
  • Loading branch information
2 people authored and karlfloersch committed Apr 24, 2021
1 parent 2784fe8 commit 24cd2e7
Show file tree
Hide file tree
Showing 22 changed files with 1,190 additions and 1,030 deletions.
96 changes: 2 additions & 94 deletions l2geth/rollup/sync_service.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package rollup

import (
"bytes"
"context"
"errors"
"fmt"
Expand All @@ -11,11 +10,11 @@ import (
"sync/atomic"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"

"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -707,7 +706,6 @@ func (s *SyncService) maybeApplyTransaction(tx *types.Transaction) error {
// Lower level API used to apply a transaction, must only be used with
// transactions that came from L1.
func (s *SyncService) applyTransaction(tx *types.Transaction) error {
tx = fixType(tx)
txs := types.Transactions{tx}
s.txFeed.Send(core.NewTxsEvent{Txs: txs})
return nil
Expand Down Expand Up @@ -747,7 +745,7 @@ func (s *SyncService) ApplyTransaction(tx *types.Transaction) error {
}

// Set the raw transaction data in the meta
txRaw, err := getRawTransaction(tx)
txRaw, err := rlp.EncodeToBytes(tx)
if err != nil {
return fmt.Errorf("invalid transaction: %w", err)
}
Expand All @@ -766,93 +764,3 @@ func (s *SyncService) ApplyTransaction(tx *types.Transaction) error {

return s.applyTransaction(tx)
}

func getRawTransaction(tx *types.Transaction) ([]byte, error) {
if tx == nil {
return nil, errors.New("Cannot process nil transaction")
}
v, r, s := tx.RawSignatureValues()

// V parameter here will include the chain ID, so we need to recover the original V. If the V
// does not equal zero or one, we have an invalid parameter and need to throw an error.
// This is technically a duplicate check because it happens inside of
// `tx.AsMessage` as well.
v = new(big.Int).SetUint64(v.Uint64() - 35 - 2*tx.ChainId().Uint64())
if v.Uint64() != 0 && v.Uint64() != 1 {
return nil, fmt.Errorf("invalid signature v parameter: %d", v.Uint64())
}

// Since we use a fixed encoding, we need to insert some placeholder address to represent that
// the user wants to create a contract (in this case, the zero address).
var target common.Address
if tx.To() == nil {
target = common.Address{}
} else {
target = *tx.To()
}

// Divide the gas price by one million to compress it
// before it is send to the sequencer entrypoint. This is to save
// space on calldata.
gasPrice := new(big.Int).Div(tx.GasPrice(), new(big.Int).SetUint64(1000000))

// Sequencer uses a custom encoding structure --
// We originally receive sequencer transactions encoded in this way, but we decode them before
// inserting into Geth so we can make transactions easily parseable. However, this means that
// we need to re-encode the transactions before executing them.
var data = new(bytes.Buffer)
data.WriteByte(getSignatureType(tx)) // 1 byte: 00 == EIP 155, 02 == ETH Sign Message
data.Write(fillBytes(r, 32)) // 32 bytes: Signature `r` parameter
data.Write(fillBytes(s, 32)) // 32 bytes: Signature `s` parameter
data.Write(fillBytes(v, 1)) // 1 byte: Signature `v` parameter
data.Write(fillBytes(new(big.Int).SetUint64(tx.Gas()), 3)) // 3 bytes: Gas limit
data.Write(fillBytes(gasPrice, 3)) // 3 bytes: Gas price
data.Write(fillBytes(new(big.Int).SetUint64(tx.Nonce()), 3)) // 3 bytes: Nonce
data.Write(target.Bytes()) // 20 bytes: Target address
data.Write(tx.Data())

return data.Bytes(), nil
}

func fillBytes(x *big.Int, size int) []byte {
b := x.Bytes()
switch {
case len(b) > size:
panic("math/big: value won't fit requested size")
case len(b) == size:
return b
default:
buf := make([]byte, size)
copy(buf[size-len(b):], b)
return buf
}
}

func getSignatureType(tx *types.Transaction) uint8 {
if tx.SignatureHashType() == 0 {
return 0
} else if tx.SignatureHashType() == 1 {
return 2
} else {
return 1
}
}

// This is a temporary fix to patch the enums being used in the raw data
func fixType(tx *types.Transaction) *types.Transaction {
meta := tx.GetMeta()
raw := meta.RawTransaction
if len(raw) == 0 {
log.Error("Transaction with no raw detected")
return tx
}
if raw[0] == 0x00 {
return tx
} else if raw[0] == 0x01 {
raw[0] = 0x02
}
queueOrigin := types.QueueOrigin(meta.QueueOrigin.Uint64())
fixed := types.NewTransactionMeta(meta.L1BlockNumber, meta.L1Timestamp, meta.L1MessageSender, meta.SignatureHashType, queueOrigin, meta.Index, meta.QueueIndex, raw)
tx.SetTransactionMeta(fixed)
return tx
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,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_ExecutionManagerWrapper } from "../../libraries/wrappers/Lib_ExecutionManagerWrapper.sol";

/* Contract Imports */
Expand All @@ -20,14 +19,21 @@ import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
/**
* @title OVM_ECDSAContractAccount
* @dev The ECDSA Contract Account can be used as the implementation for a ProxyEOA deployed by the
* ovmCREATEEOA operation. It enables backwards compatibility with Ethereum's Layer 1, by
* providing eth_sign and EIP155 formatted transaction encodings.
* ovmCREATEEOA operation. It enables backwards compatibility with Ethereum's Layer 1, by
* providing EIP155 formatted transaction encodings.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {

/*************
* Libraries *
*************/

using Lib_EIP155Tx for Lib_EIP155Tx.EIP155Tx;


/*************
* Constants *
*************/
Expand All @@ -44,20 +50,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 @@ -66,66 +64,46 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
bytes memory
)
{
bool isEthSign = _signatureType == Lib_OVMCodec.EOASignatureType.ETH_SIGNED_MESSAGE;
// Attempt to decode the transaction.
Lib_EIP155Tx.EIP155Tx memory transaction = Lib_EIP155Tx.decode(
_encodedTransaction,
Lib_ExecutionManagerWrapper.ovmCHAINID()
);

// 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.
require(
Lib_ECDSAUtils.recover(
_transaction,
isEthSign,
_v,
_r,
_s
) == address(this),
transaction.sender() == address(this),
"Signature provided for EOA transaction execution is invalid."
);

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

// Grab the chain ID of the current network.
uint256 chainId;
assembly {
chainId := chainid()
}

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

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

// TEMPORARY: Disable gas checks for mainnet.
// // Need to make sure that the gas is sufficient to execute the transaction.
// require(
// gasleft() >= SafeMath.add(decodedTx.gasLimit, EXECUTION_VALIDATION_GAS_OVERHEAD),
// gasleft() >= SafeMath.add(transaction.gasLimit, EXECUTION_VALIDATION_GAS_OVERHEAD),
// "Gas is not sufficient to execute the transaction."
// );

// Transfer fee to relayer.
require(
ovmETH.transfer(
msg.sender,
SafeMath.mul(decodedTx.gasLimit, decodedTx.gasPrice)
SafeMath.mul(transaction.gasLimit, transaction.gasPrice)
),
"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, bytes memory revertdata) = Lib_ExecutionManagerWrapper.ovmCREATE(
decodedTx.data
transaction.data
);

// Return true if the contract creation succeeded, false w/ revertdata otherwise.
Expand All @@ -140,7 +118,7 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
// cases, but since this is a contract we'd end up bumping the nonce twice.
Lib_ExecutionManagerWrapper.ovmINCREMENTNONCE();

return decodedTx.to.call(decodedTx.data);
return transaction.to.call(transaction.data);
}
}
}
Loading

0 comments on commit 24cd2e7

Please sign in to comment.