Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
3c916141
Unverified
Commit
3c916141
authored
Aug 18, 2023
by
mergify[bot]
Committed by
GitHub
Aug 18, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into aj/cannon-factory-addrs
parents
e2aa54d0
fc8e68fc
Changes
18
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
341 additions
and
555 deletions
+341
-555
hip-olives-press.md
.changeset/hip-olives-press.md
+5
-0
system_test.go
op-e2e/system_test.go
+2
-2
rpc.go
op-node/client/rpc.go
+3
-3
script.sh
op-node/cmd/batch_decoder/script.sh
+3
-0
state.go
op-node/rollup/driver/state.go
+2
-2
sync_client.go
op-node/sources/sync_client.go
+3
-3
retry.go
op-program/host/prefetcher/retry.go
+12
-12
retry_test.go
op-program/host/prefetcher/retry_test.go
+5
-5
dial.go
op-service/client/dial.go
+3
-3
operation.go
op-service/retry/operation.go
+1
-1
operation_test.go
op-service/retry/operation_test.go
+1
-1
strategies.go
op-service/retry/strategies.go
+1
-1
strategies_test.go
op-service/retry/strategies_test.go
+1
-1
txmgr.go
op-service/txmgr/txmgr.go
+2
-2
package.json
packages/common-ts/package.json
+1
-1
.depcheckrc
packages/sdk/.depcheckrc
+1
-0
package.json
packages/sdk/package.json
+14
-13
pnpm-lock.yaml
pnpm-lock.yaml
+281
-505
No files found.
.changeset/hip-olives-press.md
0 → 100644
View file @
3c916141
---
'
@eth-optimism/sdk'
:
patch
---
Updated npm dependencies to latest
op-e2e/system_test.go
View file @
3c916141
...
...
@@ -34,9 +34,9 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth"
oppprof
"github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum-optimism/optimism/op-service/retry"
)
func
TestL2OutputSubmitter
(
t
*
testing
.
T
)
{
...
...
@@ -533,7 +533,7 @@ func TestSystemMockP2P(t *testing.T) {
// poll to see if the verifier node is connected & meshed on gossip.
// Without this verifier, we shouldn't start sending blocks around, or we'll miss them and fail the test.
backOffStrategy
:=
backoff
.
Exponential
()
backOffStrategy
:=
retry
.
Exponential
()
for
i
:=
0
;
i
<
10
;
i
++
{
if
check
()
{
break
...
...
op-node/client/rpc.go
View file @
3c916141
...
...
@@ -8,7 +8,7 @@ import (
"regexp"
"time"
"github.com/ethereum-optimism/optimism/op-service/
backoff
"
"github.com/ethereum-optimism/optimism/op-service/
retry
"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus"
...
...
@@ -103,8 +103,8 @@ func NewRPC(ctx context.Context, lgr log.Logger, addr string, opts ...RPCOption)
// Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional.
func
dialRPCClientWithBackoff
(
ctx
context
.
Context
,
log
log
.
Logger
,
addr
string
,
attempts
int
,
opts
...
rpc
.
ClientOption
)
(
*
rpc
.
Client
,
error
)
{
bOff
:=
backoff
.
Exponential
()
return
backoff
.
Do
(
ctx
,
attempts
,
bOff
,
func
()
(
*
rpc
.
Client
,
error
)
{
bOff
:=
retry
.
Exponential
()
return
retry
.
Do
(
ctx
,
attempts
,
bOff
,
func
()
(
*
rpc
.
Client
,
error
)
{
if
!
IsURLAvailable
(
addr
)
{
log
.
Warn
(
"failed to dial address, but may connect later"
,
"addr"
,
addr
)
return
nil
,
fmt
.
Errorf
(
"address unavailable (%s)"
,
addr
)
...
...
op-node/cmd/batch_decoder/script.sh
0 → 100755
View file @
3c916141
echo
$1
jq
'.frames[] | {timestamp, inclusion_block}'
$1
jq
'.batches[]|.Timestamp'
$1
op-node/rollup/driver/state.go
View file @
3c916141
...
...
@@ -15,8 +15,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
)
// Deprecated: use eth.SyncStatus instead.
...
...
@@ -178,7 +178,7 @@ func (s *Driver) eventLoop() {
var
delayedStepReq
<-
chan
time
.
Time
// keep track of consecutive failed attempts, to adjust the backoff time accordingly
bOffStrategy
:=
backoff
.
Exponential
()
bOffStrategy
:=
retry
.
Exponential
()
stepAttempts
:=
0
// step requests a derivation step to be taken. Won't deadlock if the channel is full.
...
...
op-node/sources/sync_client.go
View file @
3c916141
...
...
@@ -11,8 +11,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/sources/caching"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/log"
"github.com/libp2p/go-libp2p/core/peer"
...
...
@@ -130,7 +130,7 @@ func (s *SyncClient) eventLoop() {
defer
s
.
wg
.
Done
()
s
.
log
.
Info
(
"Starting sync client event loop"
)
backoffStrategy
:=
&
backoff
.
ExponentialStrategy
{
backoffStrategy
:=
&
retry
.
ExponentialStrategy
{
Min
:
1000
*
time
.
Millisecond
,
Max
:
20
_000
*
time
.
Millisecond
,
MaxJitter
:
250
*
time
.
Millisecond
,
...
...
@@ -142,7 +142,7 @@ func (s *SyncClient) eventLoop() {
s
.
log
.
Debug
(
"Shutting down RPC sync worker"
)
return
case
reqNum
:=
<-
s
.
requests
:
_
,
err
:=
backoff
.
Do
(
s
.
resCtx
,
5
,
backoffStrategy
,
func
()
(
interface
{},
error
)
{
_
,
err
:=
retry
.
Do
(
s
.
resCtx
,
5
,
backoffStrategy
,
func
()
(
interface
{},
error
)
{
// Limit the maximum time for fetching payloads
ctx
,
cancel
:=
context
.
WithTimeout
(
s
.
resCtx
,
time
.
Second
*
10
)
defer
cancel
()
...
...
op-program/host/prefetcher/retry.go
View file @
3c916141
...
...
@@ -4,8 +4,8 @@ import (
"context"
"math"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -16,19 +16,19 @@ const maxAttempts = math.MaxInt // Succeed or die trying
type
RetryingL1Source
struct
{
logger
log
.
Logger
source
L1Source
strategy
backoff
.
Strategy
strategy
retry
.
Strategy
}
func
NewRetryingL1Source
(
logger
log
.
Logger
,
source
L1Source
)
*
RetryingL1Source
{
return
&
RetryingL1Source
{
logger
:
logger
,
source
:
source
,
strategy
:
backoff
.
Exponential
(),
strategy
:
retry
.
Exponential
(),
}
}
func
(
s
*
RetryingL1Source
)
InfoByHash
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
eth
.
BlockInfo
,
error
)
{
return
backoff
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
error
)
{
return
retry
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
error
)
{
res
,
err
:=
s
.
source
.
InfoByHash
(
ctx
,
blockHash
)
if
err
!=
nil
{
s
.
logger
.
Warn
(
"Failed to retrieve info"
,
"hash"
,
blockHash
,
"err"
,
err
)
...
...
@@ -38,7 +38,7 @@ func (s *RetryingL1Source) InfoByHash(ctx context.Context, blockHash common.Hash
}
func
(
s
*
RetryingL1Source
)
InfoAndTxsByHash
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
eth
.
BlockInfo
,
types
.
Transactions
,
error
)
{
return
backoff
.
Do2
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
types
.
Transactions
,
error
)
{
return
retry
.
Do2
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
types
.
Transactions
,
error
)
{
i
,
t
,
err
:=
s
.
source
.
InfoAndTxsByHash
(
ctx
,
blockHash
)
if
err
!=
nil
{
s
.
logger
.
Warn
(
"Failed to retrieve l1 info and txs"
,
"hash"
,
blockHash
,
"err"
,
err
)
...
...
@@ -48,7 +48,7 @@ func (s *RetryingL1Source) InfoAndTxsByHash(ctx context.Context, blockHash commo
}
func
(
s
*
RetryingL1Source
)
FetchReceipts
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
eth
.
BlockInfo
,
types
.
Receipts
,
error
)
{
return
backoff
.
Do2
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
types
.
Receipts
,
error
)
{
return
retry
.
Do2
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
types
.
Receipts
,
error
)
{
i
,
r
,
err
:=
s
.
source
.
FetchReceipts
(
ctx
,
blockHash
)
if
err
!=
nil
{
s
.
logger
.
Warn
(
"Failed to fetch receipts"
,
"hash"
,
blockHash
,
"err"
,
err
)
...
...
@@ -62,11 +62,11 @@ var _ L1Source = (*RetryingL1Source)(nil)
type
RetryingL2Source
struct
{
logger
log
.
Logger
source
L2Source
strategy
backoff
.
Strategy
strategy
retry
.
Strategy
}
func
(
s
*
RetryingL2Source
)
InfoAndTxsByHash
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
eth
.
BlockInfo
,
types
.
Transactions
,
error
)
{
return
backoff
.
Do2
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
types
.
Transactions
,
error
)
{
return
retry
.
Do2
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
BlockInfo
,
types
.
Transactions
,
error
)
{
i
,
t
,
err
:=
s
.
source
.
InfoAndTxsByHash
(
ctx
,
blockHash
)
if
err
!=
nil
{
s
.
logger
.
Warn
(
"Failed to retrieve l2 info and txs"
,
"hash"
,
blockHash
,
"err"
,
err
)
...
...
@@ -76,7 +76,7 @@ func (s *RetryingL2Source) InfoAndTxsByHash(ctx context.Context, blockHash commo
}
func
(
s
*
RetryingL2Source
)
NodeByHash
(
ctx
context
.
Context
,
hash
common
.
Hash
)
([]
byte
,
error
)
{
return
backoff
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
([]
byte
,
error
)
{
return
retry
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
([]
byte
,
error
)
{
n
,
err
:=
s
.
source
.
NodeByHash
(
ctx
,
hash
)
if
err
!=
nil
{
s
.
logger
.
Warn
(
"Failed to retrieve node"
,
"hash"
,
hash
,
"err"
,
err
)
...
...
@@ -86,7 +86,7 @@ func (s *RetryingL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]
}
func
(
s
*
RetryingL2Source
)
CodeByHash
(
ctx
context
.
Context
,
hash
common
.
Hash
)
([]
byte
,
error
)
{
return
backoff
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
([]
byte
,
error
)
{
return
retry
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
([]
byte
,
error
)
{
c
,
err
:=
s
.
source
.
CodeByHash
(
ctx
,
hash
)
if
err
!=
nil
{
s
.
logger
.
Warn
(
"Failed to retrieve code"
,
"hash"
,
hash
,
"err"
,
err
)
...
...
@@ -96,7 +96,7 @@ func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]
}
func
(
s
*
RetryingL2Source
)
OutputByRoot
(
ctx
context
.
Context
,
root
common
.
Hash
)
(
eth
.
Output
,
error
)
{
return
backoff
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
Output
,
error
)
{
return
retry
.
Do
(
ctx
,
maxAttempts
,
s
.
strategy
,
func
()
(
eth
.
Output
,
error
)
{
o
,
err
:=
s
.
source
.
OutputByRoot
(
ctx
,
root
)
if
err
!=
nil
{
s
.
logger
.
Warn
(
"Failed to fetch l2 output"
,
"root"
,
root
,
"err"
,
err
)
...
...
@@ -110,7 +110,7 @@ func NewRetryingL2Source(logger log.Logger, source L2Source) *RetryingL2Source {
return
&
RetryingL2Source
{
logger
:
logger
,
source
:
source
,
strategy
:
backoff
.
Exponential
(),
strategy
:
retry
.
Exponential
(),
}
}
...
...
op-program/host/prefetcher/retry_test.go
View file @
3c916141
...
...
@@ -7,8 +7,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -104,8 +104,8 @@ func createL1Source(t *testing.T) (*RetryingL1Source, *testutils.MockL1Source) {
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlDebug
)
mock
:=
&
testutils
.
MockL1Source
{}
source
:=
NewRetryingL1Source
(
logger
,
mock
)
// Avoid sleeping in tests by using a fixed
backoff
strategy with no delay
source
.
strategy
=
backoff
.
Fixed
(
0
)
// Avoid sleeping in tests by using a fixed
retry
strategy with no delay
source
.
strategy
=
retry
.
Fixed
(
0
)
return
source
,
mock
}
...
...
@@ -217,8 +217,8 @@ func createL2Source(t *testing.T) (*RetryingL2Source, *MockL2Source) {
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlDebug
)
mock
:=
&
MockL2Source
{}
source
:=
NewRetryingL2Source
(
logger
,
mock
)
// Avoid sleeping in tests by using a fixed
backoff
strategy with no delay
source
.
strategy
=
backoff
.
Fixed
(
0
)
// Avoid sleeping in tests by using a fixed
retry
strategy with no delay
source
.
strategy
=
retry
.
Fixed
(
0
)
return
source
,
mock
}
...
...
op-service/client/dial.go
View file @
3c916141
...
...
@@ -7,7 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-service/
backoff
"
"github.com/ethereum-optimism/optimism/op-service/
retry
"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
...
...
@@ -49,8 +49,8 @@ func DialRollupClientWithTimeout(timeout time.Duration, log log.Logger, url stri
// Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional.
func
dialRPCClientWithBackoff
(
ctx
context
.
Context
,
log
log
.
Logger
,
addr
string
)
(
*
rpc
.
Client
,
error
)
{
bOff
:=
backoff
.
Fixed
(
defaultRetryTime
)
return
backoff
.
Do
(
ctx
,
defaultRetryCount
,
bOff
,
func
()
(
*
rpc
.
Client
,
error
)
{
bOff
:=
retry
.
Fixed
(
defaultRetryTime
)
return
retry
.
Do
(
ctx
,
defaultRetryCount
,
bOff
,
func
()
(
*
rpc
.
Client
,
error
)
{
if
!
client
.
IsURLAvailable
(
addr
)
{
log
.
Warn
(
"failed to dial address, but may connect later"
,
"addr"
,
addr
)
return
nil
,
fmt
.
Errorf
(
"address unavailable (%s)"
,
addr
)
...
...
op-service/
backoff
/operation.go
→
op-service/
retry
/operation.go
View file @
3c916141
package
backoff
package
retry
import
(
"context"
...
...
op-service/
backoff
/operation_test.go
→
op-service/
retry
/operation_test.go
View file @
3c916141
package
backoff
package
retry
import
(
"context"
...
...
op-service/
backoff
/strategies.go
→
op-service/
retry
/strategies.go
View file @
3c916141
package
backoff
package
retry
import
(
"math"
...
...
op-service/
backoff
/strategies_test.go
→
op-service/
retry
/strategies_test.go
View file @
3c916141
package
backoff
package
retry
import
(
"testing"
...
...
op-service/txmgr/txmgr.go
View file @
3c916141
...
...
@@ -17,7 +17,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/
backoff
"
"github.com/ethereum-optimism/optimism/op-service/
retry
"
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
)
...
...
@@ -176,7 +176,7 @@ func (m *SimpleTxManager) send(ctx context.Context, candidate TxCandidate) (*typ
ctx
,
cancel
=
context
.
WithTimeout
(
ctx
,
m
.
cfg
.
TxSendTimeout
)
defer
cancel
()
}
tx
,
err
:=
backoff
.
Do
(
ctx
,
30
,
backoff
.
Fixed
(
2
*
time
.
Second
),
func
()
(
*
types
.
Transaction
,
error
)
{
tx
,
err
:=
retry
.
Do
(
ctx
,
30
,
retry
.
Fixed
(
2
*
time
.
Second
),
func
()
(
*
types
.
Transaction
,
error
)
{
tx
,
err
:=
m
.
craftTx
(
ctx
,
candidate
)
if
err
!=
nil
{
m
.
l
.
Warn
(
"Failed to create a transaction, will retry"
,
"err"
,
err
)
...
...
packages/common-ts/package.json
View file @
3c916141
...
...
@@ -46,7 +46,7 @@
"express-prom-bundle"
:
"^6.4.1"
,
"lodash"
:
"^4.17.21"
,
"morgan"
:
"^1.10.0"
,
"pino"
:
"^
6.11.3
"
,
"pino"
:
"^
8.15.0
"
,
"pino-multi-stream"
:
"^5.3.0"
,
"pino-sentry"
:
"^0.14.0"
,
"prom-client"
:
"^13.1.0"
...
...
packages/sdk/.depcheckrc
View file @
3c916141
...
...
@@ -12,6 +12,7 @@ ignores: [
"chai",
"ts-node",
"typedoc",
"typescript",
"ethereum-waffle",
"nyc"
]
packages/sdk/package.json
View file @
3c916141
...
...
@@ -41,30 +41,31 @@
"@ethersproject/transactions"
:
"^5.7.0"
,
"@nomiclabs/hardhat-ethers"
:
"^2.0.2"
,
"@nomiclabs/hardhat-waffle"
:
"^2.0.1"
,
"@types/chai"
:
"^4.3.5"
,
"@types/chai-as-promised"
:
"^7.1.5"
,
"@types/mocha"
:
"^10.0.1"
,
"@types/node"
:
"^20.5.0"
,
"chai-as-promised"
:
"^7.1.1"
,
"ethereum-waffle"
:
"^4.0.10"
,
"ethers"
:
"^5.7.
0
"
,
"ethers"
:
"^5.7.
2
"
,
"hardhat"
:
"^2.9.6"
,
"hardhat-deploy"
:
"^0.11.4"
,
"isomorphic-fetch"
:
"^3.0.0"
,
"mocha"
:
"^10.
0
.0"
,
"mocha"
:
"^10.
2
.0"
,
"nyc"
:
"^15.1.0"
,
"ts-node"
:
"^10.9.1"
,
"typedoc"
:
"^0.22.13"
,
"viem"
:
"^0.3.30"
,
"vitest"
:
"^0.28.3"
,
"zod"
:
"^3.11.6"
"typedoc"
:
"^0.24.8"
,
"typescript"
:
"^5.1.6"
,
"viem"
:
"^1.6.0"
,
"vitest"
:
"^0.34.2"
,
"zod"
:
"^3.22.1"
},
"dependencies"
:
{
"@eth-optimism/contracts"
:
"0.6.0"
,
"@eth-optimism/contracts-bedrock"
:
"0.16.0"
,
"@eth-optimism/core-utils"
:
"0.12.3"
,
"@types/chai"
:
"^4.2.18"
,
"@types/chai-as-promised"
:
"^7.1.4"
,
"@types/mocha"
:
"^10.0.1"
,
"@types/node"
:
"^20.5.0"
,
"@eth-optimism/contracts-bedrock"
:
"workspace:*"
,
"@eth-optimism/core-utils"
:
"workspace:*"
,
"lodash"
:
"^4.17.21"
,
"merkletreejs"
:
"^0.
2.27
"
,
"merkletreejs"
:
"^0.
3.10
"
,
"rlp"
:
"^2.2.7"
},
"peerDependencies"
:
{
...
...
pnpm-lock.yaml
View file @
3c916141
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment