2

I am building a daap and i want the user to be able to mint their app-tokens but the policy of the token must include the signature of the app wallet as well, for this I did the policy with the following code: where appKeyHash and clientKeyHash are the respective hashes for client and app addresses

const nativeScripts = Loader.Cardano.NativeScripts.new();
  const script = Loader.Cardano.ScriptPubkey.new(clientKeyHash);
  const nativeScript = Loader.Cardano.NativeScript.new_script_pubkey(script);
  const appScript = Loader.Cardano.ScriptPubkey.new(appKeyHash);
  const appNativeScript =
    Loader.Cardano.NativeScript.new_script_pubkey(appScript);

  const lockScript = Loader.Cardano.NativeScript.new_timelock_expiry(
    Loader.Cardano.TimelockExpiry.new(ttl)
  );
  nativeScripts.add(nativeScript);
  nativeScripts.add(appNativeScript);
  nativeScripts.add(lockScript);

  const finalScript = Loader.Cardano.NativeScript.new_script_all(
    Loader.Cardano.ScriptAll.new(nativeScripts)
  );
  const policyId = Buffer.from(
    Loader.Cardano.ScriptHash.from_bytes(
      finalScript.hash().to_bytes()
    ).to_bytes(),
    "hex"
  ).toString("hex");

Then I do all the transaction fee calculation and use Nami wallet to partial-sign the Tx (in the client side). Then I send to my backend the partial-signed transaction, and I add one vkeywitness using the private key of the app and Cardano-Serialization-Lib with the following code:

 const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txString, "hex"));
const body = tx.body();
const txHash = CardanoWasm.hash_transaction(body);
const txWitnesses = tx.witness_set();
const txVkeys = txWitnesses.vkeys();
const txScripts = txWitnesses.native_scripts();

const addWitnesses = CardanoWasm.TransactionWitnessSet.new();
const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();

const vkeyWitness = CardanoWasm.make_vkey_witness(txHash, prvKey);
vkeyWitnesses.add(vkeyWitness);
addWitnesses.set_vkeys(vkeyWitnesses);
const addVkeys = addWitnesses.vkeys();
const addScripts = addWitnesses.native_scripts();

const totalVkeys = CardanoWasm.Vkeywitnesses.new();
const totalScripts = CardanoWasm.NativeScripts.new();
if (txVkeys) {
  for (let i = 0; i < txVkeys.len(); i++) {
    totalVkeys.add(txVkeys.get(i));
  }
}
if (txScripts) {
  for (let i = 0; i < txScripts.len(); i++) {
    totalScripts.add(txScripts.get(i));
  }
}
if (addVkeys) {
  for (let i = 0; i < addVkeys.len(); i++) {
    totalVkeys.add(addVkeys.get(i));
  }
}
if (addScripts) {
  for (let i = 0; i < addScripts.len(); i++) {
    totalScripts.add(addScripts.get(i));
  }
}
const totalWitnesses = CardanoWasm.TransactionWitnessSet.new();
totalWitnesses.set_vkeys(totalVkeys);
totalWitnesses.set_native_scripts(totalScripts);
const signedTx = CardanoWasm.Transaction.new(
  tx.body(),
  totalWitnesses,
  tx.auxiliary_data()
);

Where tx is the hex-CBOR transaction i get from my front end (with the front-end witnessvkey already attached)

But when i try to send it i get the following error :

"transaction submit error ShelleyTxValidationError ShelleyBasedEraAlonzo (ApplyTxError [UtxowFailure (WrappedShelleyEraFailure (ScriptWitnessNotValidatingUTXOW (fromList [ScriptHash \"afae51e0836484a4196a3ef11018ab78990008df6459e70f7db6cfb4\"]))),UtxowFailure (WrappedShelleyEraFailure (UtxoFailure (FeeTooSmallUTxO (Coin 187325) (Coin 187193))))])"

The first error its because i am not correctly validating the transaction, the seccond is because the fees go up a little because I added one extra signature. I am not worried about the seccond error, what i do not underestand is

why is happening the first error:

