Commit a6f578c8 authored by Matthew Slipper's avatar Matthew Slipper

Merge remote-tracking branch 'upstream/develop' into feat/bss-multiple-txs

parents 36a43683 cebc747c
---
'@eth-optimism/proxyd': minor
---
Add X-Forwarded-For header when proxying RPCs on proxyd
---
'@eth-optimism/contracts': patch
---
Update hardhat task for managing the gas oracle
...@@ -7,16 +7,18 @@ import ( ...@@ -7,16 +7,18 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus"
"io" "io"
"io/ioutil" "io/ioutil"
"math" "math"
"math/rand" "math/rand"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus"
) )
const ( const (
...@@ -84,6 +86,8 @@ type Backend struct { ...@@ -84,6 +86,8 @@ type Backend struct {
maxRPS int maxRPS int
maxWSConns int maxWSConns int
outOfServiceInterval time.Duration outOfServiceInterval time.Duration
stripTrailingXFF bool
proxydIP string
} }
type BackendOpt func(b *Backend) type BackendOpt func(b *Backend)
...@@ -140,6 +144,18 @@ func WithTLSConfig(tlsConfig *tls.Config) BackendOpt { ...@@ -140,6 +144,18 @@ func WithTLSConfig(tlsConfig *tls.Config) BackendOpt {
} }
} }
func WithStrippedTrailingXFF() BackendOpt {
return func(b *Backend) {
b.stripTrailingXFF = true
}
}
func WithProxydIP(ip string) BackendOpt {
return func(b *Backend) {
b.proxydIP = ip
}
}
func NewBackend( func NewBackend(
name string, name string,
rpcURL string, rpcURL string,
...@@ -163,6 +179,10 @@ func NewBackend( ...@@ -163,6 +179,10 @@ func NewBackend(
opt(backend) opt(backend)
} }
if !backend.stripTrailingXFF && backend.proxydIP == "" {
log.Warn("proxied requests' XFF header will not contain the proxyd ip address")
}
return backend return backend
} }
...@@ -316,7 +336,18 @@ func (b *Backend) doForward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error ...@@ -316,7 +336,18 @@ func (b *Backend) doForward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error
httpReq.SetBasicAuth(b.authUsername, b.authPassword) httpReq.SetBasicAuth(b.authUsername, b.authPassword)
} }
xForwardedFor := GetXForwardedFor(ctx)
if b.stripTrailingXFF {
ipList := strings.Split(xForwardedFor, ", ")
if len(ipList) > 0 {
xForwardedFor = ipList[0]
}
} else if b.proxydIP != "" {
xForwardedFor = fmt.Sprintf("%s, %s", xForwardedFor, b.proxydIP)
}
httpReq.Header.Set("content-type", "application/json") httpReq.Header.Set("content-type", "application/json")
httpReq.Header.Set("X-Forwarded-For", xForwardedFor)
httpRes, err := b.client.Do(httpReq) httpRes, err := b.client.Do(httpReq)
if err != nil { if err != nil {
......
...@@ -32,15 +32,16 @@ type BackendOptions struct { ...@@ -32,15 +32,16 @@ type BackendOptions struct {
} }
type BackendConfig struct { type BackendConfig struct {
Username string `toml:"username"` Username string `toml:"username"`
Password string `toml:"password"` Password string `toml:"password"`
RPCURL string `toml:"rpc_url"` RPCURL string `toml:"rpc_url"`
WSURL string `toml:"ws_url"` WSURL string `toml:"ws_url"`
MaxRPS int `toml:"max_rps"` MaxRPS int `toml:"max_rps"`
MaxWSConns int `toml:"max_ws_conns"` MaxWSConns int `toml:"max_ws_conns"`
CAFile string `toml:"ca_file"` CAFile string `toml:"ca_file"`
ClientCertFile string `toml:"client_cert_file"` ClientCertFile string `toml:"client_cert_file"`
ClientKeyFile string `toml:"client_key_file"` ClientKeyFile string `toml:"client_key_file"`
StripTrailingXFF bool `toml:"strip_trailing_xff"`
} }
type BackendsConfig map[string]*BackendConfig type BackendsConfig map[string]*BackendConfig
......
...@@ -96,6 +96,10 @@ func Start(config *Config) error { ...@@ -96,6 +96,10 @@ func Start(config *Config) error {
log.Info("using custom TLS config for backend", "name", name) log.Info("using custom TLS config for backend", "name", name)
opts = append(opts, WithTLSConfig(tlsConfig)) opts = append(opts, WithTLSConfig(tlsConfig))
} }
if cfg.StripTrailingXFF {
opts = append(opts, WithStrippedTrailingXFF())
}
opts = append(opts, WithProxydIP(os.Getenv("PROXYD_IP")))
back := NewBackend(name, rpcURL, wsURL, lim, opts...) back := NewBackend(name, rpcURL, wsURL, lim, opts...)
backendNames = append(backendNames, name) backendNames = append(backendNames, name)
backendsByName[name] = back backendsByName[name] = back
......
...@@ -5,20 +5,23 @@ import ( ...@@ -5,20 +5,23 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/rs/cors" "github.com/rs/cors"
"io"
"net/http"
"strconv"
"time"
) )
const ( const (
ContextKeyAuth = "authorization" ContextKeyAuth = "authorization"
ContextKeyReqID = "req_id" ContextKeyReqID = "req_id"
ContextKeyXForwardedFor = "x_forwarded_for"
) )
type Server struct { type Server struct {
...@@ -214,7 +217,16 @@ func (s *Server) populateContext(w http.ResponseWriter, r *http.Request) context ...@@ -214,7 +217,16 @@ func (s *Server) populateContext(w http.ResponseWriter, r *http.Request) context
return nil return nil
} }
xff := r.Header.Get("X-Forwarded-For")
if xff == "" {
ipPort := strings.Split(r.RemoteAddr, ":")
if len(ipPort) == 2 {
xff = ipPort[0]
}
}
ctx := context.WithValue(r.Context(), ContextKeyAuth, s.authenticatedPaths[authorization]) ctx := context.WithValue(r.Context(), ContextKeyAuth, s.authenticatedPaths[authorization])
ctx = context.WithValue(ctx, ContextKeyXForwardedFor, xff)
return context.WithValue( return context.WithValue(
ctx, ctx,
ContextKeyReqID, ContextKeyReqID,
...@@ -271,3 +283,11 @@ func GetReqID(ctx context.Context) string { ...@@ -271,3 +283,11 @@ func GetReqID(ctx context.Context) string {
} }
return reqId return reqId
} }
func GetXForwardedFor(ctx context.Context) string {
xff, ok := ctx.Value(ContextKeyXForwardedFor).(string)
if !ok {
return ""
}
return xff
}
...@@ -7,8 +7,25 @@ import { predeploys } from '../src/predeploys' ...@@ -7,8 +7,25 @@ import { predeploys } from '../src/predeploys'
import { getContractDefinition } from '../src/contract-defs' import { getContractDefinition } from '../src/contract-defs'
task('set-l2-gasprice') task('set-l2-gasprice')
.addOptionalParam('l2GasPrice', 'Gas Price to set on L2', 0, types.int) .addOptionalParam(
'l2GasPrice',
'Gas Price to set on L2',
undefined,
types.int
)
.addOptionalParam('transactionGasPrice', 'tx.gasPrice', undefined, types.int) .addOptionalParam('transactionGasPrice', 'tx.gasPrice', undefined, types.int)
.addOptionalParam(
'overhead',
'amortized additional gas used by each batch that users must pay for',
undefined,
types.int
)
.addOptionalParam(
'scalar',
'amount to scale up the gas to charge',
undefined,
types.int
)
.addOptionalParam( .addOptionalParam(
'contractsRpcUrl', 'contractsRpcUrl',
'Sequencer HTTP Endpoint', 'Sequencer HTTP Endpoint',
...@@ -42,15 +59,45 @@ task('set-l2-gasprice') ...@@ -42,15 +59,45 @@ task('set-l2-gasprice')
throw new Error(`Incorrect key. Owner ${owner}, Signer ${addr}`) throw new Error(`Incorrect key. Owner ${owner}, Signer ${addr}`)
} }
// List the current values
const gasPrice = await GasPriceOracle.callStatic.gasPrice() const gasPrice = await GasPriceOracle.callStatic.gasPrice()
console.log(`Gas Price is currently ${gasPrice.toString()}`) const scalar = await GasPriceOracle.callStatic.scalar()
console.log(`Setting Gas Price to ${args.l2GasPrice}`) const overhead = await GasPriceOracle.callStatic.overhead()
const tx = await GasPriceOracle.connect(signer).setGasPrice( console.log('Current values:')
args.l2GasPrice, console.log(`Gas Price: ${gasPrice.toString()}`)
{ gasPrice: args.transactionGasPrice } console.log(`Scalar: ${scalar.toString()}`)
) console.log(`Overhead: ${overhead.toString()}`)
if (args.l2GasPrice !== undefined) {
console.log(`Setting gas price to ${args.l2GasPrice}`)
const tx = await GasPriceOracle.connect(signer).setGasPrice(
args.l2GasPrice,
{ gasPrice: args.transactionGasPrice }
)
const receipt = await tx.wait() const receipt = await tx.wait()
console.log(`Success - ${receipt.transactionHash}`) console.log(`Success - ${receipt.transactionHash}`)
}
if (args.scalar !== undefined) {
console.log(`Setting scalar to ${args.scalar}`)
const tx = await GasPriceOracle.connect(signer).setScalar(args.scalar, {
gasPrice: args.transactionGasPrice,
})
const receipt = await tx.wait()
console.log(`Success - ${receipt.transactionHash}`)
}
if (args.overhead !== undefined) {
console.log(`Setting overhead to ${args.overhead}`)
const tx = await GasPriceOracle.connect(signer).setOverhead(
args.overhead,
{ gasPrice: args.transactionGasPrice }
)
const receipt = await tx.wait()
console.log(`Success - ${receipt.transactionHash}`)
}
}) })
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
TransactionReceipt, TransactionReceipt,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { ethers, BigNumber, Event } from 'ethers' import { ethers, BigNumber, Event } from 'ethers'
import { sleep } from '@eth-optimism/core-utils'
import { import {
ICrossChainProvider, ICrossChainProvider,
OEContracts, OEContracts,
...@@ -395,15 +396,27 @@ export class CrossChainProvider implements ICrossChainProvider { ...@@ -395,15 +396,27 @@ export class CrossChainProvider implements ICrossChainProvider {
return null return null
} }
public async waitForMessageReciept( public async waitForMessageReceipt(
message: MessageLike, message: MessageLike,
opts?: { opts: {
confirmations?: number confirmations?: number
pollIntervalMs?: number pollIntervalMs?: number
timeoutMs?: number timeoutMs?: number
} } = {}
): Promise<MessageReceipt> { ): Promise<MessageReceipt> {
throw new Error('Not implemented') let totalTimeMs = 0
while (totalTimeMs < (opts.timeoutMs || Infinity)) {
const tick = Date.now()
const receipt = await this.getMessageReceipt(message)
if (receipt !== null) {
return receipt
} else {
await sleep(opts.pollIntervalMs || 4000)
totalTimeMs += Date.now() - tick
}
}
throw new Error(`timed out waiting for message receipt`)
} }
public async estimateL2MessageGasLimit( public async estimateL2MessageGasLimit(
......
...@@ -186,7 +186,7 @@ export interface ICrossChainProvider { ...@@ -186,7 +186,7 @@ export interface ICrossChainProvider {
* @returns CrossChainMessage receipt including receipt of the transaction that relayed the * @returns CrossChainMessage receipt including receipt of the transaction that relayed the
* given message. * given message.
*/ */
waitForMessageReciept( waitForMessageReceipt(
message: MessageLike, message: MessageLike,
opts?: { opts?: {
confirmations?: number confirmations?: number
......
/* eslint-disable @typescript-eslint/no-empty-function */
import './setup' import './setup'
describe('CrossChainERC20Pair', () => { describe('CrossChainERC20Pair', () => {
describe('construction', () => { describe('construction', () => {
it('should have a messenger', () => {}) it('should have a messenger')
describe('when the token is a standard bridge token', () => { describe('when the token is a standard bridge token', () => {
it('should resolve the correct bridge', () => {}) it('should resolve the correct bridge')
}) })
describe('when the token is SNX', () => { describe('when the token is SNX', () => {
it('should resolve the correct bridge', () => {}) it('should resolve the correct bridge')
}) })
describe('when the token is DAI', () => { describe('when the token is DAI', () => {
it('should resolve the correct bridge', () => {}) it('should resolve the correct bridge')
}) })
describe('when a custom adapter is provided', () => { describe('when a custom adapter is provided', () => {
it('should use the custom adapter', () => {}) it('should use the custom adapter')
}) })
}) })
describe('deposit', () => { describe('deposit', () => {
describe('when the user has enough balance and allowance', () => { describe('when the user has enough balance and allowance', () => {
describe('when the token is a standard bridge token', () => { describe('when the token is a standard bridge token', () => {
it('should trigger a token deposit', () => {}) it('should trigger a token deposit')
}) })
describe('when the token is ETH', () => { describe('when the token is ETH', () => {
it('should trigger a token deposit', () => {}) it('should trigger a token deposit')
}) })
describe('when the token is SNX', () => { describe('when the token is SNX', () => {
it('should trigger a token deposit', () => {}) it('should trigger a token deposit')
}) })
describe('when the token is DAI', () => { describe('when the token is DAI', () => {
it('should trigger a token deposit', () => {}) it('should trigger a token deposit')
}) })
}) })
describe('when the user does not have enough balance', () => { describe('when the user does not have enough balance', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
describe('when the user has not given enough allowance to the bridge', () => { describe('when the user has not given enough allowance to the bridge', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
}) })
describe('withdraw', () => { describe('withdraw', () => {
describe('when the user has enough balance', () => { describe('when the user has enough balance', () => {
describe('when the token is a standard bridge token', () => { describe('when the token is a standard bridge token', () => {
it('should trigger a token withdrawal', () => {}) it('should trigger a token withdrawal')
}) })
describe('when the token is ETH', () => { describe('when the token is ETH', () => {
it('should trigger a token withdrawal', () => {}) it('should trigger a token withdrawal')
}) })
describe('when the token is SNX', () => { describe('when the token is SNX', () => {
it('should trigger a token withdrawal', () => {}) it('should trigger a token withdrawal')
}) })
describe('when the token is DAI', () => { describe('when the token is DAI', () => {
it('should trigger a token withdrawal', () => {}) it('should trigger a token withdrawal')
}) })
}) })
describe('when the user does not have enough balance', () => { describe('when the user does not have enough balance', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
}) })
describe('populateTransaction', () => { describe('populateTransaction', () => {
describe('deposit', () => { describe('deposit', () => {
it('should populate the transaction with the correct values', () => {}) it('should populate the transaction with the correct values')
}) })
describe('withdraw', () => { describe('withdraw', () => {
it('should populate the transaction with the correct values', () => {}) it('should populate the transaction with the correct values')
}) })
}) })
describe('estimateGas', () => { describe('estimateGas', () => {
describe('deposit', () => { describe('deposit', () => {
it('should estimate gas required for the transaction', () => {}) it('should estimate gas required for the transaction')
}) })
describe('withdraw', () => { describe('withdraw', () => {
it('should estimate gas required for the transaction', () => {}) it('should estimate gas required for the transaction')
}) })
}) })
}) })
/* eslint-disable @typescript-eslint/no-empty-function */
import './setup' import './setup'
describe('CrossChainMessenger', () => { describe('CrossChainMessenger', () => {
describe('sendMessage', () => { describe('sendMessage', () => {
describe('when no l2GasLimit is provided', () => { describe('when no l2GasLimit is provided', () => {
it('should send a message with an estimated l2GasLimit', () => {}) it('should send a message with an estimated l2GasLimit')
}) })
describe('when an l2GasLimit is provided', () => { describe('when an l2GasLimit is provided', () => {
it('should send a message with the provided l2GasLimit', () => {}) it('should send a message with the provided l2GasLimit')
}) })
}) })
describe('resendMessage', () => { describe('resendMessage', () => {
describe('when the message being resent exists', () => { describe('when the message being resent exists', () => {
it('should resend the message with the new gas limit', () => {}) it('should resend the message with the new gas limit')
}) })
describe('when the message being resent does not exist', () => { describe('when the message being resent does not exist', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
}) })
describe('finalizeMessage', () => { describe('finalizeMessage', () => {
describe('when the message being finalized exists', () => { describe('when the message being finalized exists', () => {
describe('when the message is ready to be finalized', () => { describe('when the message is ready to be finalized', () => {
it('should finalize the message', () => {}) it('should finalize the message')
}) })
describe('when the message is not ready to be finalized', () => { describe('when the message is not ready to be finalized', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
describe('when the message has already been finalized', () => { describe('when the message has already been finalized', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
}) })
describe('when the message being finalized does not exist', () => { describe('when the message being finalized does not exist', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
}) })
}) })
/* eslint-disable @typescript-eslint/no-empty-function */
import { expect } from './setup' import { expect } from './setup'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { Contract } from 'ethers' import { Contract } from 'ethers'
...@@ -383,24 +382,26 @@ describe('CrossChainProvider', () => { ...@@ -383,24 +382,26 @@ describe('CrossChainProvider', () => {
describe('getMessagesByAddress', () => { describe('getMessagesByAddress', () => {
describe('when the address has sent messages', () => { describe('when the address has sent messages', () => {
describe('when no direction is specified', () => { describe('when no direction is specified', () => {
it('should find all messages sent by the address', () => {}) it('should find all messages sent by the address')
}) })
describe('when a direction is specified', () => { describe('when a direction is specified', () => {
it('should find all messages only in the given direction', () => {}) it('should find all messages only in the given direction')
}) })
describe('when a block range is specified', () => { describe('when a block range is specified', () => {
it('should find all messages within the block range', () => {}) it('should find all messages within the block range')
}) })
describe('when both a direction and a block range are specified', () => { describe('when both a direction and a block range are specified', () => {
it('should find all messages only in the given direction and within the block range', () => {}) it(
'should find all messages only in the given direction and within the block range'
)
}) })
}) })
describe('when the address has not sent messages', () => { describe('when the address has not sent messages', () => {
it('should find nothing', () => {}) it('should find nothing')
}) })
}) })
...@@ -784,44 +785,44 @@ describe('CrossChainProvider', () => { ...@@ -784,44 +785,44 @@ describe('CrossChainProvider', () => {
describe('getMessageStatus', () => { describe('getMessageStatus', () => {
describe('when the message is an L1 => L2 message', () => { describe('when the message is an L1 => L2 message', () => {
describe('when the message has not been executed on L2 yet', () => { describe('when the message has not been executed on L2 yet', () => {
it('should return a status of UNCONFIRMED_L1_TO_L2_MESSAGE', () => {}) it('should return a status of UNCONFIRMED_L1_TO_L2_MESSAGE')
}) })
describe('when the message has been executed on L2', () => { describe('when the message has been executed on L2', () => {
it('should return a status of RELAYED', () => {}) it('should return a status of RELAYED')
}) })
describe('when the message has been executed but failed', () => { describe('when the message has been executed but failed', () => {
it('should return a status of FAILED_L1_TO_L2_MESSAGE', () => {}) it('should return a status of FAILED_L1_TO_L2_MESSAGE')
}) })
}) })
describe('when the message is an L2 => L1 message', () => { describe('when the message is an L2 => L1 message', () => {
describe('when the message state root has not been published', () => { describe('when the message state root has not been published', () => {
it('should return a status of STATE_ROOT_NOT_PUBLISHED', () => {}) it('should return a status of STATE_ROOT_NOT_PUBLISHED')
}) })
describe('when the message state root is still in the challenge period', () => { describe('when the message state root is still in the challenge period', () => {
it('should return a status of IN_CHALLENGE_PERIOD', () => {}) it('should return a status of IN_CHALLENGE_PERIOD')
}) })
describe('when the message is no longer in the challenge period', () => { describe('when the message is no longer in the challenge period', () => {
describe('when the message has been relayed successfully', () => { describe('when the message has been relayed successfully', () => {
it('should return a status of RELAYED', () => {}) it('should return a status of RELAYED')
}) })
describe('when the message has been relayed but the relay failed', () => { describe('when the message has been relayed but the relay failed', () => {
it('should return a status of READY_FOR_RELAY', () => {}) it('should return a status of READY_FOR_RELAY')
}) })
describe('when the message has not been relayed', () => { describe('when the message has not been relayed', () => {
it('should return a status of READY_FOR_RELAY', () => {}) it('should return a status of READY_FOR_RELAY')
}) })
}) })
}) })
describe('when the message does not exist', () => { describe('when the message does not exist', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
}) })
...@@ -982,64 +983,157 @@ describe('CrossChainProvider', () => { ...@@ -982,64 +983,157 @@ describe('CrossChainProvider', () => {
// track of // track of
}) })
describe('waitForMessageReciept', () => { describe('waitForMessageReceipt', () => {
let l2Messenger: Contract
let provider: CrossChainProvider
beforeEach(async () => {
l2Messenger = (await (
await ethers.getContractFactory('MockMessenger')
).deploy()) as any
provider = new CrossChainProvider({
l1Provider: ethers.provider,
l2Provider: ethers.provider,
l1ChainId: 31337,
contracts: {
l2: {
L2CrossDomainMessenger: l2Messenger.address,
},
},
})
})
describe('when the message receipt already exists', () => { describe('when the message receipt already exists', () => {
it('should immediately return the receipt', () => {}) it('should immediately return the receipt', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
const tx = await l2Messenger.triggerRelayedMessageEvents([
hashCrossChainMessage(message),
])
const messageReceipt = await provider.waitForMessageReceipt(message)
expect(messageReceipt.receiptStatus).to.equal(1)
expect(
omit(messageReceipt.transactionReceipt, 'confirmations')
).to.deep.equal(
omit(
await ethers.provider.getTransactionReceipt(tx.hash),
'confirmations'
)
)
})
}) })
describe('when the message receipt does not exist already', () => { describe('when the message receipt does not exist already', () => {
describe('when no extra options are provided', () => { describe('when no extra options are provided', () => {
it('should wait for the receipt to be published', () => {}) it('should wait for the receipt to be published', async () => {
it('should wait forever for the receipt if the receipt is never published', () => {}) const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
setTimeout(async () => {
await l2Messenger.triggerRelayedMessageEvents([
hashCrossChainMessage(message),
])
}, 5000)
const tick = Date.now()
const messageReceipt = await provider.waitForMessageReceipt(message)
const tock = Date.now()
expect(messageReceipt.receiptStatus).to.equal(1)
expect(tock - tick).to.be.greaterThan(5000)
})
it('should wait forever for the receipt if the receipt is never published', () => {
// Not sure how to easily test this without introducing some sort of cancellation token
// I don't want the promise to loop forever and make the tests never finish.
})
}) })
describe('when a timeout is provided', () => { describe('when a timeout is provided', () => {
it('should throw an error if the timeout is reached', () => {}) it('should throw an error if the timeout is reached', async () => {
}) const message = {
}) direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
describe('when the message does not exist', () => { await expect(
it('should throw an error', () => {}) provider.waitForMessageReceipt(message, {
timeoutMs: 10000,
})
).to.be.rejectedWith('timed out waiting for message receipt')
})
})
}) })
}) })
describe('estimateL2MessageGasLimit', () => { describe('estimateL2MessageGasLimit', () => {
it('should perform a gas estimation of the L2 action', () => {}) it('should perform a gas estimation of the L2 action')
}) })
describe('estimateMessageWaitTimeBlocks', () => { describe('estimateMessageWaitTimeBlocks', () => {
describe('when the message exists', () => { describe('when the message exists', () => {
describe('when the message is an L1 => L2 message', () => { describe('when the message is an L1 => L2 message', () => {
describe('when the message has not been executed on L2 yet', () => { describe('when the message has not been executed on L2 yet', () => {
it('should return the estimated blocks until the message will be confirmed on L2', () => {}) it(
'should return the estimated blocks until the message will be confirmed on L2'
)
}) })
describe('when the message has been executed on L2', () => { describe('when the message has been executed on L2', () => {
it('should return 0', () => {}) it('should return 0')
}) })
}) })
describe('when the message is an L2 => L1 message', () => { describe('when the message is an L2 => L1 message', () => {
describe('when the state root has not been published', () => { describe('when the state root has not been published', () => {
it('should return the estimated blocks until the state root will be published and pass the challenge period', () => {}) it(
'should return the estimated blocks until the state root will be published and pass the challenge period'
)
}) })
describe('when the state root is within the challenge period', () => { describe('when the state root is within the challenge period', () => {
it('should return the estimated blocks until the state root passes the challenge period', () => {}) it(
'should return the estimated blocks until the state root passes the challenge period'
)
}) })
describe('when the state root passes the challenge period', () => { describe('when the state root passes the challenge period', () => {
it('should return 0', () => {}) it('should return 0')
}) })
}) })
}) })
describe('when the message does not exist', () => { describe('when the message does not exist', () => {
it('should throw an error', () => {}) it('should throw an error')
}) })
}) })
describe('estimateMessageWaitTimeSeconds', () => { describe('estimateMessageWaitTimeSeconds', () => {
it('should be the result of estimateMessageWaitTimeBlocks multiplied by the L1 block time', () => {}) it(
'should be the result of estimateMessageWaitTimeBlocks multiplied by the L1 block time'
)
}) })
}) })
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