paulmillr / scure-base

Secure, audited & 0-deps implementation of bech32, base64, base32, base16 & base58
https://paulmillr.com/noble/#scure
MIT License
106 stars 13 forks source link

feature request: add bech32 descriptor checksum #12

Open devlzcode opened 1 year ago

devlzcode commented 1 year ago

I don't want to interfere with the code base but it'd be useful to have this added

adapted from bitcoin core:

const INPUT_CHARSET =
    "0123456789()[],'/*abcdefgh@:$%{}" +
    "IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~" +
    'ijklmnopqrstuvwxyzABCDEFGH`#"\\ ',
  CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

export const addChecksum = (str: string) => `${str}#${descChecksum(str)}`;

function polymod(c: bigint, val: number): bigint {
  let c0 = Number((c >> 35n) & 31n);
  c = ((c & 0x7ffffffffn) << 5n) ^ BigInt(val);
  if (c0 & 1) c ^= 0xf5dee51989n;
  if (c0 & 2) c ^= 0xa9fdca3312n;
  if (c0 & 4) c ^= 0x1bab10e32dn;
  if (c0 & 8) c ^= 0x3706b1677an;
  if (c0 & 16) c ^= 0x644d626ffdn;
  return c;
}

function descChecksum(span: string): string {
  let c = 1n;
  let cls = 0;
  let clscount = 0;
  for (let ch of span) {
    let pos = INPUT_CHARSET.indexOf(ch);
    if (pos === -1) return "";
    c = polymod(c, pos & 31);
    cls = cls * 3 + (pos >> 5);
    if (++clscount === 3) {
      c = polymod(c, cls);
      cls = 0;
      clscount = 0;
    }
  }
  if (clscount > 0) c = polymod(c, cls);
  for (let j = 0; j < 8; ++j) c = polymod(c, 0);
  c ^= 1n;

  let ret = new Array(8).fill(" ");
  for (let j = 0; j < 8; ++j)
    ret[j] = CHECKSUM_CHARSET[Number((c >> (5n * BigInt(7 - j))) & 31n)];
  return ret.join("");
}
paulmillr commented 1 year ago

I think bech32 computes and verifies checksum by default. Doesn't it?

devlzcode commented 1 year ago

I think bech32 computes and verifies checksum by default. Doesn't it?

I'm fairly certain these are two different formats (they even have different polymod generators). This is used for creating a bitcoin-core specific checksum of an output descriptor that is used quite often and afaik is only accessible via an rpc call unless you implement it yourself. More about this here: https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md#checksums

paulmillr commented 1 year ago

So, this is similar to bech32, but not quite bech32, and used only for BTC core RPC calls?