WrappedShelleyEraFailure (ScriptWitnessNotValidatingUTXOW (fromList [ScriptHash \"afae51e0836484a4196a3ef11018ab78990008df6459e70f7db6cfb4\"]))) 

Even if i signed the Tx with the correponding privakekeys ?

1
  • Can you share the body of minting nft. I am also facing this issue. I have done from the cardano-cli but i want to make it within react project and want to use nami wallet for minting that user can do from his wallet. If its possible or you can help in this? Jul 28 at 7:54

4 Answers 4

2

After days of searching I finally found my mistake: I was tryng to do the signing from backend, so I needed to generate the prvKey for this I followed the instructions provided in Cardano Serialization Lib, my code was

const Bip32PrivateKey =
  "xprv1wrgj3q60pxt5uq094h6f9nj00jx4hrwddgqrnv632a86llc94egpkn77l9mg9ewham96309yj6yv4zdsdr05flv6xsntjgcgj3pfp59ef589a87gen99t4ru5jhnfm4ar0tvlry4guz6l95tnjcjtnf4jv3rf52k";
const prvKeyBip32 = CardanoWasm.Bip32PrivateKey.from_bech32(Bip32PrivateKey);


function harden(num) {
  return 0x80000000 + num;
}

const rootKey = CardanoWasm.Bip32PrivateKey.from_bech32(Bip32PrivateKey);
const accountKey = rootKey
  .derive(harden(1852)) // purpose
  .derive(harden(1815)) // coin type
  .derive(harden(0)); // account #0

const utxoPubKey = accountKey
  .derive(0) // external
  .derive(0)
  .to_public();

const stakeKey = accountKey
  .derive(2) // chimeric
  .derive(0)
  .to_public();

const baseAddr = CardanoWasm.BaseAddress.new(
  CardanoWasm.NetworkInfo.testnet().network_id(),
  CardanoWasm.StakeCredential.from_keyhash(utxoPubKey.to_raw_key().hash()),
  CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash())
);


const prvKey = rootKey.to_raw_key();

And I was trying to sign with prvKey, for a UTXO in baseAddr, that did not work, but if O do

const prvKey = accountKey
  .derive(0) 
  .derive(0)
  .to_raw_key();

Then I can sign and submit the tx for UTXOs in the baseAddr.

1

There seems to be a bug on @emurgo/cardano-serialization-lib end related to the issue that you are facing, submitted here

1
  • Thank you andy! but i am afraid that that bug seems to be related with the fees and not with the signing. I tried to find the issue in my code, running a simpler Tx and sending using Blockfrost. but that did not for the same issue, so i think i am doing something wrong in the signing. Could you please share the code that you use to sign? In the reported bug you show above the only issue is the fees, so validation is working fine! Or maybe the fact that I am signing with a private key i got from my 24 words recovery phrase. Nov 29, 2021 at 23:41
1

This is not a particularly trivial task, as we faced it today! I can't tell exactly what is off about your code, but it's possibly something very minor. I've noticed that the JS -> WASM handover can cause some weird type confusion for example if you pass a TimelockExpiry object into txBuilder.set_ttl it will actually set it to 0. It's very easy to make mistakes!

I can however offer an example that we tested and is working, based off some existing code we had as well as andycandy's example on the Github Issue. You can find the Gist here (hopefully it will help you figure out what is wrong with your code!):

https://gist.github.com/lovalabs/83d08796ab9ff65cbf188fbca8d03343

Note that we're in the process of releasing a minting SDK for cardano-serialization-lib that will be wrap around these low-level details and make minting assets much easier. Since the version of csl this is based off is still in beta, we're waiting for it to be stable before releasing.

EDIT: After some further testing I've discovered that there may be a bug in the TransactionBuilder implementation of build() vs build_tx() which could be the reason for your error. When I do this:

const tx = txBuilder.build_tx()

I don't get a ScriptWitnessNotValidatingUTXOW error. But if I do this:

const txBody = txBuilder.build()
const tx = S.Transaction.new(
        txBody,
        txWitnessSet,
        auxiliaryData
)

Then I do get a ScriptWitnessNotValidatingUTXOW error. Will investigate further before raising an issue on Github though perhaps you could confirm this.

2
  • Thank you so much i dont see the method txBuilder.build_tx(), i think as you said the bug could be there... What version are you using? Dec 2, 2021 at 5:37
  • I got to see that i added the vkey correctly ... but it does not match with the one in the transaction that i sent. I change all my code to only do a simple transaction. and yet i cant do it. I do not know what is the problem Dec 2, 2021 at 5:40
1

There might be an issue with the policy id generated in your first code block.

The hash function on NativeScript should take a namespace parameter (see here https://github.com/Emurgo/cardano-serialization-lib/blob/3e5dc377829d1ba46348713196b35476d8604bd9/rust/pkg/cardano_serialization_lib.js.flow#L2699),

that is prepended when calculating the hash (see here https://github.com/Emurgo/cardano-serialization-lib/blob/da6eba20a5ffab2eeae1b1823c26ee67b1426f42/rust/src/lib.rs#L1629)

Try adding the zero like this:

const nativeScript = CardanoWasm.NativeScript.new_script_all(CardanoWasm.ScriptAll.new(nativeScripts))

const policyId = Buffer.from(nativeScript.hash(0).to_bytes()).toString('hex');

instead of this part:

const finalScript = Loader.Cardano.NativeScript.new_script_all(
  Loader.Cardano.ScriptAll.new(nativeScripts)
);
const policyId = Buffer.from(
  Loader.Cardano.ScriptHash.from_bytes(
    finalScript.hash().to_bytes()
  ).to_bytes(),
  "hex"
).toString("hex");
3
  • No I dont think so. If I quit one address and left only one and sign with nami ot works.. the problem is related to the signing off-line with cardano serialization lib... I can not do it ! I tried with a simpler tx and is giving me the same error Dec 2, 2021 at 13:37
  • 1
    Hm then maybe the error might not be in the code posted here, but in-between, where you 1) create the transaction in the frontend, 2) cbor / hex encode it for sending to the backend and then 3) decode the payload there. Maybe you could just verify the whole thing works by testing it only in the frontend first incl. the cbor + hex encoding / decoding etc. Maybe something like this:
    – sean
    Dec 2, 2021 at 18:19
  • 1
    const signedTx = CardanoWasm.Transaction.new(...); const txhex = toHex(cbor.encode(signedTx.to_bytes())); const payload = { txhex }; // -> This payload would be sent to the backend, but now test in frontend const hexDecode = cbor.decode(fromHex(payload.txhex)); const txFromBytes = CardanoWasm.Transaction.from_bytes(hexDecode); let witnessSet = txFromBytes.witness_set(); let witnesses = ws.vkeys(); witnesses.add(scriptWitness); witnessSet.set_vkeys(witnesses); witnessSet.set_native_scripts(nativeScripts); // ...
    – sean
    Dec 2, 2021 at 18:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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