Skip to content

Commit 3d27786

Browse files
Add support for asset fingerprint (CIP14) (#294)
* Added extended signing key support for cip8 * Fixed unused imports, flake8 checks pass. * Fixed mypy error for overloaded variable * Remove extraneous parameter for verify * Added ByteString to _restored_typed_primitive * Added type checking * Added support for CIP 14 * Added support for ScriptHash and AssetName * Add import to cips and docs --------- Co-authored-by: Niels Mündler <[email protected]>
1 parent 8cbf815 commit 3d27786

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed

docs/source/api/pycardano.cip.rst

+5
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ Implementation of Cardano Improvement Proposals (CIPs)
77
:members:
88
:undoc-members:
99
:show-inheritance:
10+
11+
.. automodule:: pycardano.cip.cip14
12+
:members:
13+
:undoc-members:
14+
:show-inheritance:

pycardano/cip/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# flake8: noqa
22

33
from .cip8 import *
4+
from .cip14 import *

pycardano/cip/cip14.py

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from typing import Union
2+
3+
from nacl.encoding import RawEncoder
4+
from nacl.hash import blake2b
5+
from pycardano.crypto.bech32 import encode
6+
from pycardano.hash import ScriptHash
7+
from pycardano.transaction import AssetName
8+
9+
10+
def encode_asset(
11+
policy_id: Union[ScriptHash, bytes, str], asset_name: Union[AssetName, bytes, str]
12+
) -> str:
13+
"""Implementation of CIP14 asset fingerprinting
14+
15+
This function encodes the asset policy and name into an asset fingerprint, which is
16+
bech32 compliant.
17+
18+
For more information:
19+
https://developers.cardano.org/docs/governance/cardano-improvement-proposals/cip-0014/
20+
21+
Args:
22+
policy_id: The asset policy as `ScriptHash`, `bytes`, or a hex `str`
23+
asset_name: The asset name as `AssetName`, `bytes`, or a hex `str`
24+
"""
25+
if isinstance(policy_id, str):
26+
policy_id = bytes.fromhex(policy_id)
27+
elif isinstance(policy_id, ScriptHash):
28+
policy_id = policy_id.payload
29+
30+
if isinstance(asset_name, str):
31+
asset_name = bytes.fromhex(asset_name)
32+
elif isinstance(asset_name, AssetName):
33+
asset_name = asset_name.payload
34+
35+
asset_hash = blake2b(
36+
policy_id + asset_name,
37+
digest_size=20,
38+
encoder=RawEncoder,
39+
)
40+
41+
return encode("asset", asset_hash)

test/pycardano/test_cip14.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import pytest
2+
3+
from pycardano.cip.cip14 import encode_asset
4+
from pycardano.hash import ScriptHash
5+
from pycardano.transaction import AssetName
6+
7+
8+
@pytest.mark.parametrize(
9+
"input_types", [(str, str), (bytes, bytes), (ScriptHash, AssetName)]
10+
)
11+
@pytest.mark.parametrize(
12+
"asset",
13+
[
14+
{
15+
"policy_id": "7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373",
16+
"asset_name": "",
17+
"asset_fingerprint": "asset1rjklcrnsdzqp65wjgrg55sy9723kw09mlgvlc3",
18+
},
19+
{
20+
"policy_id": "7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc37e",
21+
"asset_name": "",
22+
"asset_fingerprint": "asset1nl0puwxmhas8fawxp8nx4e2q3wekg969n2auw3",
23+
},
24+
{
25+
"policy_id": "1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209",
26+
"asset_name": "",
27+
"asset_fingerprint": "asset1uyuxku60yqe57nusqzjx38aan3f2wq6s93f6ea",
28+
},
29+
{
30+
"policy_id": "7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373",
31+
"asset_name": "504154415445",
32+
"asset_fingerprint": "asset13n25uv0yaf5kus35fm2k86cqy60z58d9xmde92",
33+
},
34+
{
35+
"policy_id": "1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209",
36+
"asset_name": "504154415445",
37+
"asset_fingerprint": "asset1hv4p5tv2a837mzqrst04d0dcptdjmluqvdx9k3",
38+
},
39+
{
40+
"policy_id": "1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209",
41+
"asset_name": "7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373",
42+
"asset_fingerprint": "asset1aqrdypg669jgazruv5ah07nuyqe0wxjhe2el6f",
43+
},
44+
{
45+
"policy_id": "7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373",
46+
"asset_name": "1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209",
47+
"asset_fingerprint": "asset17jd78wukhtrnmjh3fngzasxm8rck0l2r4hhyyt",
48+
},
49+
{
50+
"policy_id": "7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373",
51+
"asset_name": "0000000000000000000000000000000000000000000000000000000000000000",
52+
"asset_fingerprint": "asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w",
53+
},
54+
],
55+
)
56+
def test_encode_asset(asset, input_types):
57+
if isinstance(input_types[0], bytes):
58+
policy_id = bytes.fromhex(asset["policy_id"])
59+
asset_name = bytes.fromhex(asset["asset_name"])
60+
elif isinstance(input_types[0], str):
61+
policy_id = asset["policy_id"]
62+
asset_name = asset["asset_name"]
63+
64+
if isinstance(input_types[0], ScriptHash):
65+
policy_id = ScriptHash(policy_id)
66+
asset_name = AssetName(asset_name)
67+
68+
fingerprint = encode_asset(
69+
policy_id=asset["policy_id"], asset_name=asset["asset_name"]
70+
)
71+
72+
assert fingerprint == asset["asset_fingerprint"]

0 commit comments

Comments
 (0)