Skip to content

doc: how to sign the transaction #10

Open
@coolaj86

Description

@coolaj86

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();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions