Anastasia-Labs / lucid-evolution

https://anastasia-labs.github.io/lucid-evolution/
43 stars 17 forks source link

Smart contract transaction hash received is the same for distinct redeemers #371

Open leobel opened 2 weeks ago

leobel commented 2 weeks ago

Hi, I've noticed that for slightly changes in a redeemer, I've only testes with changing some digits in a json numeric property but could be something deeper, the resulting transaction hash received by Aiken is the same.

Lucid Evolution @lucid-evolution/lucid = "v0.3.49"

Aiken

compiler = "v1.1.3"
plutus = "v3"
aiken-lang/stdlib = "v2.1.0"

Here is the smart contract:

validator foo {
  spend(
    _datum: Option<Data>,
    _redeemer: Data,
    _self_ref: OutputReference,
    self_tx: Transaction,
  ) {
    let Transaction { id, .. } = self_tx
    trace @"tx hash": id
    fail
  }

  else(_) {
    fail
  }
}

You can reproduce the issue with this code

import { Blockfrost, Data, Lucid, SpendingValidator, UTxO } from "@lucid-evolution/lucid";
import * as fs from 'fs';

const lucid = await Lucid(
    new Blockfrost(
        "https://cardano-preview.blockfrost.io/api/v0",
        "preview..." // your key here
    ),
    "Preview"
);

// Select your wallet here
const prvKey = fs.readFileSync('./src/me.sk').toString();
lucid.selectWallet.fromPrivateKey(prvKey);
const walletAddress = await lucid.wallet().address();
console.log('Wallet Address:', walletAddress);

// compiled code for the smart contract above
const spend: SpendingValidator = {
    type: "PlutusV3",
    script: "590358590355010100323232323232323225333002323232323253330073370e900118049baa001132323253300b3372c92109747820686173683a200037326664646464646002002444a6664666603200626464646464646601800400266e2922010128000025333016337100069007099b80483c80400c54ccc058cdc4001a410004266e00cdc0241002800690068b299980c800899b8a4881035b5d2900005133714911035b5f2000333300800133714911025d2900005223330090090023006001223330090090020013758602e0046eb4c054004c8cdd81ba83015001374e602c0026ea800c4c94ccc05c0044cdc52441027b7d00003133714911037b5f200032323300100100322533301a00110031533301a301c0011323330090093019001337149101023a2000333009009301a001004301b0011323330090093019001337149101023a2000333009009301a001300633003003301d002301b0013371491102207d000033756006264a66602e002266e29221025b5d00003133714911035b5f2000333300600133714911015d000032233300700700230040012233300700700200137580066e292201022c2000133005375a0040022646466e2922010268270000132333001001337006e3400920013371491101270000322253330153371000490000800899191919980300319b8000548004cdc599b80002533301833710004900a0a40c02903719b8b33700002a66603066e2000520141481805206e0043370c004901019b8300148080cdc70020011bae002222232330010010052253330170011005153330173019001133003301800100513300430180013300200230190012232330010010032253330103370e0029000099b8a488101300000315333010337100029000099b8a489012d003300200233702900000089980299b8400148050cdc599b803370a002900a240c00066002002444a66601a66e2400920001001133300300333708004900a19b8b3370066e14009201448180004c03cc040c040c040c040c040c040c040c040c040c040c040c034dd5003a450016300e300f002300d001300a37540022c60166018004601400260140046010002600a6ea8004526153300349011856616c696461746f722072657475726e65642066616c736500136565734ae7155ceaab9e5573eae855d12ba401"
};

const spendAddress = "addr_test1xpx8w9plfr5t8zusdg8flxs6jgcl5uzhxc7nsy76vg6zz46vwu2r7j8gkw9eq6swn7dp4y33lfc9wd3a8qfa5c35y9tsjkus62";

let spendRedeemer = Data.to(Data.fromJson({
    foo: 123
}));
// let spendRedeemer = "bf43666f6f187bff"; 

// let spendRedeemer = Data.to(Data.fromJson({
//     foo: 124
// }));
// let spendRedeemer = "bf43666f6f187cff"; 

console.log('Redeemer:', spendRedeemer);

const validTo = 1729163464873 + (20 * 60 * 60 * 1000); // 20 hour

const utxo: UTxO = {
    address: spendAddress,
    txHash: "a9d1ab3e1bd3d048a149b596c6270b8aca30cf37bc2024c6de143d04835958a9",
    outputIndex: 0,
    assets: { lovelace: BigInt(5_000_000) }
}

const tx = await lucid
    .newTx()
    .collectFrom([utxo], spendRedeemer)
    // consume script
    .attach.SpendingValidator(spend)

    .pay.ToAddress(
        walletAddress, 
        {
            lovelace: BigInt(1_000_000),
        }
    )
    .validTo(validTo)
    .complete();

You can try with redeemers:

let spendRedeemer = Data.to(Data.fromJson({
    foo: 123
}));

and

let spendRedeemer = Data.to(Data.fromJson({
    foo: 124
}));

Transaction hash will be the same: C5863C120096F9400EBDBFE2FF58F6FA3909E770D1D990AFDF28064946FFE78E

zachyking commented 2 weeks ago

Redeemer is not a part of transaction body but the witness set, and transaction hash is a transaction body hash. So if everything else in the tx is the same it's expected behavior.

leobel commented 1 day ago

Redeemer is not a part of transaction body but the witness set, and transaction hash is a transaction body hash. So if everything else in the tx is the same it's expected behavior.

script_data_hash is part of transaction body, so changes on the redeemer should be reflected on the transaction hash: https://github.com/IntersectMBO/cardano-ledger/blob/7358718500e9bf1adc78092bdcf90ee9c2231df7/eras/conway/impl/cddl-files/conway.cddl#L359

; This is a hash of data which may affect evaluation of a script.
;     This data consists of:
;       - The redeemers from the transaction_witness_set (the value of field 5).
;       - The datums from the transaction_witness_set (the value of field 4).
;       - The value in the costmdls map corresponding to the script's language
;         (in field 18 of protocol_param_update.)
;     (In the future it may contain additional protocol parameters.)