Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TIP-52: Multi Address #152

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cfbe787
Adapt TIP-47 from TIP-19
PhilippGackstatter May 12, 2023
4f67068
Merge pull request #12 from iotaledger/tip47
muXxer May 12, 2023
54d3faa
Replace TIP-19, add TIP-47 in Readme
PhilippGackstatter May 16, 2023
4b8ecf8
Fix concept typo
PhilippGackstatter Aug 14, 2023
21fc627
Fix sparsity typo
PhilippGackstatter Sep 26, 2023
aee2b55
Add TIP-52
PhilippGackstatter Oct 3, 2023
376ee4e
Fix invalid YAML
PhilippGackstatter Oct 3, 2023
fbd5565
Simplify validation rules
PhilippGackstatter Oct 3, 2023
93ba482
Remove stray validation rule
PhilippGackstatter Oct 3, 2023
c8ebfce
Address review comments
PhilippGackstatter Oct 4, 2023
11711ef
Remove outdated TIP-47
PhilippGackstatter Oct 17, 2023
4174339
Revert changes to README and TIP-19
PhilippGackstatter Oct 17, 2023
8143114
Merge pull request #21 from iotaledger/chore/replace-tip19
PhilippGackstatter Oct 17, 2023
ea5c56a
Add bech32 repr table
PhilippGackstatter Oct 23, 2023
7ecfa8b
Remove manually added Table of Contents
PhilippGackstatter Oct 24, 2023
6c3be63
Merge remote-tracking branch 'tips-draft/main' into tip52
PhilippGackstatter Oct 24, 2023
43f9c2b
Update type values of unlocks and addresses
PhilippGackstatter Nov 3, 2023
2ef6824
Update Multi Address test vector
PhilippGackstatter Nov 3, 2023
1250787
Update tips/TIP-0052/tip-0052.md
PhilippGackstatter Nov 6, 2023
ead8a96
Add Anchor Address to allowed addresses
PhilippGackstatter Nov 6, 2023
52bf351
Fix incorrect bech32 encoding of byte 40
PhilippGackstatter Nov 27, 2023
a8e6e04
Increase address and unlock count to `2`
PhilippGackstatter Dec 19, 2023
5b1d8f4
Explicitly state global uniqueness of Sig Unlock
PhilippGackstatter Mar 1, 2024
f0e696c
Correct ref unlock validity in multi to syntactic
PhilippGackstatter Mar 1, 2024
8aaffcd
Add multi unlock tx validity
PhilippGackstatter Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
354 changes: 354 additions & 0 deletions tips/TIP-0052/tip-0052.md
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 ) <[email protected]>, Max Hase (@muXxer) <[email protected]>, Philipp Gackstatter
(@PhilippGackstatter) <[email protected]>
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>
PhilippGackstatter marked this conversation as resolved.
Show resolved Hide resolved
<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
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Author

@PhilippGackstatter PhilippGackstatter Nov 10, 2023

Choose a reason for hiding this comment

The 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/).
Loading