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
686bfc72
Unverified
Commit
686bfc72
authored
Jan 04, 2024
by
Mark Tyneway
Committed by
GitHub
Jan 04, 2024
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #8602 from ethereum-optimism/eclipse-specs
specs: Ecotone / 4844 / Dencun
parents
b37c865f
bc4ca067
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
551 additions
and
36 deletions
+551
-36
deposits.md
specs/deposits.md
+62
-15
derivation.md
specs/derivation.md
+342
-7
exec-engine.md
specs/exec-engine.md
+83
-3
predeploys.md
specs/predeploys.md
+11
-2
superchain-upgrades.md
specs/superchain-upgrades.md
+30
-4
system_config.md
specs/system_config.md
+23
-5
No files found.
specs/deposits.md
View file @
686bfc72
...
...
@@ -30,10 +30,12 @@ with the authorization and validation conditions on L2.
-
[
Nonce Handling
](
#nonce-handling
)
-
[
Deposit Receipt
](
#deposit-receipt
)
-
[
L1 Attributes Deposited Transaction
](
#l1-attributes-deposited-transaction
)
-
[
L1 Attributes Deposited Transaction Calldata
](
#l1-attributes-deposited-transaction-calldata
)
-
[
Special Accounts on L2
](
#special-accounts-on-l2
)
-
[
L1 Attributes Depositor Account
](
#l1-attributes-depositor-account
)
-
[
L1 Attributes Predeployed Contract
](
#l1-attributes-predeployed-contract
)
-
[
L1 Attributes Predeployed Contract: Reference Implementation
](
#l1-attributes-predeployed-contract-reference-implementation
)
-
[
Ecotone L1Block upgrade
](
#ecotone-l1block-upgrade
)
-
[
User-Deposited Transactions
](
#user-deposited-transactions
)
-
[
Deposit Contract
](
#deposit-contract
)
-
[
Address Aliasing
](
#address-aliasing
)
...
...
@@ -103,6 +105,8 @@ The `sourceHash` of a deposit transaction is computed based on the origin:
And
`seqNumber = l2BlockNum - l2EpochStartBlockNum`
,
where
`l2BlockNum`
is the L2 block number of the inclusion of the deposit tx in L2,
and
`l2EpochStartBlockNum`
is the L2 block number of the first L2 block in the epoch.
-
Upgrade-deposited:
`keccak256(bytes32(uint256(2)), keccak256(intent))`
.
Where
`intent`
is a UTF-8 byte string, identifying the upgrade intent.
Without a
`sourceHash`
in a deposit, two different deposited transactions could have the same exact hash.
...
...
@@ -160,8 +164,9 @@ The deposit transaction is processed exactly like a type-3 (EIP-1559) transactio
-
No L1-cost fee is charged, as deposits are derived from L1 and do not have to be submitted as data back to it.
-
No base fee is charged. The total base fee accounting does not change.
Note that this includes contract-deployment behavior like with regular transactions,
and gas metering is the same (with the exception of fee related changes above), including metering of intrinsic gas.
Note that this includes contract-deployment behavior like with regular transactions, and gas
metering is the same (with the exception of fee related changes above), including metering of
intrinsic gas.
Any non-EVM state-transition error emitted by the EVM execution is processed in a special way:
...
...
@@ -241,19 +246,41 @@ This transaction MUST have the following values:
contract]
[
predeploy
]
).
3.
`mint`
is
`0`
4.
`value`
is
`0`
5.
`gasLimit`
is set to 150,000,000.
6.
`isSystemTx`
is set to
`true`
.
7.
`data`
is an
[
ABI
]
encoded call to the
[
L1 attributes predeployed contract
][
predeploy
]
's
`setL1BlockValues()`
function with correct values associated with the corresponding L1 block (cf.
[
reference implementation
][
l1-attr-ref-implem
]
).
If the Regolith upgrade is active, some fields are overridden:
1.
`gasLimit`
is set to 1,000,000
2.
`isSystemTx`
is set to
`false`
This system-initiated transaction for L1 attributes is not charged any ETH for its allocated
`gasLimit`
,
as it is effectively part of the state-transition processing.
5.
`gasLimit`
is set to 150,000,000 prior to the Regolith upgrade, and 1,000,000 after.
6.
`isSystemTx`
is set to
`true`
prior to the Regolith upgrade, and
`false`
after.
7.
`data`
is an encoded call to the
[
L1 attributes predeployed contract
][
predeploy
]
that
depends on the upgrades that are active (see below).
This system-initiated transaction for L1 attributes is not charged any ETH for its allocated
`gasLimit`
, as it is considered part of state-transition processing.
### L1 Attributes Deposited Transaction Calldata
Prior to the Ecotone upgrade, the
`data`
field of the L1 attributes deposited transaction is an
[
ABI
][
ABI
]
encoded call to the
`setL1BlockValues()`
function with correct values associated with
the corresponding L1 block (cf.
[
reference implementation
][
l1-attr-ref-implem
]
).
If the Ecotone upgrade is active, then
`data`
is instead a call to the
`setL1BlockValuesEcotone()`
function, where the input args are no longer ABI encoded function parameters, but are instead
packed into 5 32-byte aligned segments (starting after the function selector). Each unsigned
integer argument is encoded as big-endian using a number of bytes corresponding to the underlying
type. The overall calldata layout is as follows:
| Input arg | Type | Calldata bytes | Segment |
| ----------------- | ----------- | -------------- | --------|
| {0x440a5e20} | | 0-3 | n/a |
| basefeeScalar | uint32 | 4-7 | 1 |
| blobBasefeeScalar | uint32 | 8-11 | |
| sequenceNumber | uint64 | 12-19 | |
| l1BlockTimestamp | uint64 | 20-27 | |
| l1BlockNumber | uint64 | 28-35 | |
| basefee | uint256 | 36-67 | 2 |
| blobBasefee | uint256 | 68-99 | 3 |
| l1BlockHash | bytes32 | 100-131 | 4 |
| batcherHash | bytes32 | 132-163 | 5 |
Total calldata length must be exactly 164 bytes, implying the sixth and final segment is only
partially filled.
## Special Accounts on L2
...
...
@@ -292,6 +319,13 @@ The predeploy stores the following values:
-
`batcherHash`
(
`bytes32`
): A versioned commitment to the batch-submitter(s) currently operating.
-
`overhead`
(
`uint256`
): The L1 fee overhead to apply to L1 cost computation of transactions in this L2 block.
-
`scalar`
(
`uint256`
): The L1 fee scalar to apply to L1 cost computation of transactions in this L2 block.
-
With the Ecotone upgrade, the predeploy additionally stores:
-
`blobBasefee`
(
`uint256`
)
-
`baseFeeScalar`
(
`uint32`
): system configurable to scale the
`basefee`
in the Ecotone l1 cost computation
-
`blobBasefeeScalar`
(
`uint32`
): system configurable to scale the
`blobBasefee`
in the Ecotone l1 cost computation
Following the Ecotone upgrade,
`overhead`
and
`scalar`
are frozen at the values they had on the
block immediately prior to the fork.
The contract implements an authorization scheme, such that it only accepts state-changing calls from
the
[
depositor account
][
depositor-account
]
.
...
...
@@ -313,6 +347,19 @@ After running `pnpm build` in the `packages/contracts-bedrock` directory, the by
the genesis file will be located in the
`deployedBytecode`
field of the build artifacts file at
`/packages/contracts-bedrock/forge-artifacts/L1Block.sol/L1Block.json`
.
#### Ecotone L1Block upgrade
The L1 Attributes Predeployed contract,
`L1Block.sol`
, is upgraded as part of the Ecotone upgrade.
The version is incremented to
`1.2.0`
and several new storage slots are used for:
-
`blobBasefee`
(
`uint256`
): The L1 basefee for blob transactions.
-
`blobBasefeeScalar`
(
`uint256`
): The scalar value applied to the L1 blob base fee portion of the L1 cost.
-
`basefeeScalar`
(
`uint256`
): The scalar value applied to the L1 base fee portion of the L1 cost.
Additionally, the
`setL1BlockValues`
function is deprecated and replaced with
`setL1BlockValuesEcotone`
.
`setL1BlockValuesEcotone`
uses packed encoding for its parameters, which is described in
[
L1 Attributes Deposited Transaction Calldata
](
#l1-attributes-deposited-transaction-calldata
)
## User-Deposited Transactions
[
user-deposited
]:
#user-deposited-transactions
...
...
specs/derivation.md
View file @
686bfc72
This diff is collapsed.
Click to expand it.
specs/exec-engine.md
View file @
686bfc72
...
...
@@ -12,6 +12,8 @@
-
[
Priority fees (Sequencer Fee Vault)
](
#priority-fees-sequencer-fee-vault
)
-
[
Base fees (Base Fee Vault)
](
#base-fees-base-fee-vault
)
-
[
L1-Cost fees (L1 Fee Vault)
](
#l1-cost-fees-l1-fee-vault
)
-
[
Pre-Ecotone
](
#pre-ecotone
)
-
[
Ecotone L1-Cost fee changes (EIP-4844 DA)
](
#ecotone-l1-cost-fee-changes-eip-4844-da
)
-
[
Engine API
](
#engine-api
)
-
[
`engine_forkchoiceUpdatedV2`
](
#engine_forkchoiceupdatedv2
)
-
[
Extended PayloadAttributesV2
](
#extended-payloadattributesv2
)
...
...
@@ -22,6 +24,8 @@
-
[
Sync
](
#sync
)
-
[
Happy-path sync
](
#happy-path-sync
)
-
[
Worst-case sync
](
#worst-case-sync
)
-
[
Ecotone: disable Blob-transactions
](
#ecotone-disable-blob-transactions
)
-
[
Ecotone: Beacon Block Root
](
#ecotone-beacon-block-root
)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
...
...
@@ -96,9 +100,14 @@ The protocol funds batch-submission of sequenced L2 transactions by charging L2
based on the estimated batch-submission costs.
This fee is charged from the L2 transaction-sender ETH balance, and collected into the L1 Fee Vault.
The exact L1 cost function to determine the L1-cost fee component of a L2 transaction is calculated as:
`(rollupDataGas + l1FeeOverhead) * l1Basefee * l1FeeScalar / 1000000`
(big-int computation, result in Wei and
`uint256`
range)
The exact L1 cost function to determine the L1-cost fee component of a L2 transaction depends on
the upgrades that are active.
#### Pre-Ecotone
Before Ecotone activation, L1 cost is calculated as:
`(rollupDataGas + l1FeeOverhead) * l1Basefee * l1FeeScalar / 1e6`
(big-int computation, result
in Wei and
`uint256`
range)
Where:
-
`rollupDataGas`
is determined from the
*full*
encoded transaction
...
...
@@ -129,6 +138,44 @@ can be accessed in two interchangeable ways:
-
Overhead as big-endian
`uint256`
in slot
`5`
-
Scalar as big-endian
`uint256`
in slot
`6`
#### Ecotone L1-Cost fee changes (EIP-4844 DA)
Ecotone allows posting batches via Blobs which are subject to a new fee market. To account for this feature,
L1 cost is computed as:
`(compressedTxSize) * (l1Basefee*16*lBasefeeScalar + l1BlobBasefeeScalar*l1BlobBasefeeScalar) / 1e6`
Where:
-
the computation is an unlimited precision integer computation, with the result in Wei and having
`uint256`
range.
-
`compressedTxSize`
is an approximation of how many bytes the transaction occupies in a compressed
batch. It is determined from the
*full*
encoded transaction as:
`compressedTxSize = (zeroes*4 +
ones*16) / 16`
(To preserve precision under integer arithmetic, the division by 16 is actually
performed at the very end of the fee computation together with the division by 1e6 as a single
division by 16e6.)
-
`l1Basefee`
is the L1 basefee of the latest L1 origin registered in the L2 chain.
-
`l1BlobBasefee`
is the blob gasprice, computed as described in
[
EIP-4844
][
4844-gas
]
from the
header of the latest registered L1 origin block.
[
4844-gas
]:
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#gas-accounting
The two basefee values and their respective scalars can be accessed in two interchangeable
ways:
-
read from the deposited L1 attributes (
`l1BasefeeScalar`
,
`l1BlobBasefeeScalar`
,
`basefee`
,
`blobBasefee`
) of the current L2 block
-
read from the L1 Block Info contract (
`0x4200000000000000000000000000000000000015`
)
-
using the respective solidity getter functions
-
using direct storage-reads:
-
basefee
`uint256`
in slot
`1`
-
blobBasefee
`uint256`
in slot
`7`
-
l1BasefeeScalar big-endian
`uint32`
slot
`8`
offset
`4`
-
l1BlobBasefeeScalar as big-endian
`uint32`
in slot
`8`
offset
`0`
## Engine API
<!--
...
...
@@ -305,6 +352,39 @@ the operation within the engine is the exact same as with L1 (although with an E
[
rollup node spec
]:
rollup-node.md
## Ecotone: disable Blob-transactions
[
EIP-4844
]
introduces Blob transactions: featuring all the functionality of an
[
EIP-1559
]
transaction,
plus a list of "blobs": "Binary Large Object", i.e. a dedicated data type for serving Data-Availability as base-layer.
With the Ecotone upgrade, all Cancun L1 execution features are enabled, with
[
EIP-4844
]
as exception:
as a L2, the OP-Stack does not serve blobs, and thus disables this new transaction type.
EIP-4844 is disabled as following:
-
Transaction network-layer announcements, announcing blob-type transactions, are ignored.
-
Transactions of the blob-type, through the RPC or otherwise, are not allowed into the transaction pool.
-
Block-building code does not select EIP-4844 transactions.
-
An L2 block state-transition with EIP-4844 transactions is invalid.
## Ecotone: Beacon Block Root
[
EIP-4788
]
introduces a "beacon block root" into the execution-layer block-header and EVM.
This block root is an
[
SSZ hash-tree-root
]
of the consensus-layer contents of the previous consensus block.
With the adoption of
[
EIP-4399
]
in the Bedrock upgrade the OP-Stack already includes the
`PREVRANDAO`
of L1.
And thus with
[
EIP-4788
]
the L1 beacon block root is made available.
For the Ecotone upgrade, this entails that:
-
The
`parent_beacon_block_root`
of the L1 origin is now embedded in the L2 block header.
-
The "Beacon roots contract" is deployed at Ecotone upgrade-time, or embedded at genesis if activated at genesis.
-
The block state-transition process now includes the same special beacon-block-root EVM processing as L1 ethereum.
[
SSZ hash-tree-root
]:
https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization
[
EIP-4399
]:
https://eips.ethereum.org/EIPS/eip-4399
[
EIP-4788
]:
https://eips.ethereum.org/EIPS/eip-4788
[
EIP-4844
]:
https://eips.ethereum.org/EIPS/eip-4844
[
eip-1559
]:
https://eips.ethereum.org/EIPS/eip-1559
[
eip-2028
]:
https://eips.ethereum.org/EIPS/eip-2028
[
eip-2718
]:
https://eips.ethereum.org/EIPS/eip-2718
...
...
specs/predeploys.md
View file @
686bfc72
...
...
@@ -254,17 +254,26 @@ the L1 portion of the fee. This fee pays for using L1 as a data availability
layer and should be added to the L2 portion of the fee, which pays for
execution, to compute the total transaction fee.
The values used to compute the L
2 portion of the fe
e are:
The values used to compute the L
1 portion of the fee prior to the Ecotone upgrad
e are:
-
scalar
-
overhead
-
decimals
After the Bedrock upgrade, these values are instead managed by the
`SystemConfig`
contract on L
2
. The
`scalar`
and
`overhead`
values
`SystemConfig`
contract on L
1
. The
`scalar`
and
`overhead`
values
are sent to the
`L1Block`
contract each block and the
`decimals`
value
has been hardcoded to 6.
Following the Ecotone upgrade, the values used for L1 fee computation are:
-
l1BasefeeScalar
-
l1BlobBasefeeScalar
-
decimals
These values are managed by the
`SystemConfig`
contract on the L1. The
`decimals`
remains hardcoded
to 6, and the old
`scalar`
and
`overhead`
values are ignored.
## L1Block
[
Implementation
](
https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L1Block.sol
)
...
...
specs/superchain-upgrades.md
View file @
686bfc72
...
...
@@ -34,7 +34,7 @@ chains following the same Superchain Target upgrade synchronously.
-
[
Regolith
](
#regolith
)
-
[
Canyon
](
#canyon
)
-
[
Delta
](
#delta
)
-
[
Ec
lipse
](
#eclips
e
)
-
[
Ec
otone
](
#ecoton
e
)
-
[
Fjord
](
#fjord
)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
...
...
@@ -252,6 +252,7 @@ but the matching L1-origin information may not be present at the time of activat
-
`v4.0.0`
: TBD - Canyon.
[
Governance proposal
](
https://gov.optimism.io/t/final-upgrade-proposal-2-canyon-network-upgrade/7088
)
.
-
`v5.0.0-1`
: Delta - Experimental, devnet pre-release stage.
-
`v6.0.0-1`
: Ecotone - Very experimental, in development.
## Post-Bedrock Network upgrades
...
...
@@ -308,10 +309,35 @@ The Delta upgrade consists of a single consensus-layer feature: [Span Batches](.
The Delta upgrade uses a
*L2 block-timestamp*
activation-rule, and is specified only in the rollup-node (
`delta_time`
).
## Ec
lips
e
## Ec
oton
e
Name of the next upgrade after Delta. Placeholder for development coordination.
The Ecotone upgrade contains the Dencun upgrade from L1, and adopts EIP-4844 blobs for data-availability.
Dencun Upgrade:
-
Cancun (Execution Layer):
-
[
EIP-1153: Transient storage opcodes
](
https://eips.ethereum.org/EIPS/eip-1153
)
-
[
EIP-4844: Shard Blob Transactions
](
https://eips.ethereum.org/EIPS/eip-4844
)
-
[
Blob transactions are disabled
](
./exec-engine.md#ecotone-disable-blob-transactions
)
-
[
EIP-4788: Beacon block root in the EVM
](
https://eips.ethereum.org/EIPS/eip-4788
)
-
[
The L1 beacon block root is embedded into L2
](
./exec-engine.md#ecotone-beacon-block-root
)
-
[
The Beacon roots contract deployment is automated
](
./derivation.md#ecotone-beacon-block-roots-contract-deployment-eip-4788
)
-
[
EIP-5656: MCOPY - Memory copying instruction
](
https://eips.ethereum.org/EIPS/eip-5656
)
-
[
EIP-6780: SELFDESTRUCT only in same transaction
](
https://eips.ethereum.org/EIPS/eip-6780
)
-
[
EIP-7516: BLOBBASEFEE opcode
](
https://eips.ethereum.org/EIPS/eip-7516
)
-
Deneb (Consensus Layer):
*not applicable to L2*
-
[
EIP-7044: Perpetually Valid Signed Voluntary Exits
](
https://eips.ethereum.org/EIPS/eip-7044
)
-
[
EIP-7045: Increase Max Attestation Inclusion Slot
](
https://eips.ethereum.org/EIPS/eip-7045
)
-
[
EIP-7514: Add Max Epoch Churn Limit
](
https://eips.ethereum.org/EIPS/eip-7514
)
Data Availability (DA) upgrade:
-
Blobs Data Availability: support blobs DA the
[
L1 Data-retrieval stage
](
./derivation.md#ecotone-blob-retrieval
)
.
-
Rollup fee update: support blobs DA in
[
L1 Data Fee computation
](
./exec-engine.md#ecotone-l1-cost-fee-changes-eip-4844-da
)
-
Auto-upgrading and extension of the
[
L1 Attributes Predeployed Contract
](
./deposits.md#ecotone-l1block-upgrade
)
(also known as
`L1Block`
predeploy)
## Fjord
Name of the next upgrade after Ec
lips
e. Placeholder for development coordination.
Name of the next upgrade after Ec
oton
e. Placeholder for development coordination.
specs/system_config.md
View file @
686bfc72
...
...
@@ -6,7 +6,9 @@
-
[
System config contents (version 0)
](
#system-config-contents-version-0
)
-
[
`batcherHash` (`bytes32`)
](
#batcherhash-bytes32
)
-
[
`overhead` and `scalar` (`uint256,uint256`)
](
#overhead-and-scalar-uint256uint256
)
-
[
Scalars
](
#scalars
)
-
[
`overhead`,`scalar` (`uint256,uint256`)
](
#overheadscalar-uint256uint256
)
-
[
`l1BasefeeScalar`,`l1BlobBasefeeScalar` (`uint32,uint32`)
](
#l1basefeescalarl1blobbasefeescalar-uint32uint32
)
-
[
`gasLimit` (`uint64`)
](
#gaslimit-uint64
)
-
[
`unsafeBlockSigner` (`address`)
](
#unsafeblocksigner-address
)
-
[
Writing the system config
](
#writing-the-system-config
)
...
...
@@ -31,10 +33,20 @@ Version `0` embeds the current batch submitter ethereum address (`bytes20`) in t
In the future this versioned hash may become a commitment to a more extensive configuration,
to enable more extensive redundancy and/or rotation configurations.
###
`overhead` and `scalar` (`uint256,uint256`)
###
Scalars
The L1 fee parameters, also known as Gas Price Oracle (GPO) parameters,
are updated in conjunction and apply new L1 costs to the L2 transactions.
The L1 fee parameters, also known as Gas Price Oracle (GPO) parameters, are used to compute the L1
data fee applied to an L2 transaction. The specific parameters used depend on the upgrades that
are active.
#### `overhead`,`scalar` (`uint256,uint256`)
Prior to the Ecotone upgrade,
`overhead`
and
`scalar`
are consulted and passed to the L2 via L1
attribute info.
#### `l1BasefeeScalar`,`l1BlobBasefeeScalar` (`uint32,uint32`)
After the Ecotone upgrade,
`l1BasefeeScalar`
and
`l1BlobBasefeeScalar`
are passed to the L2 instead.
### `gasLimit` (`uint64`)
...
...
@@ -70,7 +82,9 @@ A rollup node initializes its derivation process by finding a starting point bas
-
When started from L2 genesis, the initial system configuration is retrieved from the rollup chain configuration.
-
When started from an existing L2 chain, a previously included L1 block is determined as derivation starting point,
and the system config can thus be retrieved from the last L2 block that referenced the L1 block as L1 origin:
-
`batcherHash`
,
`overhead`
and
`scalar`
are retrieved from the L1 block info transaction.
-
If the chain state precedes the Ecotone upgrade,
`batcherHash`
,
`overhead`
and
`scalar`
are
retrieved from the L1 block info transaction. Otherwise,
`batcherHash`
,
`l1BasefeeScalar`
, and
`l1BlobBasefeeScalar`
are retrieved instead.
-
`gasLimit`
is retrieved from the L2 block header.
-
other future variables may also be retrieved from other contents of the L2 block, such as the header.
...
...
@@ -89,6 +103,10 @@ The contained log events are filtered and processed as follows:
-
type
`1`
:
`overhead`
and
`scalar`
overwrite, as two packed
`uint256`
entries.
-
type
`2`
:
`gasLimit`
overwrite, as
`uint64`
payload.
-
type
`3`
:
`unsafeBlockSigner`
overwrite, as
`address`
payload.
-
type
`4`
:
`l1BasefeeScalar`
and
`l1BlobBasefeeScalar`
overwrite, as two packed
`uint32`
entries in
[
`abi.encodePacked()`
][
encodePacked
]
format.
[
encodePacked
]:
https://docs.soliditylang.org/en/latest/abi-spec.html#non-standard-packed-mode
Note that individual derivation stages may be processing different L1 blocks,
and should thus maintain individual system configuration copies,
...
...
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