<p>It seems I was close, but the str.encode() function was not the correct one to use.
Also a zero byte needs to be prepended to the data to be hashed. Thanks to user bwbush from the cardano forum for helping me out, couldn't have done this without his help!</p>
<p>Here's the final solution:</p>
<pre><code>import hashlib
from cbor2 import dumps, loads, shareable_encoder, CBORTag

class Sig:
        def __init__(self, hash, slot):
                self.hash=hash
                self.slot=slot


def default_encoder(encoder,value):
        encoder.encode([1, [[0, bytes.fromhex(value.hash)], [5, value.slot]]])


obj=Sig(&quot;37c19eca0d623d804bfeb8951bf1eb0f7fe193deeb71793985dbd25e&quot;, 32241869 + i)

serialized = bytes.fromhex(&quot;00&quot;) + dumps(obj, default=default_encoder, value_sharing=False)

m=hashlib.blake2b(digest_size=28)
m.update(serialized)

print(m.hexdigest())
</code></pre>

CC BY-SA 4.0
2021-05-18T08:43:39.680Z
2021-05-18T08:43:39.680Z
tycl
<p>As the title says, I want to to reimplement the hashing part of a policy script in Python 3.
I have already made a post over at <a href="https://forum.cardano.org/t/policy-hash-in-python/61384/2" rel="noreferrer">forums.cardano.org</a>, and someone has pointed me to the right direction of serializing to CBOR according to the <a href="https://github.com/input-output-hk/cardano-ledger-specs/blob/683bef2e40cbd10339452c9f2009867c855baf1a/shelley-ma/shelley-ma-test/cddl-files/shelley-ma.cddl#L228-L246" rel="noreferrer">CDDL specs</a>.</p>
<p>Here's the relevant part of the CDDL specifications:</p>
<pre><code>native_script =
  [ script_pubkey
  // script_all
  // script_any
  // script_n_of_k
  // invalid_before
     ; Timelock validity intervals are half-open intervals [a, b).
     ; This field specifies the left (included) endpoint a.
  // invalid_hereafter
     ; Timelock validity intervals are half-open intervals [a, b).
     ; This field specifies the right (excluded) endpoint b.
  ]

script_pubkey = (0, addr_keyhash)
script_all = (1, [ * native_script ])
script_any = (2, [ * native_script ])
script_n_of_k = (3, n: uint, [ * native_script ])
invalid_before = (4, uint)
invalid_hereafter = (5, uint)
</code></pre>
<p>CBOR and CDDL are both new to me, so I'm not sure if I am doing it right.</p>
<p>According to the above specification, my script</p>
<pre><code>{
   &quot;type&quot;:&quot;all&quot;,
   &quot;scripts&quot;:[
      {
         &quot;keyHash&quot;:&quot;37c19eca0d623d804bfeb8951bf1eb0f7fe193deeb71793985dbd25e&quot;,
         &quot;type&quot;:&quot;sig&quot;
      },
      {
         &quot;type&quot;:&quot;before&quot;,
         &quot;slot&quot;:32241869
      }
   ]
}
</code></pre>
<p>would evaluate to:
<code>native_script = [1, [[0, $hash28], [5, uint]]]</code></p>
<p>Is this correct? Do I need to use any cbor tags for serializing?</p>
<p>Here is my attempt so far (I've tried many variations already, but this feels like the most correct one):</p>
<pre><code>import hashlib
from cbor2 import dumps, loads, shareable_encoder, CBORTag

class Sig:
        def __init__(self, hash, slot):
                self.hash=hash
                self.slot=slot


def default_encoder(encoder,value):
        encoder.encode([1, [[0, value.hash.encode()], [5, value.slot]]])

obj=Sig(&quot;37c19eca0d623d804bfeb8951bf1eb0f7fe193deeb71793985dbd25e&quot;, 32241869)

serialized = dumps(obj, default=default_encoder, value_sharing=False)
print(serialized.hex())
m=hashlib.blake2b(digest_size=28)
m.update(serialized)
print(m.digest().hex())

result = loads(serialized)
print(result)
</code></pre>
<p>Needless to say, the code does not calculate me the same policy id as
<code>cardano-cli transaction policyid --script-file test.script</code> does.</p>
<p>I am using the library <a href="https://github.com/agronholm/cbor2" rel="noreferrer">cbor2</a>, which seems the best maintained one for python. Also, I use blake2b with digest_size set to 28 bytes since the <a href="https://hydra.iohk.io/build/4975913/download/1/ledger-spec.pdf#cite.rfcBLAKE2" rel="noreferrer">ledger specs</a> say blake2b_224 is used for multisig scripts. I am however also not sure if the other parameters in the hash function need to be set or not. My haskell skills are also not the best, so trying to look up in the original source code didn't get me very far, and I've tried so now for several hours :-)</p>
<p>If anyone could help me, it'd be very appreciated.</p>

