-
Notifications
You must be signed in to change notification settings - Fork 52
TIP-52: Multi Address #152
base: main
Are you sure you want to change the base?
Changes from all commits
cfbe787
4f67068
54d3faa
4b8ecf8
21fc627
aee2b55
376ee4e
fbd5565
93ba482
c8ebfce
11711ef
4174339
8143114
ea5c56a
7ecfa8b
6c3be63
43f9c2b
2ef6824
1250787
ead8a96
52bf351
a8e6e04
5b1d8f4
f0e696c
8aaffcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,354 @@ | ||
--- | ||
tip: 52 | ||
title: Multi Address | ||
description: Defines an Address that is owned by multiple other addresses. | ||
author: | ||
Eike Haß (@eike-hass ) <eike.hass@iota.org>, Max Hase (@muXxer) <maximilian.hase@iota.org>, Philipp Gackstatter | ||
(@PhilippGackstatter) <philipp.gackstatter@iota.org> | ||
discussions-to: TODO | ||
status: Draft | ||
type: Standards | ||
layer: Core | ||
created: 2023-09-07 | ||
--- | ||
|
||
# Summary | ||
|
||
This TIP proposes a new Address type to allow multiple addresses to create complex multi-signature-like unlock | ||
conditions for outputs. The address is considered unlocked as soon as the cumulative weight of all unlocked contained | ||
addresses matches or exceeds the configured threshold, where each of the addresses has an assigned weight. | ||
|
||
# Motivation | ||
|
||
The current outputs in Stardust can be unlocked by single addresses, which does not allow outputs to be unlocked by | ||
multiple different entities, except with Ed25519 Threshold Signatures. The latter allows to model multi-control | ||
structures, but it is not transparent who the underlying controlling addresses are and in particular, they do not allow | ||
different Account or NFT Addresses to control an output. The _Multi Address_ introduced in this TIP allows for any other | ||
address type to control an output in a multi-control structure, in various configurations. The W3C DID Specification | ||
distinguishes between [independent control](https://www.w3.org/TR/did-core/#independent-control) and | ||
[group control](https://www.w3.org/TR/did-core/#group-control) which is a useful definition in this context. With the | ||
current approach only independent control can be modelled. Allowing for group control by multiple inspectable | ||
controlling entities might be useful in the following scenarios: | ||
|
||
- Modelling multiple W3C DID controllers. | ||
- Transparent modelling of ownership over funds or assets of entities like organizations. | ||
- Transparent modelling of committees for anchoring (e.g. L2) | ||
|
||
A _Multi Address_ allows for use-cases like: | ||
|
||
- Requiring a signature by a hardware security module (HSM) and a signature by one of two trusted devices through | ||
software to unlock. | ||
- Requiring signatures of a supermajority of controlling parties each of which are an Account to unlock. | ||
- Modelling a 1 of n control structure in addition to a single backup address that has full control. | ||
- Allowing interested parties to inspect the conditions for unlocking, e.g. majority or supermajority. | ||
|
||
# Multi Address | ||
|
||
The following table shows the mapping from the address type of the **first byte** to the address type: | ||
|
||
| Address | Type Byte as `uint8` | Bech32 Encoded | | ||
| ------------- | -------------------- | -------------- | | ||
| Multi Address | 40 | iota1**9**... | | ||
|
||
The following table shows the serialization of a _Multi Address_: | ||
|
||
<details> | ||
<summary>Multi Address</summary> | ||
<blockquote>Defines a Multi Address that consists of addresses with weights and a threshold value. The Multi Address can be unlocked if the cumulative weight of all unlocked addresses is equal to or exceeds the threshold.</blockquote> | ||
</details> | ||
<table> | ||
<tr> | ||
<td> | ||
<b>Name</b> | ||
</td> | ||
<td> | ||
<b>Type</b> | ||
</td> | ||
<td> | ||
<b>Description</b> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Address Type</td> | ||
<td>uint8</td> | ||
<td>Set to <strong>value 40</strong> to denote a <i>Multi Address</i>.</td> | ||
</tr> | ||
<tr> | ||
<td>Addresses Count</td> | ||
<td>uint8</td> | ||
<td>The number of addresses following.</td> | ||
</tr> | ||
<tr> | ||
<td valign="top">Addresses <code>anyOf</code></td> | ||
<td colspan="2"> | ||
<details> | ||
<summary>Weighted Address</summary> | ||
<blockquote>An Address with an assigned weight.</blockquote> | ||
<table> | ||
<tr> | ||
<td> | ||
<b>Name</b> | ||
</td> | ||
<td> | ||
<b>Type</b> | ||
</td> | ||
<td> | ||
<b>Description</b> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td valign="top">Address <code>oneOf</code></td> | ||
<td colspan="2"> | ||
<details> | ||
<summary>Ed25519 Address</summary> | ||
<blockquote>An Address derived from an Ed25519 Public Key. Defined in <a href='../TIP-0038/tip-0038.md#ed25519-address'>TIP-38 (Ed25519 Address)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>Account Address</summary> | ||
<blockquote>An Address derived from an Account ID which can be unlocked by unlocking the corresponding Account. Defined in <a href='../TIP-0038/tip-0038.md#account-address'>TIP-38 (Account Address)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>NFT Address</summary> | ||
<blockquote>An Address derived from an NFT ID which can be unlocked by unlocking the corresponding NFT. Defined in <a href='../TIP-0038/tip-0038.md#nft-address'>TIP-38 (NFT Address)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>Anchor Address</summary> | ||
<blockquote>An Address derived from an Anchor ID which can be unlocked by unlocking the corresponding Anchor. Defined in <a href='../TIP-0038/tip-0038.md#anchor-address'>TIP-38 (Anchor Address)</a>.</blockquote> | ||
</details> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Weight</td> | ||
<td>uint8</td> | ||
<td>The weight of the unlocked address.</td> | ||
</tr> | ||
</table> | ||
</details> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Threshold</td> | ||
<td>uint16</td> | ||
<td>The threshold that needs to be reached by the unlocked addresses in order to unlock the Multi Address.</td> | ||
</tr> | ||
</table> | ||
|
||
A _Multi Address_ is a container for other addresses. Each address has weight and the Multi Address contains a threshold | ||
`Threshold` that needs to be reached by the unlocked addresses in order to unlock the Multi Address. | ||
|
||
## Additional syntactic transaction validation rules | ||
|
||
A Multi Address is only valid if all of the following conditions hold: | ||
|
||
- `2 <= Addresses Count <= 10`. | ||
- The addresses must be lexically ordered and unique based on the contained serialized address. | ||
- `Weight >= 1`, for each `Weight` of contained `Addresses`. | ||
- `Cumulative Weight >= Threshold`, where `Cumulative Weight` is the sum of weight of all `Weight`s in `Addresses`. | ||
- `Threshold >= 1`. | ||
|
||
## Unlocks | ||
|
||
### Multi Unlock | ||
|
||
<details> | ||
<summary>Multi Unlock</summary> | ||
<blockquote>Unlocks a Multi Address with a list of other unlocks.</blockquote> | ||
</details> | ||
<table> | ||
<tr> | ||
<td> | ||
<b>Name</b> | ||
</td> | ||
<td> | ||
<b>Type</b> | ||
</td> | ||
<td> | ||
<b>Description</b> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Unlock Type</td> | ||
<td>uint8</td> | ||
<td>Set to <strong>value 5</strong> to denote a <i>Multi Unlock</i>.</td> | ||
</tr> | ||
<tr> | ||
<td>Unlocks Count</td> | ||
<td>uint8</td> | ||
<td>The number of unlocks following.</td> | ||
</tr> | ||
<tr> | ||
<td valign="top">Unlocks <code>anyOf</code></td> | ||
<td colspan="2"> | ||
<details> | ||
<summary>Signature Unlock</summary> | ||
<blockquote>Unlocks the address derived from the contained Public Key in the transaction in which it is contained in. Defined in <a href='../TIP-0045/tip-0045.md#signature-unlock'>TIP-45 (Signature Unlock)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>Reference Unlock</summary> | ||
<blockquote>References a previous unlock to support unlocking multiple inputs owned by the same address. Defined in <a href='../TIP-0045/tip-0045.md#reference-unlock'>TIP-45 (Reference Unlock)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>Account Unlock</summary> | ||
<blockquote>Points to the unlock of a consumed Account Output. Defined in <a href='../TIP-0042/tip-0042.md#account-unlock'>TIP-42 (Account Unlock)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>Anchor Unlock</summary> | ||
<blockquote>Points to the unlock of a consumed Anchor Output. Defined in <a href='../TIP-0054/tip-0054.md#anchor-unlock'>TIP-54 (Anchor Unlock)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>NFT Unlock</summary> | ||
<blockquote>Points to the unlock of a consumed NFT Output. Defined in <a href='../TIP-0043/tip-0043.md#nft-unlock'>TIP-43 (NFT Unlock)</a>.</blockquote> | ||
</details> | ||
<details> | ||
<summary>Empty Unlock</summary> | ||
<blockquote>Used to maintain correct index relationship between addresses and signatures when unlocking a Multi Address where not all addresses are unlocked. Defined in <a href='../TIP-0052/tip-0052.md#empty-unlock'>TIP-52 (Empty Unlock)</a>.</blockquote> | ||
</details> | ||
</td> | ||
</tr> | ||
</table> | ||
|
||
#### Additional syntactic transaction validation rules | ||
|
||
A Multi Unlock is only valid if all of the following conditions hold: | ||
|
||
- `2 <= Unlocks Count <= 10`. | ||
|
||
A transaction containing a Multi Unlock is only valid if all of the following conditions hold: | ||
|
||
- If the transaction's `Unlocks` field contains Reference, Account, Anchor or NFT Unlocks, they must point to an Unlock | ||
outside of the Multi Unlock. | ||
- The _Multi Unlock_ must be unique within the subset of _Multi Unlocks_ in the `Unlocks` field of the transaction based | ||
on the serialization of the unlock. | ||
- Any _Signer UIDs_ from _Signature Unlocks_ present in a _Multi Unlock_ are not also present outside a multi unlock. | ||
- Note that this implies that duplicate _Signature Unlocks_ are allowed to be present within multiple distinct Multi | ||
Unlocks. | ||
|
||
#### Additional semantic transaction validation rules | ||
|
||
A transaction containing a Multi Unlock is only valid if all of the following conditions hold: | ||
|
||
- The corresponding Address of the to-be-unlocked input is of type _Multi Address_. | ||
- The `Addresses Count` of the Multi Address and the `Unlocks Count` of the Multi Unlock must match. | ||
- Let the `Cumulative Unlocked Weight` be the sum of weights of the addresses that were successfully unlocked, through | ||
Unlocks other than _Empty Unlocks_, which do not add to this sum. | ||
- `Cumulative Unlocked Weight >= Threshold`. | ||
|
||
### Empty Unlock | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it be a syntactic rule that it can't be used outside of a MultiUnlock or are we fine waiting semantic and see that address/unlock is not a good match? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The schemas tell you where the Empty Unlock is allowed - only in Multi Unlocks. It's somewhat similar for implicit account creation addresses. They are also not allowed in Address Unlock Conditions within NFT Outputs, but are allowed in Basic Outputs. Neither are they allowed in Expiration Unlock Conditions. Those rules are not defined explicitly (but can be derived from TIP-42 when being creative), but really the rule is encoded in the schema of the Expiration UC. Since we already have the schemas and they are much easier to maintain in the TIPs I would like to avoid duplicating those rules in text were it cannot be automatically updated. |
||
|
||
The indices of _Unlocks_ and `Addresses` in a Multi Address must match when unlocking it since it may be configured to | ||
be unlocked by less than the number of addresses it contains. In such cases, the Empty Unlock needs to be used to | ||
maintain correct index relationship between unlocks and addresses in a _Multi Unlock_, to omit the unlocks of the | ||
addresses that are not unlocked. | ||
|
||
For example, consider a Multi Address with 3 addresses with weight 1 and a threshold of 1. The Multi Address can be | ||
unlocked by providing a Multi Unlock with a Signature Unlock of the first address and two subsequent Empty Unlocks. | ||
|
||
<details> | ||
<summary>Empty Unlock</summary> | ||
<blockquote>Used to maintain correct index relationship between addresses and signatures when unlocking a Multi Address where not all addresses are unlocked.</blockquote> | ||
</details> | ||
<table> | ||
<tr> | ||
<td> | ||
<b>Name</b> | ||
</td> | ||
<td> | ||
<b>Type</b> | ||
</td> | ||
<td> | ||
<b>Description</b> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Unlock Type</td> | ||
<td>uint8</td> | ||
<td>Set to <strong>value 6</strong> to denote an <i>Empty Unlock</i>.</td> | ||
</tr> | ||
</table> | ||
|
||
## Bech32 Representation | ||
|
||
To allow querying the outputs owned by a Multi Address, its Bech32 representation is used, which is computed as follows: | ||
|
||
- Let `Address Hash` be the BLAKE2b-256 hash of the serialized Multi Address. | ||
- Bech32-encode the concatenation of the Multi Address type prefix and the `Address Hash`. | ||
|
||
# Rationale & Alternatives | ||
|
||
Multi Addresses make transparent which other entities control an entity, since they are specified in the address itself. | ||
For a Digital Identity this allows for inspection of which other identities control it and expresses the relationship of | ||
L1 entities. Additionally Multi Addresses enhance the security and recoverability of Accounts by adding support for | ||
multi-factor authentication (e.g. two addresses with weight 1 and a threshold of 2) or backup addresses (e.g. same as | ||
before plus an address with weight 2 whose private key is in cold storage) for outputs on L1. | ||
|
||
**Alternatives** | ||
|
||
- One alternative would be to have two sets of addresses, where one lists the mandatory and one the optional addresses, | ||
the latter of which contains weights and a threshold. While this allows for similar use cases, it is harder to | ||
validate correctness while not being more expressive. | ||
- Rather than introducing Multi Address as an additional type, it would be possible to introduce it as the only type of | ||
address, as it could express all other existing types. However, that exposes every protocol user to the complexity of | ||
Multi Addresses, which may not be desirable. Introducing it as a dedicated type confines the complexity to a smaller | ||
part of the user-facing surface, while still allowing for the same functionality as if it was the only top-level | ||
address type. | ||
- Another alternative is to simplify towards k of n use cases, avoiding the complexity of weights. This excludes some | ||
use cases and a weight-based approach is not significantly more complex than a k of n only approach, which can already | ||
be achieved with Ed25519 Threshold signatures. | ||
|
||
# Test Vectors | ||
|
||
## Bech32 | ||
|
||
```json | ||
{ | ||
"type": 40, | ||
"addresses": [ | ||
{ | ||
"address": { | ||
"type": 0, | ||
"pubKeyHash": "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" | ||
}, | ||
"weight": 1 | ||
}, | ||
{ | ||
"address": { | ||
"type": 0, | ||
"pubKeyHash": "0x53fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" | ||
}, | ||
"weight": 1 | ||
}, | ||
{ | ||
"address": { | ||
"type": 0, | ||
"pubKeyHash": "0x54fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" | ||
}, | ||
"weight": 1 | ||
}, | ||
{ | ||
"address": { | ||
"type": 8, | ||
"accountId": "0x55fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" | ||
}, | ||
"weight": 2 | ||
}, | ||
{ | ||
"address": { | ||
"type": 16, | ||
"nftId": "0x56fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" | ||
}, | ||
"weight": 3 | ||
} | ||
], | ||
"threshold": 2 | ||
} | ||
``` | ||
|
||
The resulting Bech32 representation must be: | ||
|
||
``` | ||
iota19qq0ezu97zl76wqnpdxxleuf55gk0eqhscjtdgqm5sqwav6gcarz6vvesnk | ||
``` | ||
|
||
# Copyright | ||
|
||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
Uh oh!
There was an error while loading. Please reload this page.