Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: how to sign the transaction #10

Open
coolaj86 opened this issue Feb 18, 2025 · 0 comments
Open

doc: how to sign the transaction #10

coolaj86 opened this issue Feb 18, 2025 · 0 comments

Comments

@coolaj86
Copy link
Member

coolaj86 commented Feb 18, 2025

Forgive the slop.

In short:

  • signatures do NOT have salt/nonce
  • hashes are DOUBLE
  • hashes are NOT reversed when signed (the "serialized" form is a lie)
  • signatures are NOT reversed

The transition has 2 forms:

  • null sigs (signed by each identity key, and the asset lock key)
  • full sigs (includes all signature data)
let textEncoder = new TextEncoder();
let Secp256k1 = require("@dashincubator/secp256k1");

async function sha256(data) {
  let hashBuffer = await crypto.subtle.digest("SHA-256", data);
  let hashBytes = new Uint8Array(hashBuffer);
  return hashBytes;
}

function toHex(bytes) {
  return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
    "",
  );
}

let Bytes = {};
/** @typedef {String} Hex */
/**
 * @param {Hex} hex
 */
Bytes.hexToBytes = function (hex) {
  let bufLen = hex.length / 2;
  let bytes = new Uint8Array(bufLen);

  let i = 0;
  let index = 0;
  let lastIndex = hex.length - 2;
  for (;;) {
    if (i > lastIndex) {
      break;
    }

    let h = hex.substr(i, 2);
    let b = parseInt(h, 16);
    let nan = isNaN(b);
    if (nan) {
      throw new Error(`'${h}' could not be parsed as hex`);
    }
    bytes[index] = b;

    i += 2;
    index += 1;
  }

  return bytes;
};

let KeyUtils = {};

/**
 * @param {Uint8Array} privKeyBytes
 * @param {Uint8Array} doubleHashBytes
 * @returns {Promise<Uint8Array>} - magic signature
 */
KeyUtils.magicSign = async function (privKeyBytes, doubleHashBytes) {
  let MAGIC_OFFSET = 27 + 4; // 27 because bitcoin, 4 because "compressed" key
  let testing = true;
  let sigOpts = { canonical: true, der: false, recovered: true };
  if (!testing) {
    Object.assign({ extraEntropy: true });
  }
  let recoverySig = await Secp256k1.sign(
    doubleHashBytes,
    privKeyBytes,
    sigOpts,
  );
  let magicSig = new Uint8Array(65);
  let recovery = MAGIC_OFFSET + recoverySig[1];
  // the magic byte is prepended (the signature is NOT reversed)
  magicSig[0] = recovery;
  magicSig.set(recoverySig[0], 1);
  return magicSig;
};

async function runTest() {
  let keyHex =
    "561349d51e3682f542adf609a95e579caf2974e823c7cf283d9bab001b2e4f7e";
  let privKeyBytes = Bytes.hexToBytes(keyHex);

  // let input = "";
  // let data = textEncoder.encode(input);

  let input =
    "0300040000000000000021037c004626332a6411dee452f556930c7415375a198b496967a8f6e07fd2ab2da80001000002000021037c004626332a6411dee452f556930c7415375a198b496967a8f6e07fd2ab2da80002000001000021037c004626332a6411dee452f556930c7415375a198b496967a8f6e07fd2ab2da80003000301000021037c004626332a6411dee452f556930c7415375a198b496967a8f6e07fd2ab2da800c60101c555131b64551385a270635f3bfb89f55784f402ad9bb4424917f8e9021dcd47010000001c834865e59455ef2033cd45d43d7315c79930b23204fec487437297707e0c079c1274f2b2686154cef2c798a7e1f4a5311aab4ee3a97a215c84c99d2d000000850db91ccff5879c898039fcfbe4f3453dcfeb46e332455dce77815291c236a0560bd67fea231b10e916e8bf11d891db0fcd01261dcbefb6077b4cdec40913ac7740f8b65bad6700a4548de22f818f1a684edf7c97e9cb196ff89765e950e462f00300080001c555131b64551385a270635f3bfb89f55784f402ad9bb4424917f8e9021dcd47010000006b4830450221008e46aee96b107b1407c29170b6c75d2bc1a45d7099ef4ae62c911513926f371a022074d9d0060071d8daae60de6926a8d27ee38556cd2ba3466069cb5bc577ea841c812103916e3f05d84deaeeb37a97b35ae8e94aa6447cea52159bbd7918a49bbcdfe4beffffffff0200e1f50500000000026a00e1428c1d000000001976a9149aa6e68be50ebd57c7521aab16b3deb45539685388ac0000000024010100e1f505000000001976a91406f42511fae21580b0ad82e101523ca060c4c6d188ac0000";
  let data = Bytes.hexToBytes(input);

  let expectedOutputStr =
    //"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d";
    "5cd6e6496d81d285df139834c05c87e438ce9b73874d3f074aebbc249e032638";

  let firstHash = await sha256(data);
  let secondHash = await sha256(firstHash);

  // Reverse the result
  // let reversedHash = secondHash.reverse();

  // Convert to hex string
  let resultHex = toHex(secondHash);
  // let resultHex = toHex(reversedHash);

  console.log("Computed Hex:", resultHex);
  console.log("Matches Expected:", resultHex === expectedOutputStr);

  let sig = await KeyUtils.magicSign(privKeyBytes, secondHash);
  let sigHex = toHex(sig);
  console.log(sigHex);
}

runTest();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant