1

Given the CBOR representation of a plutus validator, its script address can be easily generated with a variety of tools, e.g. cardano-cli and PyCardano. However, both tools generate different script addresses for the same CBOR. How is this possible? Am I finding a bug in PyCardano?

In what follows I describe the building of the script address with both tools, using an example. We are going to work with the following CBOR:

$ cat script.plutus
{
    "type": "PlutusScriptV2",
    "description": "",
    "cborHex": "581a581801000022232632498cd5ce2481064255524e542100120011"
}

Generate script address with cardano-cli:

$ cardano-cli address build \
>   --payment-script-file script.plutus \
>   --testnet-magic 2 \
>   --out-file scriptA.addr

which resulted in

$ cat scriptA.addr ; echo
addr_test1wrmz3pjz4dmfxj0fc0a0eyw69tp6h7mpndzf9g3kttq9cqqqw47ym

Generate script address with PyCardano:

>>> from pycardano import (
...     BlockFrostChainContext,
...     Network,
...     Address,
...     plutus_script_hash,
...     PlutusV2Script
... )
>>> from blockfrost import ApiUrls
>>> context = BlockFrostChainContext("your-preview-project-id", base_url=ApiUrls.preview.value)
>>> network = Network.TESTNET
>>> script_plutus = PlutusV2Script(bytes.fromhex("581a581801000022232632498cd5ce2481064255524e542100120011"))                            
>>> script_hash = plutus_script_hash(script_plutus)
>>> scriptB_addr = Address(script_hash, network=network)
>>> scriptB_addr
addr_test1wqql5y0gsa3ul0vcmdzmp9zaxz8ak0d3j8yfd4yd73pfh4c6ew25x

As claimed, we have obtained two completely different script-addresses, which is very strange.

Edit: correct procedure with PyCardano:

Following Michele's solution and insight, an efficient way of using PyCardano to deal with the same file script.plutus that we used with cardano-cli could be as follows:

>>> import json
>>> from pycardano import (
...     BlockFrostChainContext,
...     Network,
...     Address,
...     plutus_script_hash,
...     PlutusV2Script
... )
>>> from blockfrost import ApiUrls
>>> context = BlockFrostChainContext("your-preview-project-id", base_url=ApiUrls.preview.value)
>>> network = Network.TESTNET
>>> with open("script.plutus", "r") as f:
...     script_cborHex = json.load(f)["cborHex"][4:]
... 
>>> script_plutus = PlutusV2Script(bytes.fromhex(script_cborHex))
>>> script_hash = plutus_script_hash(script_plutus)
>>> scriptB_addr = Address(script_hash, network=network)
>>> scriptB_addr
addr_test1wrmz3pjz4dmfxj0fc0a0eyw69tp6h7mpndzf9g3kttq9cqqqw47ym

and, as Michele pointed out, we have recovered the same address produced by cardano-cli.

2
  • The script address is the hash of a larger binary data struct. Even a single byte being different between the two will cause the hash to be vastly different. My first guess is that Network.TESTNET is not the same as 2. Even if these two are the same there is something else different. May 14, 2023 at 7:07
  • 1
    network magic is irrelevant; all the address cares about is if it is testnet (any of them) or mainnet May 14, 2023 at 7:29

1 Answer 1

3

After a quick look at the PyCardano implementation of script_hash

the problem is in the input of the function.

The hash of a Plutus script is calculated using the compiled contract wrapped only once in CBOR.

cardano-cli wraps the contract bytes twice in CBOR for historical reasons.

for that reason it is a valid input for cardano-cli but a bad input for PyCardano

in your case your script looks like this

581a581801000022232632498cd5ce2481064255524e542100120011
581a 5818 01000022232632498cd5ce2481064255524e542100120011
CBOR #1 CBOR #2 the actual contract

so ultimately you want to pass CBOR #2 + the actual contract

your snippet should then look like this

>>> from pycardano import (
...     BlockFrostChainContext,
...     Network,
...     Address,
...     plutus_script_hash,
...     PlutusV2Script
... )
>>> from blockfrost import ApiUrls
>>> network = Network.TESTNET
>>> script_plutus = PlutusV2Script(bytes.fromhex("581801000022232632498cd5ce2481064255524e542100120011"))                            
>>> script_hash = plutus_script_hash(script_plutus)
>>> scriptB_addr = Address(script_hash, network=network)
>>> scriptB_addr
addr_test1wrmz3pjz4dmfxj0fc0a0eyw69tp6h7mpndzf9g3kttq9cqqqw47ym
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.