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

CIP-0014: User-facing Asset Fingerprint #64

Merged
merged 5 commits into from
Mar 26, 2021
Merged
Changes from all commits
Commits
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
169 changes: 169 additions & 0 deletions CIP-0014/CIP-0014.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
CIP: 14
Title: User-Facing Asset Fingerprint
Authors: Matthias Benkort <matthias.benkort@iohk.io>, Rodney Lorrimar <rodney.lorrimar@iohk.io>
Comments-Summary: A common format for displaying a user-facing identifier for assets.
Comments-URI: https://github.com/cardano-foundation/CIPs/wiki/Comments:CIP-0014
Status: Draft
Type: Standards
Created: 2020-02-01
License: CC-BY-4.0
---

# Abstract

This specification defines a user-facing asset fingerprint as a bech32-encoded blake2b-160 digest of the concatenation of the policy id and the asset name.

# Motivation

The Mary era of Cardano introduces the support for native assets. On the blockchain, native assets are uniquely identified by both their so-called policy id and asset name. Neither the policy id nor the asset name are intended to be human-readable data.

On the one hand, the policy id is a hash digest of either a monetary script or a Plutus script. On the other hand, the asset name is an arbitrary bytestring of up to 32 bytes (which does not necessarily decode to a valid UTF-8 sequence). In addition, it is possible for an asset to have an empty asset name, or, for assets to have identical asset names under different policies.

Because assets are manipulated in several user-facing features on desktop and via hardware applications, it is useful to come up with a short(er) and human-readable identifier for assets that user can recognize and refer to when talking about assets. We call such an identifier an _asset fingerprint_.

# Specification

We define the asset fingerprint in pseudo-code as:

```
assetFingerprint := encodeBech32
( datapart = hash
( algorithm = 'blake2b'
, digest-length = 20
Copy link
Contributor

@refi93 refi93 Feb 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worse-case is the Ledger Nano S which can show up to 20 characters. Although, users can scroll in theory, we know that in practice many won't.

@KtorZ To make it clear - even though the hash digest was set to 20 bytes, the current bech32 assetId length exceeds Ledger's screen capacity of 20 characters (it has the "asset1" prefix, checksum at the end and one bech32 character is not the same as 1 byte, obviously) - so users will indeed have to scroll on Ledger to see it fully.

However, given that we chose to go with a hash of the policyId and assetName concatenation, even the first screenful of the id should be a reasonable assurance that given assetId is correct, though it's definitely recommendable to scroll through the whole assetId to be completely sure

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

he current bech32 assetId length exceeds Ledger's screen capacity of 20 characters

I know :( .. bech32 isn't the "smallest" encoding possible, base64 with no padding would produce shorter strings but they'd also be too long. My hope is that this is short-enough to still allow users to check them, if not fully, at least as much as possible from what they get on the first screen 😬

, message = policyId | assetName
)
, humanReadablePart = 'asset'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about more descriptive such as
humanReadablePart = 'fngrprnt'

Copy link
Member Author

@KtorZ KtorZ Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Referring to it as fingerprint would actually be less descriptive 😶 There are many things that can be a fingerprint of something. Yet here, it is used to referred to an asset, hence the prefix. In the same way we don't use hash as a prefix for key hashes for instance, but we use: addr_vkh or stake_vkh 👍

)
```

where `|` designates the concatenation of two byte strings. The `digest-length` is given in _bytes_ (so, 160 bits).

# Rationale

## Design choices

- The asset fingerprint needs to be _somewhat unique_ (although collisions are plausible, see next section) and refer to a particular asset. It must therefore include both the policy id and the asset name.

- Using a hash gives us asset id of a same deterministic length which is short enough to display reasonably well on small screens.

- We use bech32 as a user-facing encoding since it is both user-friendly and quite common within the Cardano eco-system (e.g. addresses, pool ids, keys).

## Security Considerations

- With a 160-bit digest, an attacker needs at least 2^80 operations to find a collision. Although 2^80 operations is relatively low (it remains expansive but doable for an attacker), it
is considered safe within the context of an asset fingerprint as a mean of _user verification_ within a particular wallet. An attacker may obtain advantage if users can be persuaded
that a certain asset is in reality another (which implies to find a collision, and make both assets at the reach of the user).

- We recommend however that in addition to the asset fingerprint, applications also show whenever possible a visual checksum calculated from the policy id and the asset name as specified in [CIP-YET-TO-COME](). Such generated images, which are designed to be unique and easy to distinguish, in combination with a readable asset fingerprint gives strong verification means to end users.

# Backwards Compatibility

N/A

# Reference Implementation

## Haskell (GHC >= 8.6.5)

<details>
<summary>Language Extensions</summary>

```hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}
```
</details>

<details>
<summary>Imports</summary>

```hs
-- package: base >= 4.0.0
import Prelude
import Data.Function
( (&) )

-- package: bech32 >= 1.0.2
import qualified Codec.Binary.Bech32 as Bech32

-- package: bech32-th >= 1.0.2
import Codec.Binary.Bech32.TH
( humanReadablePart )

-- package: bytestring >= 0.10.0.0
import Data.ByteString
( ByteString )

-- package: cryptonite >= 0.22
import Crypto.Hash
( hash )
import Crypto.Hash.Algorithms
( Blake2b_160 )

-- package: memory >= 0.14
import Data.ByteArray
( convert )

-- package: text >= 1.0.0.0
import Data.Text
( Text )
```
</details>

```hs
newtype PolicyId = PolicyId ByteString
newtype AssetName = AssetName ByteString
newtype AssetFingerprint = AssetFingerprint Text

mkAssetFingerprint :: PolicyId -> AssetName -> AssetFingerprint
mkAssetFingerprint (PolicyId policyId) (AssetName assetName)
= (policyId <> assetName)
& convert . hash @_ @Blake2b_160
& Bech32.encodeLenient hrp . Bech32.dataPartFromBytes
& AssetFingerprint
where
hrp = [humanReadablePart|asset|]
```

## Test Vectors

> :information_source: `policy_id` and `asset_name` are hereby base16-encoded; their raw, decoded, versions should be used when computing the fingerprint.

```yaml
- policy_id: 7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373
asset_name: ""
asset_fingerprint: asset1rjklcrnsdzqp65wjgrg55sy9723kw09mlgvlc3

- policy_id: 7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc37e
asset_name: ""
asset_fingerprint: asset1nl0puwxmhas8fawxp8nx4e2q3wekg969n2auw3

- policy_id: 1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209
asset_name: ""
asset_fingerprint: asset1uyuxku60yqe57nusqzjx38aan3f2wq6s93f6ea

- policy_id: 7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373
asset_name: 504154415445
asset_fingerprint: asset13n25uv0yaf5kus35fm2k86cqy60z58d9xmde92

- policy_id: 1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209
asset_name: 504154415445
asset_fingerprint: asset1hv4p5tv2a837mzqrst04d0dcptdjmluqvdx9k3

- policy_id: 1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209
asset_name: 7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373
asset_fingerprint: asset1aqrdypg669jgazruv5ah07nuyqe0wxjhe2el6f

- policy_id: 7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373
asset_name: 1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209
asset_fingerprint: asset17jd78wukhtrnmjh3fngzasxm8rck0l2r4hhyyt

- policy_id: 7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373
asset_name: 0000000000000000000000000000000000000000000000000000000000000000
asset_fingerprint: asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w
```

# Copyright

CC-BY-4.0