tip | title | description | author | discussions-to | status | type | layer | created |
---|---|---|---|---|---|---|---|---|
17 |
Wotsicide |
Define migration from legacy WOTS addresses to post-Chrysalis Phase 2 network |
Luca Moser (@luca-moser) <[email protected]> |
Active |
Standards |
Core |
2021-01-13 |
This RFC defines the migration process of funds residing on WOTS addresses in the current legacy network to the post-Chrysalis Phase 2 network.
The IOTA protocol wants to move away from WOTS as it created a number of security, protocol and UX issues:
- WOTS signatures are big and make up a disproportional amount of data of a transaction.
- It is only safe to spend from an address once. Spending multiple times from the same address reveals random parts of the private key, making any subsequent transfers (other than the first) susceptible to thefts.
- As a prevention mechanism to stop users from spending multiple times from the same address, nodes have to keep an ever growing list of those addresses.
In the beginning of the new Chrysalis Phase 2 network, only Ed25519 addresses are supported. The protocol will no longer support WOTS addresses. Therefore, there needs to be a migration process from WOTS addresses to Ed25519 address in the new network.
To make the migration as smooth as possible, the specified mechanism allows for users to migrate their funds at any time with only a small delay until they're available on the new network.
This RFC outlines the detailed architecture of how users will be able to migrate their funds and specifies the underlying components and their purposes.
On a high-level the migration process works as follows:
- Users create migration bundles in the legacy network which target their Ed25519 address in the new network.
- The Coordinator then mints those migrated funds in receipts which are placed within milestones on the new network.
- Nodes in the new network evaluate receipts and book the corresponding funds by creating new UTXOs in the ledger.
- Users issue migration bundles which effectively burn their funds. During this period, normal value bundles and zero-value transactions are allowed to become part of a milestone cone.
- The Coordinator is stopped, and a new global snapshot is created which as its only solid entry point contains the last issued milestone (and put on dbfiles.iota.org). This global snapshot is used to create the genesis snapshot containing the already migrated funds for the new network. The remainder of the total supply which has not been migrated is allocated on the
TreasuryOutput
. Users are instructed to check the validity of these two snapshots. - A new Hornet version is released which only allows migration bundles to be broadcasted or be part of milestone cones. Users must update their node software as otherwise they will no longer peer.
- The legacy network is restarted with the global snapshot, and the new network bootstraps with the genesis snapshot.
- Further funds migrated in the legacy network are transferred to the new network using the receipt mechanism.
In order to facilitate the migration process, the node software making up the legacy network needs to be updated. This update will be deployed by stopping the Coordinator and forcing all nodes to upgrade to this new version.
The node software will no longer book ledger mutations to non-migration addresses. This means that users are incentivized to migrate their funds as they want to use their tokens. See this document on what migration addresses are.
A migration bundle is defined as follows:
- It contains exactly one output transaction of which the destination address is a valid migration address and is positioned as the tail transaction within the bundle. The output transaction value is at least 1'000'000 tokens.
- It does not contain any zero-value transactions which do not hold signature fragments. This means that transactions other than the tail transaction must always be part of an input.
- Input transactions must not use migration addresses.
The node will only use tail transactions of migration or milestone bundles for the tip-pool. This means that past cones referenced by a milestone will only include such bundles.
The legacy node software is updated with an additional HTTP API command called getWhiteFlagConfirmation
which given request data in the following form:
{
"command": "getWhiteFlagConfirmation",
"milestoneIndex": 1434593
}
returns data for the given milestone white-flag confirmation:
{
"milestoneBundle": [
"SDGKWKJAG...",
"WNGHJWIFA...",
"DSIEWSDIG..."
],
"includedBundles": [
[
"SKRGI9DFS...",
"NBJSKRJGW...",
"ITRUQORTZ..."
],
[
"OTIDFJKSD...",
"BNSUGRWER...",
"OPRGJSDFJ..."
],
...
]
}
where milestoneBundle
contains the milestone bundle trytes and includedBundles
is an array of tryte arrays of included bundles in the same DFS order as the white-flag confirmation. Trytes within a bundle "array" are sorted from currentIndex
= 0 ascending to the lastIndex
.
This HTTP API command allows interested parties to verify which migration bundles were confirmed by a given milestone.
The Coordinator will only include migration bundles (respectively the tails of those bundles) in its inclusion Merkle proof. Nodes which do not run with the updated code will crash once the updated confirmation is in place.
As an additional measure to prevent users from submitting never confirming non-migration bundles (which would lead to key-reuse), nodes will no longer accept non-migration bundles in the HTTP API.
HTTP API level checks:
- The user must submit an entire migration bundle. No more single zero-value transactions, value-spam bundles etc. are allowed.
- Input transactions are spending the entirety of the funds residing on the corresponding address. There must be more than 0 tokens on the given address.
Wallet software must be updated to no longer support non-migration bundles.
There are no restrictions put in place on the gossip level, as it is too complex to prevent non-migration transactions to be filtered out, however, these transactions will never become part of a milestone cone.
A TreasuryTransaction
is a payload which contains a reference to the current TreasuryOutput
(in form of a TreasuryInput
object) and an output TreasuryOutput
which deposits the remainder.
Serialized form:
Name | Type | Description | |||||||||
Type | uint32 | Set to value 4 to denote a TreasuryTransaction. | |||||||||
Input |
|
||||||||||
Output |
|
The TreasuryInput
is equivalent to a normal UTXOInput
but instead of referencing a transaction, it references a milestone. This input can only be used within TreasuryTransaction
payloads.
Serialized form:
Name | Type | Description |
Input Type | byte | Set to value 1 to denote an TreasuryInput. |
Milestone Hash | Array<byte>[32]> | The hash of the milestone which created the referenced TreasuryOutput. |
The TreasuryOutput
is a special output type which represents the treasury of the network, respectively the non yet migrated funds. At any given moment in time, there is only one TreasuryOutput
.
Serialized form:
Name | Type | Description |
Output Type | byte | Set to value 2 to denote an TreasuryInput. |
Amount | uint64 | The amount of funds residing in the treasury. |
The TreasuryOutput
can not be referenced or spent by transactions, it can only be referenced by receipts.
The TreasuryOutput
can be queried from the HTTP API and needs to be included within snapshots in order to keep the total supply intact.
Receipts allow for fast migration of funds from the legacy into the new network by representing entries of funds which were migrated in the old network.
Receipts are listings of funds for which nodes must generate UTXOs in the form of SigLockedSingleOutput
s targeting the given address. Receipts are embedded within milestone payloads and therefore signed by the Coordinator. A milestone may contain up to one receipt as a payload. The Coordinator chooses whether to embed a receipt payload or not.
Serialized form:
Name | Type | Description | |||||||||||||||||||||
Payload Type | uint32 | Set to value 3 to denote a Receipt. | |||||||||||||||||||||
Migrated At | uint32 | The index of the legacy milestone in which the listed funds were migrated at. | |||||||||||||||||||||
Final | bool | Flags whether this receipt is the last receipt for the given Migrated At index. | |||||||||||||||||||||
Funds Count | uint16 | Denotes how many migrated fund entries are within the receipt. | |||||||||||||||||||||
Funds |
Migrated Funds Entry
|
||||||||||||||||||||||
Payload Length | uint32 | The length in bytes of the payload. | |||||||||||||||||||||
Payload |
TreasuryTransaction Payload |
funds_array_count
can be max 127 and must be > 0.funds
array must be in lexical sort order (by their serialized form).- any
tail_transaction_hash
must be unique over the entire receipt. deposit
must be ≥ 1'000'000 IOTA tokens.payload_length
can not be zero.treasury_transaction
must be aTreasury Transaction
payload.
migrated_at
can not decrease between subsequent receipts.- There must not be any subsequent receipts with the same
migrated_at
index after the one with thefinal
flag set to true. - The amount field of the previous
TreasuryOutput
minus the sum of all the newly migrated funds must equal the amount of the newTreasuryOutput
within theTreasuryTransaction
payload.
While the syntactical and semantic validation ensures that the receipt's integrity is correct, it doesn't actually tell whether the given funds were really migrated in the legacy network.
In order validate this criteria, the node software performs following operation:
- The HTTP API of a legacy node is queried for all the
tail_transaction_hashes
, the addresses and their corresponding migrated funds. - The node checks whether the funds within the receipt matches the response from the legacy node.
- Additionally, if the receipt's
final
flag was set to true, it is validated whether all funds for the given legacy milestone were migrated by looking at all the receipts with the samemigrated_at
index.
If the operation fails, the node software must gracefully terminate with an appropriate error message.
In an optimal setting, node operators choose to only ask their own deployed nodes in the legacy network.
After successful receipt validation, the node software generates UTXOs in the following form: A SigLockedSingleOutput
is allocated with the given ed25519_address
and the funds as the deposit. As there is no actual transaction which generates the UTXO and normally a UTXO ID consists of transaction hash | the output index
, the milestone hash of the milestone which included the receipt with the given funds is used as the transaction hash
. The output index
equals the index of the funds within the receipt (this is also why the receipt is limited to 127 entries). This allows to easily look up in which milestone these funds were generated in.
If one wants to audit the UTXO path of an input, it means that milestones need to be kept forever as they're needed to recognize that a certain output was generated by it. However, this can be offloaded to a 2nd level service.
All the generated SigLockedSingleOutput
s from the migrated funds are then booked into the ledger and the new TreasuryOutput
is persisted as a UTXO using the milestone hash of the receipt which included the Treasury Transaction
payload.
For transparency reasons, the IF offers software which renders a dashboard showing details throughout the entire migration process:
- A list of outstanding funds residing on migration addresses to be migrated with the milestone index at which they were created.
- Migrated funds.
- Generated receipts.
At current legacy network ledger size of 261446 entries (addresses with ≥ 1'000'000 tokens), it would take min. ~2058 receipts to migrate all funds. While theoretically the max message size allows for more entries to be within one receipt, it is limited by the fact that the index of the migrated address within the receipt is used to generate the output_index
of the generated SigLockedSingleDeposit
(further explained below).
Assuming the best case scenario in which all 261446 entries were sent to migration addresses in the legacy network, these funds could therefore be migrated into the new network within ~5.7h (at a 10 second milestone interval). Of course, in practice users will migrate over time and the receipt mechanism will need to be in place as long as the new network runs.
If looked at the receipt validation from a higher-level, it becomes quite apparent that this is analogous to previous global snapshots where users would post comments on a PR on GitHub saying that they computed the same ledger state, just that it is more granular and automatic while still leveraging the same source of truth: the ledger state/database of nodes.
- The local snapshot file format needs to also include the to be applied receipts and supply information.
Copyright and related rights waived via CC0.