Commit cc02fdf7 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Merge pull request #1720 from ethereum-optimism/feat/allow-unprotected-txs

l2geth: allow for unprotected txs
parents 4c35ece9 6bd6768e
---
'@eth-optimism/data-transport-layer': patch
---
Handle unprotected transactions
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/l2geth': patch
---
Allow for unprotected transactions
......@@ -55,7 +55,7 @@ jobs:
if: failure()
uses: jwalton/gh-docker-logs@v1
with:
images: 'ethereumoptimism/hardhat,ops_deployer,ops_dtl,ethereumoptimism/l2geth,ethereumoptimism/message-relayer,ops_batch_submitter,ethereumoptimism/l2geth,ops_integration_tests'
images: 'ethereumoptimism/hardhat,ops_deployer,ops_dtl,ops_l2geth,ethereumoptimism/message-relayer,ops_batch_submitter,ops_replica,ops_integration_tests'
dest: '/home/runner/logs'
- name: Tar logs
......
......@@ -31,6 +31,7 @@
"@eth-optimism/contracts": "0.5.4",
"@eth-optimism/core-utils": "0.7.2",
"@eth-optimism/message-relayer": "0.2.4",
"@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/providers": "^5.4.5",
"@ethersproject/transactions": "^5.4.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
......
import { OptimismEnv } from './shared/env'
import {
defaultTransactionFactory,
gasPriceForL2,
sleep,
isLiveNetwork,
} from './shared/utils'
import { expect } from 'chai'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
describe('Replica Tests', () => {
let env: OptimismEnv
before(async () => {
env = await OptimismEnv.new()
})
describe('Matching blocks', () => {
if (isLiveNetwork()) {
console.log('Skipping replica tests on live network')
return
}
it('should sync a transaction', async () => {
const tx = defaultTransactionFactory()
tx.gasPrice = await gasPriceForL2(env)
const result = await env.l2Wallet.sendTransaction(tx)
let receipt: TransactionReceipt
while (!receipt) {
receipt = await env.replicaProvider.getTransactionReceipt(result.hash)
await sleep(200)
}
const sequencerBlock = (await env.l2Provider.getBlock(
result.blockNumber
)) as any
const replicaBlock = (await env.replicaProvider.getBlock(
result.blockNumber
)) as any
expect(sequencerBlock.stateRoot).to.deep.eq(replicaBlock.stateRoot)
expect(sequencerBlock.hash).to.deep.eq(replicaBlock.hash)
})
it('sync an unprotected tx (eip155)', async () => {
const tx = {
...defaultTransactionFactory(),
nonce: await env.l2Wallet.getTransactionCount(),
gasPrice: await gasPriceForL2(env),
chainId: null, // Disables EIP155 transaction signing.
}
const signed = await env.l2Wallet.signTransaction(tx)
const result = await env.l2Provider.sendTransaction(signed)
let receipt: TransactionReceipt
while (!receipt) {
receipt = await env.replicaProvider.getTransactionReceipt(result.hash)
await sleep(200)
}
const sequencerBlock = (await env.l2Provider.getBlock(
result.blockNumber
)) as any
const replicaBlock = (await env.replicaProvider.getBlock(
result.blockNumber
)) as any
expect(sequencerBlock.stateRoot).to.deep.eq(replicaBlock.stateRoot)
expect(sequencerBlock.hash).to.deep.eq(replicaBlock.hash)
})
})
})
......@@ -84,16 +84,19 @@ describe('Basic RPC tests', () => {
).to.be.rejectedWith('invalid transaction: invalid sender')
})
it('should not accept a transaction without a chain ID', async () => {
it('should accept a transaction without a chain ID', async () => {
const tx = {
...defaultTransactionFactory(),
nonce: await wallet.getTransactionCount(),
gasPrice: await gasPriceForL2(env),
chainId: null, // Disables EIP155 transaction signing.
}
const signed = await wallet.signTransaction(tx)
const response = await provider.sendTransaction(signed)
await expect(
provider.sendTransaction(await wallet.signTransaction(tx))
).to.be.rejectedWith('Cannot submit unprotected transaction')
expect(response.chainId).to.equal(0)
const v = response.v
expect(v === 27 || v === 28).to.be.true
})
it('should accept a transaction with a value', async () => {
......
......@@ -10,6 +10,7 @@ import {
getAddressManager,
l1Provider,
l2Provider,
replicaProvider,
l1Wallet,
l2Wallet,
fundUser,
......@@ -52,6 +53,7 @@ export class OptimismEnv {
// The providers
l1Provider: providers.JsonRpcProvider
l2Provider: providers.JsonRpcProvider
replicaProvider: providers.JsonRpcProvider
constructor(args: any) {
this.addressManager = args.addressManager
......@@ -67,6 +69,7 @@ export class OptimismEnv {
this.l2Wallet = args.l2Wallet
this.l1Provider = args.l1Provider
this.l2Provider = args.l2Provider
this.replicaProvider = args.replicaProvider
this.ctc = args.ctc
this.scc = args.scc
}
......@@ -126,6 +129,7 @@ export class OptimismEnv {
l2Wallet,
l1Provider,
l2Provider,
replicaProvider,
})
}
......
......@@ -55,13 +55,19 @@ const env = cleanEnv(process.env, {
export const l1Provider = new providers.JsonRpcProvider(env.L1_URL)
l1Provider.pollingInterval = env.L1_POLLING_INTERVAL
export const l2Provider = new providers.JsonRpcProvider(env.L2_URL)
export const l2Provider = injectL2Context(
new providers.JsonRpcProvider(env.L2_URL)
)
l2Provider.pollingInterval = env.L2_POLLING_INTERVAL
export const verifierProvider = new providers.JsonRpcProvider(env.VERIFIER_URL)
export const verifierProvider = injectL2Context(
new providers.JsonRpcProvider(env.VERIFIER_URL)
)
verifierProvider.pollingInterval = env.VERIFIER_POLLING_INTERVAL
export const replicaProvider = new providers.JsonRpcProvider(env.REPLICA_URL)
export const replicaProvider = injectL2Context(
new providers.JsonRpcProvider(env.REPLICA_URL)
)
replicaProvider.pollingInterval = env.REPLICA_POLLING_INTERVAL
// The sequencer private key which is funded on L1
......
......@@ -1573,9 +1573,6 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
if !tx.Protected() {
return common.Hash{}, errors.New("Cannot submit unprotected transaction")
}
if err := b.SendTx(ctx, tx); err != nil {
return common.Hash{}, err
}
......
......@@ -134,8 +134,8 @@ type RollupClient interface {
// Client is an HTTP based RollupClient
type Client struct {
client *resty.Client
signer *types.EIP155Signer
client *resty.Client
chainID *big.Int
}
// TransactionResponse represents the response from the remote server when
......@@ -166,11 +166,10 @@ func NewClient(url string, chainID *big.Int) *Client {
}
return nil
})
signer := types.NewEIP155Signer(chainID)
return &Client{
client: client,
signer: &signer,
client: client,
chainID: chainID,
}
}
......@@ -322,7 +321,7 @@ func (c *Client) GetLatestTransactionBatchIndex() (*uint64, error) {
// batchedTransactionToTransaction converts a transaction into a
// types.Transaction that can be consumed by the SyncService
func batchedTransactionToTransaction(res *transaction, signer *types.EIP155Signer) (*types.Transaction, error) {
func batchedTransactionToTransaction(res *transaction, chainID *big.Int) (*types.Transaction, error) {
// `nil` transactions are not found
if res == nil {
return nil, errElementNotFound
......@@ -373,7 +372,15 @@ func batchedTransactionToTransaction(res *transaction, signer *types.EIP155Signe
sig := make([]byte, crypto.SignatureLength)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = byte(res.Decoded.Signature.V)
var signer types.Signer
if res.Decoded.Signature.V == 27 || res.Decoded.Signature.V == 28 {
signer = types.HomesteadSigner{}
sig[64] = byte(res.Decoded.Signature.V - 27)
} else {
signer = types.NewEIP155Signer(chainID)
sig[64] = byte(res.Decoded.Signature.V)
}
tx, err := tx.WithSignature(signer, sig[:])
if err != nil {
......@@ -431,7 +438,7 @@ func (c *Client) GetTransaction(index uint64, backend Backend) (*types.Transacti
if !ok {
return nil, fmt.Errorf("could not get tx with index %d", index)
}
return batchedTransactionToTransaction(res.Transaction, c.signer)
return batchedTransactionToTransaction(res.Transaction, c.chainID)
}
// GetLatestTransaction will get the latest transaction, meaning the transaction
......@@ -452,7 +459,7 @@ func (c *Client) GetLatestTransaction(backend Backend) (*types.Transaction, erro
return nil, errors.New("Cannot get latest transaction")
}
return batchedTransactionToTransaction(res.Transaction, c.signer)
return batchedTransactionToTransaction(res.Transaction, c.chainID)
}
// GetEthContext will return the EthContext by block number
......@@ -564,7 +571,7 @@ func (c *Client) GetLatestTransactionBatch() (*Batch, []*types.Transaction, erro
if !ok {
return nil, nil, fmt.Errorf("Cannot parse transaction batch response")
}
return parseTransactionBatchResponse(txBatch, c.signer)
return parseTransactionBatchResponse(txBatch, c.chainID)
}
// GetTransactionBatch will return the transaction batch by batch index
......@@ -584,19 +591,19 @@ func (c *Client) GetTransactionBatch(index uint64) (*Batch, []*types.Transaction
if !ok {
return nil, nil, fmt.Errorf("Cannot parse transaction batch response")
}
return parseTransactionBatchResponse(txBatch, c.signer)
return parseTransactionBatchResponse(txBatch, c.chainID)
}
// parseTransactionBatchResponse will turn a TransactionBatchResponse into a
// Batch and its corresponding types.Transactions
func parseTransactionBatchResponse(txBatch *TransactionBatchResponse, signer *types.EIP155Signer) (*Batch, []*types.Transaction, error) {
func parseTransactionBatchResponse(txBatch *TransactionBatchResponse, chainID *big.Int) (*Batch, []*types.Transaction, error) {
if txBatch == nil || txBatch.Batch == nil {
return nil, nil, errElementNotFound
}
batch := txBatch.Batch
txs := make([]*types.Transaction, len(txBatch.Transactions))
for i, tx := range txBatch.Transactions {
transaction, err := batchedTransactionToTransaction(tx, signer)
transaction, err := batchedTransactionToTransaction(tx, chainID)
if err != nil {
return nil, nil, fmt.Errorf("Cannot parse transaction batch: %w", err)
}
......
......@@ -165,7 +165,7 @@ services:
depends_on:
- dtl
deploy:
replicas: 0
replicas: 1
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.geth
......@@ -181,8 +181,8 @@ services:
ETH1_CTC_DEPLOYMENT_HEIGHT: 8
RETRIES: 60
ports:
- ${L2GETH_HTTP_PORT:-8549}:8545
- ${L2GETH_WS_PORT:-8550}:8546
- ${REPLICA_HTTP_PORT:-8549}:8545
- ${REPLICA_WS_PORT:-8550}:8546
integration_tests:
deploy:
......@@ -195,6 +195,8 @@ services:
environment:
L1_URL: http://l1_chain:8545
L2_URL: http://l2geth:8545
REPLICA_URL: http://replica:8545
VERIFIER_URL: http://verifier:8545
URL: http://deployer:8081/addresses.json
ENABLE_GAS_REPORT: 1
NO_NETWORK: 1
......
......@@ -2,8 +2,14 @@
import { ethers } from 'ethers'
export const parseSignatureVParam = (
v: number | ethers.BigNumber,
v: number | ethers.BigNumber | string,
chainId: number
): number => {
return ethers.BigNumber.from(v).toNumber() - 2 * chainId - 35
v = ethers.BigNumber.from(v).toNumber()
// Handle unprotected transactions
if (v === 27 || v === 28) {
return v
}
// Handle EIP155 transactions
return v - 2 * chainId - 35
}
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