Commit ad916d23 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into dependabot/npm_and_yarn/typescript-5.2.2

parents 84a1aa35 56d08277
...@@ -56,8 +56,9 @@ def main(): ...@@ -56,8 +56,9 @@ def main():
deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1') deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1')
op_node_dir = pjoin(args.monorepo_dir, 'op-node') op_node_dir = pjoin(args.monorepo_dir, 'op-node')
ops_bedrock_dir = pjoin(monorepo_dir, 'ops-bedrock') ops_bedrock_dir = pjoin(monorepo_dir, 'ops-bedrock')
deploy_config_dir = pjoin(contracts_bedrock_dir, 'deploy-config'), deploy_config_dir = pjoin(contracts_bedrock_dir, 'deploy-config')
devnet_config_path = pjoin(contracts_bedrock_dir, 'deploy-config', 'devnetL1.json') devnet_config_path = pjoin(deploy_config_dir, 'devnetL1.json')
devnet_config_template_path = pjoin(deploy_config_dir, 'devnetL1-template.json')
ops_chain_ops = pjoin(monorepo_dir, 'op-chain-ops') ops_chain_ops = pjoin(monorepo_dir, 'op-chain-ops')
sdk_dir = pjoin(monorepo_dir, 'packages', 'sdk') sdk_dir = pjoin(monorepo_dir, 'packages', 'sdk')
...@@ -69,6 +70,7 @@ def main(): ...@@ -69,6 +70,7 @@ def main():
l1_deployments_path=pjoin(deployment_dir, '.deploy'), l1_deployments_path=pjoin(deployment_dir, '.deploy'),
deploy_config_dir=deploy_config_dir, deploy_config_dir=deploy_config_dir,
devnet_config_path=devnet_config_path, devnet_config_path=devnet_config_path,
devnet_config_template_path=devnet_config_template_path,
op_node_dir=op_node_dir, op_node_dir=op_node_dir,
ops_bedrock_dir=ops_bedrock_dir, ops_bedrock_dir=ops_bedrock_dir,
ops_chain_ops=ops_chain_ops, ops_chain_ops=ops_chain_ops,
...@@ -124,10 +126,16 @@ def deploy_contracts(paths): ...@@ -124,10 +126,16 @@ def deploy_contracts(paths):
'--rpc-url', 'http://127.0.0.1:8545' '--rpc-url', 'http://127.0.0.1:8545'
], env={}, cwd=paths.contracts_bedrock_dir) ], env={}, cwd=paths.contracts_bedrock_dir)
def init_devnet_l1_deploy_config(paths, update_timestamp=False):
deploy_config = read_json(paths.devnet_config_template_path)
if update_timestamp:
deploy_config['l1GenesisBlockTimestamp'] = '{:#x}'.format(int(time.time()))
write_json(paths.devnet_config_path, deploy_config)
def devnet_l1_genesis(paths): def devnet_l1_genesis(paths):
log.info('Generating L1 genesis state') log.info('Generating L1 genesis state')
init_devnet_l1_deploy_config(paths)
geth = subprocess.Popen([ geth = subprocess.Popen([
'geth', '--dev', '--http', '--http.api', 'eth,debug', 'geth', '--dev', '--http', '--http.api', 'eth,debug',
'--verbosity', '4', '--gcmode', 'archive', '--dev.gaslimit', '30000000' '--verbosity', '4', '--gcmode', 'archive', '--dev.gaslimit', '30000000'
...@@ -157,13 +165,13 @@ def devnet_deploy(paths): ...@@ -157,13 +165,13 @@ def devnet_deploy(paths):
if os.path.exists(paths.allocs_path) == False: if os.path.exists(paths.allocs_path) == False:
devnet_l1_genesis(paths) devnet_l1_genesis(paths)
devnet_config_backup = pjoin(paths.devnet_dir, 'devnetL1.json.bak') # It's odd that we want to regenerate the devnetL1.json file with
shutil.copy(paths.devnet_config_path, devnet_config_backup) # an updated timestamp different than the one used in the devnet_l1_genesis
deploy_config = read_json(paths.devnet_config_path) # function. But, without it, CI flakes on this test rather consistently.
deploy_config['l1GenesisBlockTimestamp'] = '{:#x}'.format(int(time.time())) # If someone reads this comment and understands why this is being done, please
write_json(paths.devnet_config_path, deploy_config) # update this comment to explain.
init_devnet_l1_deploy_config(paths, update_timestamp=True)
outfile_l1 = pjoin(paths.devnet_dir, 'genesis-l1.json') outfile_l1 = pjoin(paths.devnet_dir, 'genesis-l1.json')
run_command([ run_command([
'go', 'run', 'cmd/main.go', 'genesis', 'l1', 'go', 'run', 'cmd/main.go', 'genesis', 'l1',
'--deploy-config', paths.devnet_config_path, '--deploy-config', paths.devnet_config_path,
......
...@@ -14,3 +14,4 @@ finalized and may change without notice. ...@@ -14,3 +14,4 @@ finalized and may change without notice.
* [Manual Usage](./manual.md) * [Manual Usage](./manual.md)
* [Creating Traces with Cannon](./cannon.md) * [Creating Traces with Cannon](./cannon.md)
* [Automation with `op-challenger`](./run-challenger.md) * [Automation with `op-challenger`](./run-challenger.md)
* [Challenging Invalid Output Proposals](./invalid-proposals.md)
## Challenging Invalid Output Proposals
The dispute game factory deployed to Goerli reads from the permissioned L2 Output Oracle contract. This restricts games
to challenging valid output proposals and an honest challenger should win every game. To test creating games that
challenge an invalid output proposal, a custom chain is required. The simplest way to do this is using the end-to-end
test utilities in [`op-e2e`](https://github.com/ethereum-optimism/optimism/tree/develop/op-e2e).
A simple starting point has been provided in the `TestCannonProposedOutputRootInvalid` test case
in [`faultproof_test.go`](https://github.com/ethereum-optimism/optimism/blob/6e174ae2b2587d9ac5e2930d7574f85d254ca8b4/op-e2e/faultproof_test.go#L334).
This is a table test that takes the output root to propose, plus functions for move and step to counter the honest
claims. The test asserts that the defender always wins and thus the output root is found to be invalid.
...@@ -13,43 +13,6 @@ import ( ...@@ -13,43 +13,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestMultipleAlphabetGames(t *testing.T) {
InitParallel(t)
ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close)
gameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
// Start a challenger with the correct alphabet trace
gameFactory.StartChallenger(ctx, sys.NodeEndpoint("l1"), "TowerDefense",
challenger.WithAlphabet("abcdefg"),
challenger.WithPrivKey(sys.cfg.Secrets.Alice),
challenger.WithAgreeProposedOutput(true),
)
game1 := gameFactory.StartAlphabetGame(ctx, "abcxyz")
// Wait for the challenger to respond to the first game
game1.WaitForClaimCount(ctx, 2)
game2 := gameFactory.StartAlphabetGame(ctx, "zyxabc")
// Wait for the challenger to respond to the second game
game2.WaitForClaimCount(ctx, 2)
// Challenger should respond to new claims
game2.Attack(ctx, 1, common.Hash{0xaa})
game2.WaitForClaimCount(ctx, 4)
game1.Defend(ctx, 1, common.Hash{0xaa})
game1.WaitForClaimCount(ctx, 4)
gameDuration := game1.GameDuration(ctx)
sys.TimeTravelClock.AdvanceTime(gameDuration)
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game1.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game2.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
}
func TestMultipleCannonGames(t *testing.T) { func TestMultipleCannonGames(t *testing.T) {
InitParallel(t) InitParallel(t)
...@@ -106,36 +69,6 @@ func TestMultipleCannonGames(t *testing.T) { ...@@ -106,36 +69,6 @@ func TestMultipleCannonGames(t *testing.T) {
challenger.WaitForGameDataDeletion(ctx, game1, game2) challenger.WaitForGameDataDeletion(ctx, game1, game2)
} }
func TestResolveDisputeGame(t *testing.T) {
InitParallel(t)
ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
game := disputeGameFactory.StartAlphabetGame(ctx, "zyxwvut")
require.NotNil(t, game)
gameDuration := game.GameDuration(ctx)
game.WaitForGameStatus(ctx, disputegame.StatusInProgress)
game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "HonestAlice",
challenger.WithAgreeProposedOutput(true),
challenger.WithAlphabet("abcdefg"),
challenger.WithPrivKey(sys.cfg.Secrets.Alice),
)
game.WaitForClaimCount(ctx, 2)
sys.TimeTravelClock.AdvanceTime(gameDuration)
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
// Challenger should resolve the game now that the clocks have expired.
game.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
}
func TestChallengerCompleteDisputeGame(t *testing.T) { func TestChallengerCompleteDisputeGame(t *testing.T) {
InitParallel(t) InitParallel(t)
...@@ -333,22 +266,32 @@ func TestCannonDefendStep(t *testing.T) { ...@@ -333,22 +266,32 @@ func TestCannonDefendStep(t *testing.T) {
func TestCannonProposedOutputRootInvalid(t *testing.T) { func TestCannonProposedOutputRootInvalid(t *testing.T) {
InitParallel(t) InitParallel(t)
honestStepsFail := func(ctx context.Context, correctTrace *disputegame.HonestHelper, parentClaimIdx int64) { // honestStepsFail attempts to perform both an attack and defend step using the correct trace.
honestStepsFail := func(ctx context.Context, game *disputegame.CannonGameHelper, correctTrace *disputegame.HonestHelper, parentClaimIdx int64) {
// Attack step should fail // Attack step should fail
correctTrace.StepFails(ctx, parentClaimIdx, true) correctTrace.StepFails(ctx, parentClaimIdx, true)
// Defending should fail too // Defending should fail too
correctTrace.StepFails(ctx, parentClaimIdx, false) correctTrace.StepFails(ctx, parentClaimIdx, false)
} }
tests := []struct { tests := []struct {
name string // name is the name of the test
outputRoot common.Hash name string
performMove func(ctx context.Context, correctTrace *disputegame.HonestHelper, parentClaimIdx int64)
performStep func(ctx context.Context, correctTrace *disputegame.HonestHelper, parentClaimIdx int64) // outputRoot is the invalid output root to propose
outputRoot common.Hash
// performMove is called to respond to each claim posted by the honest op-challenger.
// It should either attack or defend the claim at parentClaimIdx
performMove func(ctx context.Context, game *disputegame.CannonGameHelper, correctTrace *disputegame.HonestHelper, parentClaimIdx int64)
// performStep is called once the maximum game depth is reached. It should perform a step to counter the
// claim at parentClaimIdx. Since the proposed output root is invalid, the step call should always revert.
performStep func(ctx context.Context, game *disputegame.CannonGameHelper, correctTrace *disputegame.HonestHelper, parentClaimIdx int64)
}{ }{
{ {
name: "AttackWithCorrectTrace", name: "AttackWithCorrectTrace",
outputRoot: common.Hash{0xab}, outputRoot: common.Hash{0xab},
performMove: func(ctx context.Context, correctTrace *disputegame.HonestHelper, parentClaimIdx int64) { performMove: func(ctx context.Context, game *disputegame.CannonGameHelper, correctTrace *disputegame.HonestHelper, parentClaimIdx int64) {
// Attack everything but oddly using the correct hash. // Attack everything but oddly using the correct hash.
correctTrace.Attack(ctx, parentClaimIdx) correctTrace.Attack(ctx, parentClaimIdx)
}, },
...@@ -357,7 +300,7 @@ func TestCannonProposedOutputRootInvalid(t *testing.T) { ...@@ -357,7 +300,7 @@ func TestCannonProposedOutputRootInvalid(t *testing.T) {
{ {
name: "DefendWithCorrectTrace", name: "DefendWithCorrectTrace",
outputRoot: common.Hash{0xab}, outputRoot: common.Hash{0xab},
performMove: func(ctx context.Context, correctTrace *disputegame.HonestHelper, parentClaimIdx int64) { performMove: func(ctx context.Context, game *disputegame.CannonGameHelper, correctTrace *disputegame.HonestHelper, parentClaimIdx int64) {
// Can only attack the root claim // Can only attack the root claim
if parentClaimIdx == 0 { if parentClaimIdx == 0 {
correctTrace.Attack(ctx, parentClaimIdx) correctTrace.Attack(ctx, parentClaimIdx)
...@@ -376,16 +319,16 @@ func TestCannonProposedOutputRootInvalid(t *testing.T) { ...@@ -376,16 +319,16 @@ func TestCannonProposedOutputRootInvalid(t *testing.T) {
InitParallel(t) InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, l1Client, game, correctTrace := setupDisputeGameForInvalidOutputRoot(t, common.Hash{0xab}) sys, l1Client, game, correctTrace := setupDisputeGameForInvalidOutputRoot(t, test.outputRoot)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
// Now maliciously play the game and it should be impossible to win // Now maliciously play the game and it should be impossible to win
game.ChallengeRootClaim(ctx, game.ChallengeRootClaim(ctx,
func(parentClaimIdx int64) { func(parentClaimIdx int64) {
test.performMove(ctx, correctTrace, parentClaimIdx) test.performMove(ctx, game, correctTrace, parentClaimIdx)
}, },
func(parentClaimIdx int64) { func(parentClaimIdx int64) {
test.performStep(ctx, correctTrace, parentClaimIdx) test.performStep(ctx, game, correctTrace, parentClaimIdx)
}) })
// Time travel past when the game will be resolvable. // Time travel past when the game will be resolvable.
......
...@@ -18,9 +18,10 @@ COPY --from=builder /app/entrypoint.sh /bin/entrypoint.sh ...@@ -18,9 +18,10 @@ COPY --from=builder /app/entrypoint.sh /bin/entrypoint.sh
COPY --from=builder /app/bin/ufm /bin/ufm COPY --from=builder /app/bin/ufm /bin/ufm
RUN apk update && \ RUN apk update && \
apk add ca-certificates && \
chmod +x /bin/entrypoint.sh chmod +x /bin/entrypoint.sh
RUN apk add ca-certificates jq curl bind-tools
VOLUME /etc/ufm VOLUME /etc/ufm
EXPOSE 8080 EXPOSE 8080
......
...@@ -39,12 +39,6 @@ address = "0x0000000000000000000000000000000000000000" ...@@ -39,12 +39,6 @@ address = "0x0000000000000000000000000000000000000000"
private_key = "0000000000000000000000000000000000000000000000000000000000000000" private_key = "0000000000000000000000000000000000000000000000000000000000000000"
# Transaction value in wei # Transaction value in wei
tx_value = 100000000000000 tx_value = 100000000000000
# Gas limit
gas_limit = 21000
# Gas tip cap
gas_tip_cap = 2000000000
# Fee cap
gas_fee_cap = 20000000000
[providers.p1] [providers.p1]
# URL to the RPC provider # URL to the RPC provider
...@@ -52,13 +46,15 @@ url = "http://localhost:8551" ...@@ -52,13 +46,15 @@ url = "http://localhost:8551"
# Read only providers are only used to check for transactions # Read only providers are only used to check for transactions
read_only = true read_only = true
# Interval to poll the provider for expected transactions # Interval to poll the provider for expected transactions
read_interval = "1s" read_interval = "10s"
# Interval to submit new transactions to the provider # Interval to submit new transactions to the provider
send_interval = "5s" send_interval = "30s"
# Wallet to be used for sending transactions # Interval between send transaction when we get "already known" txpool err
wallet = "default" send_transaction_retry_interval = "100ms"
# Network to pool transactions, i.e. providers in the same network will check transactions from each other # Max time to retry
network = "op-goerli" send_transaction_retry_timeout = "5s"
# Interval between each send transaction to the same network
send_transaction_cool_down = "30s"
# Interval between receipt retrieval # Interval between receipt retrieval
receipt_retrieval_interval = "500ms" receipt_retrieval_interval = "500ms"
# Max time to check for receipt # Max time to check for receipt
...@@ -72,13 +68,15 @@ url = "http://localhost:8552" ...@@ -72,13 +68,15 @@ url = "http://localhost:8552"
# Read only providers are only used to check for transactions # Read only providers are only used to check for transactions
read_only = false read_only = false
# Interval to poll the provider for expected transactions # Interval to poll the provider for expected transactions
read_interval = "2s" read_interval = "10s"
# Interval to submit new transactions to the provider # Interval to submit new transactions to the provider
send_interval = "3s" send_interval = "30s"
# Wallet to be used for sending transactions # Interval between send transaction when we get "already known" txpool err
wallet = "default" send_transaction_retry_interval = "100ms"
# Network to pool transactions, i.e. providers in the same network will check transactions from each other # Max time to retry
network = "op-goerli" send_transaction_retry_timeout = "5s"
# Interval between each send transaction to the same network
send_transaction_cool_down = "30s"
# Interval between receipt retrieval # Interval between receipt retrieval
receipt_retrieval_interval = "500ms" receipt_retrieval_interval = "500ms"
# Max time to check for receipt # Max time to check for receipt
......
...@@ -48,10 +48,7 @@ type WalletConfig struct { ...@@ -48,10 +48,7 @@ type WalletConfig struct {
PrivateKey string `toml:"private_key"` PrivateKey string `toml:"private_key"`
// transaction parameters // transaction parameters
TxValue big.Int `toml:"tx_value"` TxValue big.Int `toml:"tx_value"`
GasLimit uint64 `toml:"gas_limit"`
GasTipCap big.Int `toml:"gas_tip_cap"`
GasFeeCap big.Int `toml:"gas_fee_cap"`
} }
type ProviderConfig struct { type ProviderConfig struct {
...@@ -64,6 +61,7 @@ type ProviderConfig struct { ...@@ -64,6 +61,7 @@ type ProviderConfig struct {
SendInterval TOMLDuration `toml:"send_interval"` SendInterval TOMLDuration `toml:"send_interval"`
SendTransactionRetryInterval TOMLDuration `toml:"send_transaction_retry_interval"` SendTransactionRetryInterval TOMLDuration `toml:"send_transaction_retry_interval"`
SendTransactionRetryTimeout TOMLDuration `toml:"send_transaction_retry_timeout"` SendTransactionRetryTimeout TOMLDuration `toml:"send_transaction_retry_timeout"`
SendTransactionCoolDown TOMLDuration `toml:"send_transaction_cool_down"`
ReceiptRetrievalInterval TOMLDuration `toml:"receipt_retrieval_interval"` ReceiptRetrievalInterval TOMLDuration `toml:"receipt_retrieval_interval"`
ReceiptRetrievalTimeout TOMLDuration `toml:"receipt_retrieval_timeout"` ReceiptRetrievalTimeout TOMLDuration `toml:"receipt_retrieval_timeout"`
...@@ -130,12 +128,6 @@ func (c *Config) Validate() error { ...@@ -130,12 +128,6 @@ func (c *Config) Validate() error {
if wallet.TxValue.BitLen() == 0 { if wallet.TxValue.BitLen() == 0 {
return errors.Errorf("wallet [%s] tx_value is missing", name) return errors.Errorf("wallet [%s] tx_value is missing", name)
} }
if wallet.GasLimit == 0 {
return errors.Errorf("wallet [%s] gas_limit is missing", name)
}
if wallet.GasFeeCap.BitLen() == 0 {
return errors.Errorf("wallet [%s] gas_fee_cap is missing", name)
}
} }
for name, provider := range c.Providers { for name, provider := range c.Providers {
...@@ -154,6 +146,9 @@ func (c *Config) Validate() error { ...@@ -154,6 +146,9 @@ func (c *Config) Validate() error {
if provider.SendTransactionRetryTimeout == 0 { if provider.SendTransactionRetryTimeout == 0 {
return errors.Errorf("provider [%s] send_transaction_retry_timeout is missing", name) return errors.Errorf("provider [%s] send_transaction_retry_timeout is missing", name)
} }
if provider.SendTransactionCoolDown == 0 {
return errors.Errorf("provider [%s] send_transaction_cool_down is missing", name)
}
if provider.ReceiptRetrievalInterval == 0 { if provider.ReceiptRetrievalInterval == 0 {
return errors.Errorf("provider [%s] receipt_retrieval_interval is missing", name) return errors.Errorf("provider [%s] receipt_retrieval_interval is missing", name)
} }
......
...@@ -2,6 +2,7 @@ package clients ...@@ -2,6 +2,7 @@ package clients
import ( import (
"context" "context"
"math/big"
"time" "time"
"github.com/ethereum-optimism/optimism/op-ufm/pkg/metrics" "github.com/ethereum-optimism/optimism/op-ufm/pkg/metrics"
...@@ -22,7 +23,7 @@ func Dial(providerName string, url string) (*InstrumentedEthClient, error) { ...@@ -22,7 +23,7 @@ func Dial(providerName string, url string) (*InstrumentedEthClient, error) {
start := time.Now() start := time.Now()
c, err := ethclient.Dial(url) c, err := ethclient.Dial(url)
if err != nil { if err != nil {
metrics.RecordError(providerName, "ethclient.Dial") metrics.RecordErrorDetails(providerName, "ethclient.Dial", err)
return nil, err return nil, err
} }
metrics.RecordRPCLatency(providerName, "ethclient", "Dial", time.Since(start)) metrics.RecordRPCLatency(providerName, "ethclient", "Dial", time.Since(start))
...@@ -34,7 +35,7 @@ func (i *InstrumentedEthClient) TransactionByHash(ctx context.Context, hash comm ...@@ -34,7 +35,7 @@ func (i *InstrumentedEthClient) TransactionByHash(ctx context.Context, hash comm
tx, isPending, err := i.c.TransactionByHash(ctx, hash) tx, isPending, err := i.c.TransactionByHash(ctx, hash)
if err != nil { if err != nil {
if !i.ignorableErrors(err) { if !i.ignorableErrors(err) {
metrics.RecordError(i.providerName, "ethclient.TransactionByHash") metrics.RecordErrorDetails(i.providerName, "ethclient.TransactionByHash", err)
} }
return nil, false, err return nil, false, err
} }
...@@ -46,7 +47,7 @@ func (i *InstrumentedEthClient) PendingNonceAt(ctx context.Context, address stri ...@@ -46,7 +47,7 @@ func (i *InstrumentedEthClient) PendingNonceAt(ctx context.Context, address stri
start := time.Now() start := time.Now()
nonce, err := i.c.PendingNonceAt(ctx, common.HexToAddress(address)) nonce, err := i.c.PendingNonceAt(ctx, common.HexToAddress(address))
if err != nil { if err != nil {
metrics.RecordError(i.providerName, "ethclient.PendingNonceAt") metrics.RecordErrorDetails(i.providerName, "ethclient.PendingNonceAt", err)
return 0, err return 0, err
} }
metrics.RecordRPCLatency(i.providerName, "ethclient", "PendingNonceAt", time.Since(start)) metrics.RecordRPCLatency(i.providerName, "ethclient", "PendingNonceAt", time.Since(start))
...@@ -58,7 +59,7 @@ func (i *InstrumentedEthClient) TransactionReceipt(ctx context.Context, txHash c ...@@ -58,7 +59,7 @@ func (i *InstrumentedEthClient) TransactionReceipt(ctx context.Context, txHash c
receipt, err := i.c.TransactionReceipt(ctx, txHash) receipt, err := i.c.TransactionReceipt(ctx, txHash)
if err != nil { if err != nil {
if !i.ignorableErrors(err) { if !i.ignorableErrors(err) {
metrics.RecordError(i.providerName, "ethclient.TransactionReceipt") metrics.RecordErrorDetails(i.providerName, "ethclient.TransactionReceipt", err)
} }
return nil, err return nil, err
} }
...@@ -71,7 +72,7 @@ func (i *InstrumentedEthClient) SendTransaction(ctx context.Context, tx *types.T ...@@ -71,7 +72,7 @@ func (i *InstrumentedEthClient) SendTransaction(ctx context.Context, tx *types.T
err := i.c.SendTransaction(ctx, tx) err := i.c.SendTransaction(ctx, tx)
if err != nil { if err != nil {
if !i.ignorableErrors(err) { if !i.ignorableErrors(err) {
metrics.RecordError(i.providerName, "ethclient.SendTransaction") metrics.RecordErrorDetails(i.providerName, "ethclient.SendTransaction", err)
} }
return err return err
} }
...@@ -79,6 +80,39 @@ func (i *InstrumentedEthClient) SendTransaction(ctx context.Context, tx *types.T ...@@ -79,6 +80,39 @@ func (i *InstrumentedEthClient) SendTransaction(ctx context.Context, tx *types.T
return err return err
} }
func (i *InstrumentedEthClient) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
start := time.Now()
gas, err := i.c.EstimateGas(ctx, msg)
if err != nil {
metrics.RecordErrorDetails(i.providerName, "ethclient.EstimateGas", err)
return 0, err
}
metrics.RecordRPCLatency(i.providerName, "ethclient", "EstimateGas", time.Since(start))
return gas, err
}
func (i *InstrumentedEthClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
start := time.Now()
gasTipCap, err := i.c.SuggestGasTipCap(ctx)
if err != nil {
metrics.RecordErrorDetails(i.providerName, "ethclient.SuggestGasTipCap", err)
return nil, err
}
metrics.RecordRPCLatency(i.providerName, "ethclient", "SuggestGasTipCap", time.Since(start))
return gasTipCap, err
}
func (i *InstrumentedEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
start := time.Now()
header, err := i.c.HeaderByNumber(ctx, number)
if err != nil {
metrics.RecordErrorDetails(i.providerName, "ethclient.HeaderByNumber", err)
return nil, err
}
metrics.RecordRPCLatency(i.providerName, "ethclient", "HeaderByNumber", time.Since(start))
return header, err
}
func (i *InstrumentedEthClient) ignorableErrors(err error) bool { func (i *InstrumentedEthClient) ignorableErrors(err error) bool {
msg := err.Error() msg := err.Error()
// we dont use errors.Is because eth client actually uses errors.New, // we dont use errors.Is because eth client actually uses errors.New,
......
...@@ -22,7 +22,7 @@ func NewSignerClient(providerName string, logger log.Logger, endpoint string, tl ...@@ -22,7 +22,7 @@ func NewSignerClient(providerName string, logger log.Logger, endpoint string, tl
start := time.Now() start := time.Now()
c, err := signer.NewSignerClient(logger, endpoint, tlsConfig) c, err := signer.NewSignerClient(logger, endpoint, tlsConfig)
if err != nil { if err != nil {
metrics.RecordError(providerName, "signer.NewSignerClient") metrics.RecordErrorDetails(providerName, "signer.NewSignerClient", err)
return nil, err return nil, err
} }
metrics.RecordRPCLatency(providerName, "signer", "NewSignerClient", time.Since(start)) metrics.RecordRPCLatency(providerName, "signer", "NewSignerClient", time.Since(start))
...@@ -33,7 +33,7 @@ func (i *InstrumentedSignerClient) SignTransaction(ctx context.Context, chainId ...@@ -33,7 +33,7 @@ func (i *InstrumentedSignerClient) SignTransaction(ctx context.Context, chainId
start := time.Now() start := time.Now()
tx, err := i.c.SignTransaction(ctx, chainId, tx) tx, err := i.c.SignTransaction(ctx, chainId, tx)
if err != nil { if err != nil {
metrics.RecordError(i.providerName, "signer.SignTransaction") metrics.RecordErrorDetails(i.providerName, "signer.SignTransaction", err)
return nil, err return nil, err
} }
metrics.RecordRPCLatency(i.providerName, "signer", "SignTransaction", time.Since(start)) metrics.RecordRPCLatency(i.providerName, "signer", "SignTransaction", time.Since(start))
......
...@@ -2,6 +2,7 @@ package provider ...@@ -2,6 +2,7 @@ package provider
import ( import (
"context" "context"
"math/big"
"time" "time"
"github.com/ethereum-optimism/optimism/op-ufm/pkg/metrics" "github.com/ethereum-optimism/optimism/op-ufm/pkg/metrics"
...@@ -21,7 +22,7 @@ import ( ...@@ -21,7 +22,7 @@ import (
// RoundTrip send a new transaction to measure round trip latency // RoundTrip send a new transaction to measure round trip latency
func (p *Provider) RoundTrip(ctx context.Context) { func (p *Provider) RoundTrip(ctx context.Context) {
log.Debug("roundTripLatency", log.Debug("RoundTrip",
"provider", p.name) "provider", p.name)
client, err := iclients.Dial(p.name, p.config.URL) client, err := iclients.Dial(p.name, p.config.URL)
...@@ -33,33 +34,38 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -33,33 +34,38 @@ func (p *Provider) RoundTrip(ctx context.Context) {
return return
} }
var nonce uint64 p.txPool.ExclusiveSend.Lock()
p.txPool.M.Lock() defer p.txPool.ExclusiveSend.Unlock()
if p.txPool.Nonce == uint64(0) {
nonce, err = client.PendingNonceAt(ctx, p.walletConfig.Address)
if err != nil {
log.Error("cant get nounce",
"provider", p.name,
"err", err)
p.txPool.M.Unlock()
return
}
p.txPool.Nonce = nonce
} else {
p.txPool.Nonce++
nonce = p.txPool.Nonce
}
p.txPool.M.Unlock()
txHash := common.Hash{} txHash := common.Hash{}
attempt := 0 attempt := 0
nonce := uint64(0)
// used for timeout // used for timeout
firstAttemptAt := time.Now() firstAttemptAt := time.Now()
// used for actual round trip time (disregard retry time) // used for actual round trip time (disregard retry time)
roundTripStartedAt := time.Now() var roundTripStartedAt time.Time
for { for {
tx := p.createTx(nonce)
txHash = tx.Hash() // sleep until we get a clear to send
for {
coolDown := time.Duration(p.config.SendTransactionCoolDown) - time.Since(p.txPool.LastSend)
if coolDown > 0 {
time.Sleep(coolDown)
} else {
break
}
}
tx, err := p.createTx(ctx, client, nonce)
nonce = tx.Nonce()
if err != nil {
log.Error("cant create tx",
"provider", p.name,
"nonce", nonce,
"err", err)
return
}
signedTx, err := p.sign(ctx, tx) signedTx, err := p.sign(ctx, tx)
if err != nil { if err != nil {
...@@ -69,7 +75,6 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -69,7 +75,6 @@ func (p *Provider) RoundTrip(ctx context.Context) {
"err", err) "err", err)
return return
} }
txHash = signedTx.Hash() txHash = signedTx.Hash()
roundTripStartedAt = time.Now() roundTripStartedAt = time.Now()
...@@ -78,25 +83,29 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -78,25 +83,29 @@ func (p *Provider) RoundTrip(ctx context.Context) {
if err.Error() == txpool.ErrAlreadyKnown.Error() || if err.Error() == txpool.ErrAlreadyKnown.Error() ||
err.Error() == txpool.ErrReplaceUnderpriced.Error() || err.Error() == txpool.ErrReplaceUnderpriced.Error() ||
err.Error() == core.ErrNonceTooLow.Error() { err.Error() == core.ErrNonceTooLow.Error() {
log.Warn("cant send transaction (retryable)",
"provider", p.name,
"err", err,
"nonce", nonce)
if time.Since(firstAttemptAt) >= time.Duration(p.config.SendTransactionRetryTimeout) { if time.Since(firstAttemptAt) >= time.Duration(p.config.SendTransactionRetryTimeout) {
log.Error("send transaction timed out (known already)", log.Error("send transaction timed out (known already)",
"provider", p.name, "provider", p.name,
"hash", txHash.Hex(), "hash", txHash.Hex(),
"nonce", nonce,
"elapsed", time.Since(firstAttemptAt), "elapsed", time.Since(firstAttemptAt),
"attempt", attempt, "attempt", attempt)
"nonce", nonce) metrics.RecordErrorDetails(p.name, "send.timeout", err)
metrics.RecordError(p.name, "ethclient.SendTransaction.nonce")
return return
} }
log.Warn("tx already known, incrementing nonce and trying again", log.Warn("tx already known, incrementing nonce and trying again",
"provider", p.name, "provider", p.name,
"nonce", nonce) "nonce", nonce)
time.Sleep(time.Duration(p.config.SendTransactionRetryInterval)) time.Sleep(time.Duration(p.config.SendTransactionRetryInterval))
p.txPool.M.Lock() nonce++
p.txPool.Nonce++
nonce = p.txPool.Nonce
p.txPool.M.Unlock()
attempt++ attempt++
if attempt%10 == 0 { if attempt%10 == 0 {
log.Debug("retrying send transaction...", log.Debug("retrying send transaction...",
...@@ -108,6 +117,7 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -108,6 +117,7 @@ func (p *Provider) RoundTrip(ctx context.Context) {
} else { } else {
log.Error("cant send transaction", log.Error("cant send transaction",
"provider", p.name, "provider", p.name,
"nonce", nonce,
"err", err) "err", err)
metrics.RecordErrorDetails(p.name, "ethclient.SendTransaction", err) metrics.RecordErrorDetails(p.name, "ethclient.SendTransaction", err)
return return
...@@ -131,6 +141,7 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -131,6 +141,7 @@ func (p *Provider) RoundTrip(ctx context.Context) {
SentAt: sentAt, SentAt: sentAt,
SeenBy: make(map[string]time.Time), SeenBy: make(map[string]time.Time),
} }
p.txPool.LastSend = sentAt
p.txPool.M.Unlock() p.txPool.M.Unlock()
var receipt *types.Receipt var receipt *types.Receipt
...@@ -140,13 +151,17 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -140,13 +151,17 @@ func (p *Provider) RoundTrip(ctx context.Context) {
log.Error("receipt retrieval timed out", log.Error("receipt retrieval timed out",
"provider", p.name, "provider", p.name,
"hash", txHash, "hash", txHash,
"nonce", nonce,
"elapsed", time.Since(sentAt)) "elapsed", time.Since(sentAt))
metrics.RecordErrorDetails(p.name, "receipt.timeout", err)
return return
} }
time.Sleep(time.Duration(p.config.ReceiptRetrievalInterval)) time.Sleep(time.Duration(p.config.ReceiptRetrievalInterval))
if attempt%10 == 0 { if attempt%10 == 0 {
log.Debug("checking for receipt...", log.Debug("checking for receipt...",
"provider", p.name, "provider", p.name,
"hash", txHash,
"nonce", nonce,
"attempt", attempt, "attempt", attempt,
"elapsed", time.Since(sentAt)) "elapsed", time.Since(sentAt))
} }
...@@ -155,6 +170,7 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -155,6 +170,7 @@ func (p *Provider) RoundTrip(ctx context.Context) {
log.Error("cant get receipt for transaction", log.Error("cant get receipt for transaction",
"provider", p.name, "provider", p.name,
"hash", txHash.Hex(), "hash", txHash.Hex(),
"nonce", nonce,
"err", err) "err", err)
return return
} }
...@@ -168,6 +184,7 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -168,6 +184,7 @@ func (p *Provider) RoundTrip(ctx context.Context) {
log.Info("got transaction receipt", log.Info("got transaction receipt",
"hash", txHash.Hex(), "hash", txHash.Hex(),
"nonce", nonce,
"roundTripLatency", roundTripLatency, "roundTripLatency", roundTripLatency,
"provider", p.name, "provider", p.name,
"blockNumber", receipt.BlockNumber, "blockNumber", receipt.BlockNumber,
...@@ -175,20 +192,83 @@ func (p *Provider) RoundTrip(ctx context.Context) { ...@@ -175,20 +192,83 @@ func (p *Provider) RoundTrip(ctx context.Context) {
"gasUsed", receipt.GasUsed) "gasUsed", receipt.GasUsed)
} }
func (p *Provider) createTx(nonce uint64) *types.Transaction { func (p *Provider) createTx(ctx context.Context, client *iclients.InstrumentedEthClient, nonce uint64) (*types.Transaction, error) {
toAddress := common.HexToAddress(p.walletConfig.Address) var err error
if nonce == 0 {
nonce, err = client.PendingNonceAt(ctx, p.walletConfig.Address)
if err != nil {
log.Error("cant get nounce",
"provider", p.name,
"nonce", nonce,
"err", err)
return nil, err
}
}
gasTipCap, err := client.SuggestGasTipCap(ctx)
if err != nil {
log.Error("cant get gas tip cap",
"provider", p.name,
"err", err)
return nil, err
}
gasTipCap = new(big.Int).Mul(gasTipCap, big.NewInt(110))
gasTipCap = new(big.Int).Div(gasTipCap, big.NewInt(100))
head, err := client.HeaderByNumber(ctx, nil)
if err != nil {
log.Error("cant get base fee from head",
"provider", p.name,
"err", err)
return nil, err
}
baseFee := head.BaseFee
gasFeeCap := new(big.Int).Add(
gasTipCap,
new(big.Int).Mul(baseFee, big.NewInt(2)))
addr := common.HexToAddress(p.walletConfig.Address)
var data []byte var data []byte
tx := types.NewTx(&types.DynamicFeeTx{ dynamicTx := &types.DynamicFeeTx{
ChainID: &p.walletConfig.ChainID, ChainID: &p.walletConfig.ChainID,
Nonce: nonce, Nonce: nonce,
GasFeeCap: &p.walletConfig.GasFeeCap, GasFeeCap: gasFeeCap,
GasTipCap: &p.walletConfig.GasTipCap, GasTipCap: gasTipCap,
Gas: p.walletConfig.GasLimit, To: &addr,
To: &toAddress,
Value: &p.walletConfig.TxValue, Value: &p.walletConfig.TxValue,
Data: data, Data: data,
}
gas, err := client.EstimateGas(ctx, ethereum.CallMsg{
From: addr,
To: &addr,
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
Data: dynamicTx.Data,
Value: dynamicTx.Value,
}) })
return tx if err != nil {
log.Error("cant estimate gas",
"provider", p.name,
"err", err)
return nil, err
}
dynamicTx.Gas = gas
tx := types.NewTx(dynamicTx)
log.Info("tx created",
"provider", p.name,
"from", addr,
"to", dynamicTx.To,
"nonce", dynamicTx.Nonce,
"value", dynamicTx.Value,
"gas", dynamicTx.Gas,
"gasTipCap", dynamicTx.GasTipCap,
"gasFeeCap", dynamicTx.GasFeeCap,
)
return tx, nil
} }
func (p *Provider) sign(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) { func (p *Provider) sign(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) {
......
...@@ -15,7 +15,11 @@ type NetworkTransactionPool struct { ...@@ -15,7 +15,11 @@ type NetworkTransactionPool struct {
M sync.Mutex M sync.Mutex
Transactions map[string]*TransactionState Transactions map[string]*TransactionState
Expected int Expected int
Nonce uint64
// Last time a transaction was sent
LastSend time.Time
// Prevents concurrent transaction send
ExclusiveSend sync.Mutex
} }
type TransactionState struct { type TransactionState struct {
......
...@@ -26,3 +26,6 @@ deployments/hardhat ...@@ -26,3 +26,6 @@ deployments/hardhat
deployments/getting-started deployments/getting-started
deployments/*/.deploy deployments/*/.deploy
deployments/1337 deployments/1337
# Devnet config which changes with each 'make devnet-up'
deploy-config/devnetL1.json
...@@ -2560,6 +2560,10 @@ packages: ...@@ -2560,6 +2560,10 @@ packages:
resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==}
engines: {node: '>= 16'} engines: {node: '>= 16'}
/@noble/hashes@1.3.2:
resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==}
engines: {node: '>= 16'}
/@nodelib/fs.scandir@2.1.5: /@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
...@@ -3097,7 +3101,7 @@ packages: ...@@ -3097,7 +3101,7 @@ packages:
/@scure/bip39@1.2.1: /@scure/bip39@1.2.1:
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
dependencies: dependencies:
'@noble/hashes': 1.3.1 '@noble/hashes': 1.3.2
'@scure/base': 1.1.1 '@scure/base': 1.1.1
/@sentry-internal/tracing@7.64.0: /@sentry-internal/tracing@7.64.0:
...@@ -3757,20 +3761,20 @@ packages: ...@@ -3757,20 +3761,20 @@ packages:
/@types/bn.js@4.11.6: /@types/bn.js@4.11.6:
resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/bn.js@5.1.0: /@types/bn.js@5.1.0:
resolution: {integrity: sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==} resolution: {integrity: sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/body-parser@1.19.1: /@types/body-parser@1.19.1:
resolution: {integrity: sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==} resolution: {integrity: sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==}
dependencies: dependencies:
'@types/connect': 3.4.35 '@types/connect': 3.4.35
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/chai-as-promised@7.1.5: /@types/chai-as-promised@7.1.5:
...@@ -3796,7 +3800,7 @@ packages: ...@@ -3796,7 +3800,7 @@ packages:
/@types/connect@3.4.35: /@types/connect@3.4.35:
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
/@types/dateformat@5.0.0: /@types/dateformat@5.0.0:
resolution: {integrity: sha512-SZg4JdHIWHQGEokbYGZSDvo5wA4TLYPXaqhigs/wH+REDOejcJzgH+qyY+HtEUtWOZxEUkbhbdYPqQDiEgrXeA==} resolution: {integrity: sha512-SZg4JdHIWHQGEokbYGZSDvo5wA4TLYPXaqhigs/wH+REDOejcJzgH+qyY+HtEUtWOZxEUkbhbdYPqQDiEgrXeA==}
...@@ -3810,7 +3814,7 @@ packages: ...@@ -3810,7 +3814,7 @@ packages:
/@types/express-serve-static-core@4.17.35: /@types/express-serve-static-core@4.17.35:
resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==} resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
'@types/qs': 6.9.7 '@types/qs': 6.9.7
'@types/range-parser': 1.2.4 '@types/range-parser': 1.2.4
'@types/send': 0.17.1 '@types/send': 0.17.1
...@@ -3856,7 +3860,7 @@ packages: ...@@ -3856,7 +3860,7 @@ packages:
dependencies: dependencies:
'@types/abstract-leveldown': 5.0.2 '@types/abstract-leveldown': 5.0.2
'@types/level-errors': 3.0.0 '@types/level-errors': 3.0.0
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/lru-cache@5.1.1: /@types/lru-cache@5.1.1:
...@@ -3887,7 +3891,7 @@ packages: ...@@ -3887,7 +3891,7 @@ packages:
/@types/mkdirp@0.5.2: /@types/mkdirp@0.5.2:
resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==} resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/mocha@10.0.1: /@types/mocha@10.0.1:
...@@ -3906,7 +3910,7 @@ packages: ...@@ -3906,7 +3910,7 @@ packages:
/@types/node-fetch@2.6.4: /@types/node-fetch@2.6.4:
resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
form-data: 3.0.1 form-data: 3.0.1
dev: true dev: true
...@@ -3919,10 +3923,10 @@ packages: ...@@ -3919,10 +3923,10 @@ packages:
/@types/node@20.5.0: /@types/node@20.5.0:
resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==} resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==}
dev: true
/@types/node@20.5.3: /@types/node@20.5.3:
resolution: {integrity: sha512-ITI7rbWczR8a/S6qjAW7DMqxqFMjjTo61qZVWJ1ubPvbIQsL5D/TvwjYEalM8Kthpe3hTzOGrF2TGbAu2uyqeA==} resolution: {integrity: sha512-ITI7rbWczR8a/S6qjAW7DMqxqFMjjTo61qZVWJ1ubPvbIQsL5D/TvwjYEalM8Kthpe3hTzOGrF2TGbAu2uyqeA==}
dev: true
/@types/normalize-package-data@2.4.1: /@types/normalize-package-data@2.4.1:
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
...@@ -3934,7 +3938,7 @@ packages: ...@@ -3934,7 +3938,7 @@ packages:
/@types/pbkdf2@3.1.0: /@types/pbkdf2@3.1.0:
resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/pino-multi-stream@5.1.3: /@types/pino-multi-stream@5.1.3:
...@@ -3952,13 +3956,13 @@ packages: ...@@ -3952,13 +3956,13 @@ packages:
/@types/pino-std-serializers@2.4.1: /@types/pino-std-serializers@2.4.1:
resolution: {integrity: sha512-17XcksO47M24IVTVKPeAByWUd3Oez7EbIjXpSbzMPhXVzgjGtrOa49gKBwxH9hb8dKv58OelsWQ+A1G1l9S3wQ==} resolution: {integrity: sha512-17XcksO47M24IVTVKPeAByWUd3Oez7EbIjXpSbzMPhXVzgjGtrOa49gKBwxH9hb8dKv58OelsWQ+A1G1l9S3wQ==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/pino@6.3.11: /@types/pino@6.3.11:
resolution: {integrity: sha512-S7+fLONqSpHeW9d7TApUqO6VN47KYgOXhCNKwGBVLHObq8HhaAYlVqUNdfnvoXjCMiwE5xcPm/5R2ZUh8bgaXQ==} resolution: {integrity: sha512-S7+fLONqSpHeW9d7TApUqO6VN47KYgOXhCNKwGBVLHObq8HhaAYlVqUNdfnvoXjCMiwE5xcPm/5R2ZUh8bgaXQ==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
'@types/pino-pretty': 4.7.1 '@types/pino-pretty': 4.7.1
'@types/pino-std-serializers': 2.4.1 '@types/pino-std-serializers': 2.4.1
sonic-boom: 2.8.0 sonic-boom: 2.8.0
...@@ -4008,7 +4012,7 @@ packages: ...@@ -4008,7 +4012,7 @@ packages:
/@types/secp256k1@4.0.3: /@types/secp256k1@4.0.3:
resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/seedrandom@3.0.1: /@types/seedrandom@3.0.1:
...@@ -4027,14 +4031,14 @@ packages: ...@@ -4027,14 +4031,14 @@ packages:
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
dependencies: dependencies:
'@types/mime': 1.3.2 '@types/mime': 1.3.2
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/serve-static@1.13.10: /@types/serve-static@1.13.10:
resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==} resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==}
dependencies: dependencies:
'@types/mime': 1.3.2 '@types/mime': 1.3.2
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@types/sinon-chai@3.2.5: /@types/sinon-chai@3.2.5:
...@@ -4071,18 +4075,18 @@ packages: ...@@ -4071,18 +4075,18 @@ packages:
/@types/ws@7.4.7: /@types/ws@7.4.7:
resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
/@types/ws@8.5.3: /@types/ws@8.5.3:
resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: false dev: false
/@types/ws@8.5.5: /@types/ws@8.5.5:
resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==}
dependencies: dependencies:
'@types/node': 20.5.0 '@types/node': 20.5.3
dev: true dev: true
/@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.60.1)(eslint@8.47.0)(typescript@5.1.6): /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.60.1)(eslint@8.47.0)(typescript@5.1.6):
......
This diff is collapsed.
This diff is collapsed.
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