Emurgo / cardano-serialization-lib

This is a library, written in Rust, for serialization & deserialization of data structures used in Cardano's Haskell implementation of Alonzo along with useful utility functions.
Other
230 stars 124 forks source link

How to verify signed Tx and signer's address? #640

Closed golddydev closed 10 months ago

golddydev commented 10 months ago

Hello everybody. I am now facing an issue while I tried to verify the signed Tx.

I was trying to verify this signed Tx in the same way I verify the signed Data using

@emurgo/cardano-message-signing-nodejs
@emurgo/cardano-serialization-lib-nodejs

I send the signedData like this

const signedData = {
    signature: transaction string,
    key: signed transaction
}

But I got error like this.

Deserialization failed in TransactionWitnessSet because: Invalid cbor: not the right type, expected Map' byte received Array'.

I think it is because the transaction format and the message (signedData) format is different. But how to fix this?

Can anyone help?

Thank you.

lisicky commented 10 months ago

Hi @eageringdev ! Unfortunately I can not help with additional information like a code example, cbor of transaction

golddydev commented 10 months ago

At least, verifying signed transaction is doable? The blockchain itself certainly do. But how can we do on our own code?

lisicky commented 10 months ago

You can check it by CSL:

  1. Use FixedTransaction type to deserialize a transaction
  2. get raw_body from FixedTransaction and and body hash by TransactionHash.from_bytes(blake2b(32).update(raw_body).digest('binary'))
  3. After you can get signatures and public keys from tx witness set and verify it by calling verify from PublicKey type
golddydev commented 10 months ago

Thanks, @lisicky Your answer inspires me. Extract the public key from transaction body.

import { C } from 'lucid-cardano';

export const decodeSignedData = ({ signedData }) => {
  const { signature, data } = signedData;
  const txVkeyWitnesses = C.TransactionWitnessSet.from_bytes(
    Buffer.from(signature, 'hex')
  );
  const pubKeyHash = txVkeyWitnesses
    .vkeys()
    ?.get(0)
    .vkey()
    .public_key()
    .hash()
    .to_hex();

  const tx = C.Transaction.from_bytes(Buffer.from(data, 'hex'));
  const txSignerPubKeyHash = tx.body().required_signers()?.get(0).to_hex();

  const isVerified = pubKeyHash?.length > 0 && pubKeyHash == txSignerPubKeyHash;

  const rewardAddress = C.RewardAddress.new(
    C.NetworkInfo.mainnet().network_id(),
    C.StakeCredential.from_keyhash(C.Ed25519KeyHash.from_hex(pubKeyHash))
  );
  return {
    isVerified,
    signerAddress: rewardAddress.to_address().to_bech32(),
  };
};

This is the code snippet to verify the signed transaction. Where the signedData.signature is signed transaction and signedData.data is transaction data (hex string).

What do you think?