Hash script data

I wanted to understand a bit better the hash-script-data functionality from cardano-cli. I checked the source code but had no luck in understanding how this hash is made.

From the examples, if we run:

cardano-cli transaction hash-script-data --script-data-value 42

We get the following hash:

9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b

However if we try to do a simple SHA256 hash of a 42 value we get:

73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049

Also tried in the Plutus object format:

{
  "constructor": 0,
  "fields": [{
    "int": 42
  }]
}

but none of those match the hash generated by cardano-cli.

Also tried innumerous other alternatives but obviously I’m missing something, anyone knows what’s the process for generating this hash?

Thank you!

1 Like

It’s not a SHA256 hash.

Related topic: Cardano-cli hash-script-data
ScriptData code: cardano-node/ScriptData.hs at 2124712b360b37b5d590279a4674d504dd32eda5 · input-output-hk/cardano-node · GitHub
ScriptData doc: Cardano.Api.ScriptData
CBOR: rfc7049
Hero: @bwbush

3 Likes

Thanks for the references! I was able to reproduce the hash from cardano-cli using libsodium generic-hash and converting the 42 number into CBOR (182a) prior to hashing.

I was trying to do the same for objects though, without luck:

For example, the object mentioned in the other topic:

{
   "constructor":0,
   "fields":[
      {
         "int": 4
      }
   ]
}

I convert it to CBOR A26B636F6E7374727563746F7200666669656C647381A163696E7404 and then try to hash it and I get 9aa23c572ebf5d39113f516d4bbf69d1b9650e55ea63cdd7fb4ee77975def5b0, while if I put that data in a json file and use cardano-cli transaction hash-script-data --script-data-file ./tests/sample.json I get 8bb54ceaee2f57731094a2e96b8ad87bcc8988b1fa838e8c833eb3b72eae29a1

Also tried using bytes instead of UTF string in the CBOR encoding: A24B636F6E7374727563746F7200466669656C647381A143696E7404
which gives me 5677bf1048dad6dce36483d2bec0fafc71f5dc9b01a2b1326c6443c2cd264b50

Even when reading the source code I’m not sure what I may be missing here :frowning:

Anyone has any idea?

I haven’t tracked down the complete answer, which I believe involves finding the schema for how PlutusData is serialized—the information will be in the ledger spec or its implementation.

A partial answer is that one shouldn’t serialize text like "construction". That is too verbose and it is very likely that there is a custom, succinct serialization that is closely related to the CBOR you generated. I’ll poke around in the specs or source code and post here when I find the answer.

2 Likes

That makes sense and is already very helpful! Made me find this which may contain the answer about how PlutusData is encoded into CBOR :slight_smile: definitely looks like my encoding is not correct.

I’ll check that in more detail tomorrow and post here my findings,

Thank you all again for the help :slight_smile:

Alright, looking at the source code it seems the constructor is converted into a tag, based on its index. The values are then converted into a list, which makes the CBOR encoding to be much smaller, makes much more sense.

What I get from the object I mentioned is: D8798104 (Equivalent to 121([4])) - Tag 121, since constructor value is 0 (121 + 0) and a list with values.

Hash turns it into 034eb48ee254d7e58ff9fda1398f8086b9a8c392e63322960137e6c30bbd7bcf, which is still different than cardano-cli output 8bb54ceaee2f57731094a2e96b8ad87bcc8988b1fa838e8c833eb3b72eae29a1 :slightly_frowning_face:

Still seems like a step forward though, feels like we’re getting closer :joy:

If anyone has any idea what I may be missing here let me know

2 Likes

Found the answer for this case :slight_smile: Turns out the list of fields property should be an indefinite length array.

The correct CBOR is: d8799f04ff

d879 - Equivalent to the tag 121
9f - indefinite-length array
04 - The value 4
ff - The list break

Thanks everyone for the inputs, they were really helpful!

2 Likes

This is probably too late but I just found this repo which could be helpful in the future.

Basically this is Intel’s take on CBOR serialization (probably inspired by TinyXML if anyone else remembers old-school libraries)

1 Like

Can you shed some light because I’m missing something.

Using follwoing code

CborWriter w = new CborWriter();
            w.WriteUInt32(42);
            var encoded = w.Encode();
            var data =  Sodium.Utilities.BinaryToHex(encoded);
            Console.WriteLine(data);
            var res = Sodium.GenericHash.Hash(encoded, null, 32);
            Console.WriteLine(Sodium.Utilities.BinaryToHex(res));

output:
182a
9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b

I’m able to get same hash as for running:
cardano-cli transaction hash-script-data --script-data-value 42
9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b

Now I have a json file with simple data:
{“fields”:[],“constructor”:0}

if I execute cli
cardano-cli transaction hash-script-data --script-data-file cos.json
923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec

if I execute:
cardano-cli transaction hash-script-data --script-data-value {“constructor”:0,“fields”:}
e91e9c37cad5a5215f210776134143165a4c438a46cc0d82e5111611805c3864

However if I execute it in c#:

CborWriter cw = new CborWriter();
            cw.WriteStartMap(null);
            cw.WriteTextString("constructor");
            cw.WriteUInt32(0);
            cw.WriteTextString("fields");
            cw.WriteStartArray(null);
            cw.WriteEndArray();
            cw.WriteEndMap();
            var complexEncoded = cw.Encode();
            
            var dataComplex = Sodium.Utilities.BinaryToHex(complexEncoded);
            Console.WriteLine(dataComplex);
            var complexRes = Sodium.GenericHash.Hash(complexEncoded, null, 32);
            Console.WriteLine(Sodium.Utilities.BinaryToHex(complexRes));

bf6b636f6e7374727563746f7200666669656c64739fffff
2b50f0300204565e13018c878fffdfc646f7c8d78fc140e89a3084b6c35c5a90 - hash

  1. Why cardano-cli that takes an file input and the one that takes string value input return different value?
  2. Any idea how to make c# code to return same hash?

I have just checked the new beta version of the
npm i @emurgo/cardano-serialization-lib-nodejs@10.0.0-beta.8

and it works same way as the cardano-cli

import { Buffer } from "buffer";
import * as pp from "@emurgo/cardano-serialization-lib-nodejs"
export const fromHex = (hex) => Buffer.from(hex, "hex");
export const toHex = (bytes) => Buffer.from(bytes).toString("hex");
const datum = pp.PlutusData.new_constr_plutus_data(
  pp.ConstrPlutusData.new(
    pp.BigNum.from_str("0"),
    pp.PlutusList.new()
  )
);
var res = pp.hash_plutus_data(datum);
console.log(res);
console.log(toHex(res.to_bytes()));