<script><cbor><python><blake2b><policy>
Re-Implementing Policy Hashing in Python
# Re-Implementing Policy Hashing in Python

(Asked by: username_0 on 2021-05-17)

As the title says, I want to to reimplement the hashing part of a policy script in Python 3.
I have already made a post over at forums.cardano.org, and someone has pointed me to the right direction of serializing to CBOR according to the CDDL specs.
Here's the relevant part of the CDDL specifications:
native_script =
  [ script_pubkey
  // script_all
  // script_any
  // script_n_of_k
  // invalid_before
     ; Timelock validity intervals are half-open intervals [a, b).
     ; This field specifies the left (included) endpoint a.
  // invalid_hereafter
     ; Timelock validity intervals are half-open intervals [a, b).
     ; This field specifies the right (excluded) endpoint b.
  ]

script_pubkey = (0, addr_keyhash)
script_all = (1, [ * native_script ])
script_any = (2, [ * native_script ])
script_n_of_k = (3, n: uint, [ * native_script ])
invalid_before = (4, uint)
invalid_hereafter = (5, uint)

CBOR and CDDL are both new to me, so I'm not sure if I am doing it right.
According to the above specification, my script
{
   "type":"all",
   "scripts":[
      {
         "keyHash":"37c19eca0d623d804bfeb8951bf1eb0f7fe193deeb71793985dbd25e",
         "type":"sig"
      },
      {
         "type":"before",
         "slot":32241869
      }
   ]
}

would evaluate to:
native_script = [1, [[0, $hash28], [5, uint]]]
Is this correct? Do I need to use any cbor tags for serializing?
Here is my attempt so far (I've tried many variations already, but this feels like the most correct one):
import hashlib
from cbor2 import dumps, loads, shareable_encoder, CBORTag

class Sig:
        def __init__(self, hash, slot):
                self.hash=hash
                self.slot=slot


def default_encoder(encoder,value):
        encoder.encode([1, [[0, value.hash.encode()], [5, value.slot]]])

obj=Sig("37c19eca0d623d804bfeb8951bf1eb0f7fe193deeb71793985dbd25e", 32241869)

serialized = dumps(obj, default=default_encoder, value_sharing=False)
print(serialized.hex())
m=hashlib.blake2b(digest_size=28)
m.update(serialized)
print(m.digest().hex())

result = loads(serialized)
print(result)

Needless to say, the code does not calculate me the same policy id as
cardano-cli transaction policyid --script-file test.script does.
I am using the library cbor2, which seems the best maintained one for python. Also, I use blake2b with digest_size set to 28 bytes since the ledger specs say blake2b_224 is used for multisig scripts. I am however also not sure if the other parameters in the hash function need to be set or not. My haskell skills are also not the best, so trying to look up in the original source code didn't get me very far, and I've tried so now for several hours :-)
If anyone could help me, it'd be very appreciated.

**Tags:** script, cbor, python, blake2b, policy

---

## 1 answers

### Answer by username_0 on 2021-05-18. Score: 2 | Accepted answer

It seems I was close, but the str.encode() function was not the correct one to use.
Also a zero byte needs to be prepended to the data to be hashed. Thanks to user bwbush from the cardano forum for helping me out, couldn't have done this without his help!
Here's the final solution:
import hashlib
from cbor2 import dumps, loads, shareable_encoder, CBORTag

class Sig:
        def __init__(self, hash, slot):
                self.hash=hash
                self.slot=slot


def default_encoder(encoder,value):
        encoder.encode([1, [[0, bytes.fromhex(value.hash)], [5, value.slot]]])


obj=Sig("37c19eca0d623d804bfeb8951bf1eb0f7fe193deeb71793985dbd25e", 32241869 + i)

serialized = bytes.fromhex("00") + dumps(obj, default=default_encoder, value_sharing=False)

m=hashlib.blake2b(digest_size=28)
m.update(serialized)

print(m.hexdigest())


---

