input-output-hk / nami

Nami Wallet is a browser based wallet extension to interact with the Cardano blockchain. Support requests: https://iohk.zendesk.com/hc/en-us/requests/new
https://namiwallet.io
Apache License 2.0
373 stars 167 forks source link

Help needed with signing Multisig scripts #171

Closed rumourscape closed 2 years ago

rumourscape commented 2 years ago

So I am working on a way to mint tokens with the policy script being signed in the backend. Unfortunately, for some reason, the signed transaction returns the error:

 ScriptWitnessNotValidatingError: ["9720b134a8c417ac3709c0a3462ef7bea7e2f95d5bfa3875e342ae66"] 

I don't see where I could have gone wrong. I have created the witnesses with the same key used to create the policy script.

Here's my code: https://github.com/rumourscape/Minting-API I would appreciate any help. Thank you.

pierre-andre-long commented 2 years ago

Would love understanding what's going wrong as well.

alessandrokonrad commented 2 years ago

I may need some more information about what you want to achieve or what you are doing exactly. Do you have all vkeys and scripts in the TransactionWitnessSet?

rumourscape commented 2 years ago

I am constructing a tx at the frontend that mints a token with a policy script constructed at the backend. The tx is signed by Nami and then sent back where it is signed again with keys for the policy script and then submitted. Yes the TransactionWitnessSet includes all the vkeys and scripts.

pierre-andre-long commented 2 years ago

Here is the code for the policy script signature part (the signature that produces the errror when submiting the transaction)

import * as Cardano from '@emurgo/cardano-serialization-lib-nodejs';
import { submitTx } from './ogmios';
import { mnemonicToEntropy } from 'bip39';
import { Buffer } from 'buffer';
import env from 'src/env.json'

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

// Purpose derivation (See BIP43)
enum Purpose {
  CIP1852 = 1852, // see CIP 1852
}

// Cardano coin type (SLIP 44)
enum CoinTypes {
  CARDANO = 1815,
}

enum ChainDerivation {
  EXTERNAL = 0, // from BIP44
  INTERNAL = 1, // from BIP44
  CHIMERIC = 2, // from CIP1852
}

function getCip1852Account(): Cardano.Bip32PrivateKey {
  const entropy = mnemonicToEntropy(env.phrase);

  const rootKey = Cardano.Bip32PrivateKey.from_bip39_entropy(
    Buffer.from(entropy, 'hex'),
    Buffer.from(''),
  );
  return rootKey
    .derive(harden(Purpose.CIP1852))
    .derive(harden(CoinTypes.CARDANO))
    .derive(harden(0)); // account #0
}

//Keys
const rootkey = getCip1852Account();
const prvkey = rootkey.to_raw_key();
const pubkey = prvkey.to_public();
const pubhash = pubkey.hash();
console.log(pubkey.to_bech32())

export function getBaseAddress() {
  const cip1852Account = getCip1852Account();

  const utxoPubKey = cip1852Account
    .derive(ChainDerivation.EXTERNAL)
    .derive(0)
    .to_public();
    const stakeKey = cip1852Account
    .derive(ChainDerivation.CHIMERIC)
    .derive(0)
    .to_public();

  const baseAddr = Cardano.BaseAddress.new(
    0,
    Cardano.StakeCredential.from_keyhash(utxoPubKey.to_raw_key().hash()),
    Cardano.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash()),
  );

  //return baseAddr.to_address().to_bech32();
  return baseAddr
}

function Script() {
  const lock = Cardano.TimelockExpiry.new(env.time_lock);
    const nativeScripts = Cardano.NativeScripts.new();
  const keyhash = getBaseAddress().payment_cred().to_keyhash()
    const script = Cardano.ScriptPubkey.new(keyhash);
    const nativeScript = Cardano.NativeScript.new_script_pubkey(script);
    const lockScript = Cardano.NativeScript.new_timelock_expiry(lock);

    nativeScripts.add(nativeScript);
    nativeScripts.add(lockScript);

    const finalScript = Cardano.NativeScript.new_script_all(
        Cardano.ScriptAll.new(nativeScripts)
    );

  return finalScript
}

export function getScript() {
    const finalScript = Script();

    //const scripthash = Cardano.ScriptHash.from_bytes( finalScript.hash(0).to_bytes() );

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

  return {
    policyId : policyId,
    script : Buffer.from(finalScript.to_bytes()).toString("hex"),
    ttl : env.time_lock
  };

}

export function signTx(tx: string, witness: string) {

  const transaction = Cardano.Transaction.from_bytes(Buffer.from(tx, 'hex'))

  //const txWitnesses = Cardano.TransactionWitnessSet;
  const txVkeys = Cardano.Vkeywitnesses.new();
  const txScripts = Cardano.NativeScripts.new();

  const addWitnesses = Cardano.TransactionWitnessSet.from_bytes(
      Buffer.from( witness, "hex")
  );
/*
  const signers = Cardano.Ed25519KeyHashes.new();
  signers.add(pubhash);
  transaction.body().set_required_signers(signers);
*/    
  const txhash = Cardano.hash_transaction(transaction.body());

  const vkeyWit = Cardano.make_vkey_witness( txhash, prvkey )
  //console.log(vkeyWit.vkey().public_key().to_bech32())

  const script = Script()
  txScripts.add(script)
  txVkeys.add(vkeyWit);
  //addScripts.add(script);

  const addVkeys = addWitnesses.vkeys();
  const addScripts = addWitnesses.native_scripts();

  const totalVkeys = Cardano.Vkeywitnesses.new();
  const totalScripts = Cardano.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 = transaction.witness_set();
  totalWitnesses.set_vkeys(totalVkeys);
  totalWitnesses.set_native_scripts(totalScripts);

  const signedTx = Cardano.Transaction.new(
    transaction.body(),
    totalWitnesses,
    transaction.auxiliary_data()
  );

  const signedTx_cborHex = Buffer.from(signedTx.to_bytes()).toString('hex')
  console.log(signedTx_cborHex)
  return submitTx(signedTx_cborHex)
  //return { 'transaction' : signedTx_cborHex}
}
rumourscape commented 2 years ago

I have found the problem and resolved the issue.