theantnest / bccaddress

Bitcoin Cash Address Generator
MIT License
80 stars 49 forks source link

Add support for new cashaddr format! #24

Closed zquestz closed 6 years ago

zquestz commented 6 years ago

If anyone wants to update code for the new cashaddr format that would be epic. Otherwise I will try to get to it soon. =)

DesWurstes commented 6 years ago

If you need, I have a one-file, dependencyless address translator here: https://github.com/cashaddress/cashaddress.github.io/blob/master/main.js#L202

Or just copy paste this:

const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
function craftCashAddress(kind, addressHash, isMainNet) {
  var payload = packCashAddressData(kind, addressHash);
  if (payload.length == 0) {
    return;
  }
  // func ExpandPrefix(prefix string) []byte {
  // ret := make(data, len(prefix) + 1)
  // for i := 0; i < len(prefix); i++ {
  //    ret[i] = byte(prefix[i]) & 0x1f;
  // }
  // ret[len(prefix)] = 0;
  // return ret;
  // }
  // https://play.golang.org/p/NMR2ImCmdpZ
  var expandPrefix = [];
  if (isMainNet == true) {
    expandPrefix = [2, 9, 20, 3, 15, 9, 14, 3, 1, 19, 8, 0];
  } else {
    expandPrefix = [2, 3, 8, 20, 5, 19, 20, 0];
  }
  var enc = expandPrefix.concat(payload);
  var mod = polyMod(enc.concat([0, 0, 0, 0, 0, 0, 0, 0]));
  var retChecksum = [];
  var t = [];
  for (var i = 0; i < 8; i++) {
    // Convert the 5-bit groups in mod to checksum values.
    // retChecksum[i] = (mod >> uint(5*(7-i))) & 0x1f
    retChecksum[i] = simplify(and(rShift(mod, 5 * (7 - i)), [31]))[0];
  }
  var combined = payload.concat(retChecksum);
  var ret = "";
  if (isMainNet == true) {
    ret = "bitcoincash:";
  } else {
    ret = "bchtest:";
  }
  for (var i = 0; i < combined.length; i++) {
    ret = ret.concat(CHARSET[combined[i]]);
  }
  if (ret.length == 54 || ret.length == 50) {
    return ret;
  } else {
    return ""
  }
}
function packCashAddressData(addressType, addressHash) {
  // Pack addr data with version byte.
  var versionByte = addressType << 3;
  // Those addresses are not in use!
  /*var encodedSize = (addressHash.length - 20) / 4
    if ((addressHash.length-20)%4 != 0) {
        return []
    }
    if (encodedSize < 0 || encodedSize > 8) {
        return []
    }
    versionByte |= encodedSize*/
  var data = [versionByte].concat(addressHash);
  return convertBits(data, 8, 5, true);
}

function convertBits(data, fromBits, tobits, pad) {
  // General power-of-2 base conversion.
  var acc = 0;
  var bits = 0;
  var ret = [];
  var maxv = (1 << tobits) - 1;
  var maxAcc = (1 << (fromBits + tobits - 1)) - 1;
  for (var i = 0; i < data.length; i++) {
    var value = data[i];
    if (value < 0 || value >> fromBits !== 0) {
      return [];
    }
    acc = ((acc << fromBits) | value) & maxAcc;
    bits += fromBits;
    while (bits >= tobits) {
      bits -= tobits;
      ret.push((acc >> bits) & maxv);
    }
  }
  if (pad) {
    if (bits > 0) {
      ret.push((acc << (tobits - bits)) & maxv);
    }
  } else if (bits >= fromBits || ((acc << (tobits - bits)) & maxv) != 0) {
    return [];
  }
  return ret;
}

function polyMod(v) {
  var c = [0, 1];
  var c0 = [0];
  var temp = [];
  for (var i = 0; i < v.length; i++) {
    c0 = rShift(c, 35);
    c = xor(add5zerosAtTheEnd(and(c, [7, -1])), [v[i]]);
    if (c0.length === 0) {
      c0 = [0];
    }
    if (c0[0] & 1) {
      c = xor(c, [0x98, 0xf2bc8e61]);
    }
    if (c0[0] & 2) {
      c = xor(c, [0x79, 0xb76d99e2]);
    }
    if (c0[0] & 4) {
      c = xor(c, [0xf3, 0x3e5fb3c4]);
    }
    if (c0[0] & 8) {
      c = xor(c, [0xae, 0x2eabe2a8]);
    }
    if (c0[0] & 16) {
      c = xor(c, [0x1e, 0x4f43e470]);
    }
  }
  return xor(c, [1]);
}

function add5zerosAtTheEnd(a) {
  a = [0].concat(a);
  for (var i = 1; i < a.length; i++) {
    a[i - 1] |= a[i] >>> 27;
    a[i] <<= 5;
  }
  return a;
}

function rShift(a, b) {
  // 35 >= b >= 0
  var t = a.slice(0);
  if (t.length === 0) {
    return [0];
  }
  if (b > 31) {
    t = a.slice(0, -1);
    b -= 32;
  }
  if (b === 0) {
    return t;
  }
  for (var i = t.length - 1; i > 0; i--) {
    t[i] >>>= b;
    t[i] |= (t[i - 1] & ((2 << (b + 1)) - 1)) << (32 - b);
  }
  t[0] >>>= b;
  if (t[0] === 0) {
    return t.slice(1);
  }
  return t;
}

function xor(a, b) {
  var t = a.length - b.length;
  var c = [];
  if (t > 0) {
    b = Array(t)
      .fill(0)
      .concat(b);
  } else if (t < 0) {
    a = Array(-t)
      .fill(0)
      .concat(a);
  }
  for (var i = 0; i < a.length; i++) {
    c.push(a[i] ^ b[i]);
  }
  return c;
}

function and(a, b) {
  var t = a.length - b.length;
  c = [];
  if (t >= 0) {
    for (var i = 0; i < b.length; i++) {
      c.push(a[i + t] & b[i]);
    }
  } else {
    for (var i = 0; i < a.length; i++) {
      c.push(a[i] & b[i - t]);
    }
  }
  return c;
}

function simplify(v) {
  if (v.length === 0) {
    return [0];
  }
  var i = 0;
  while (v[i] === 0) {
    i++;
  }
  var z = v.slice(i);
  if (z.length === 0) {
    z = [0];
  }
  return z;
}

alert(craftCashAddress(1, [1,8,120,1,8,120,1,8,120,1,8,120,1,8,120,1,8,120,9,100], true))
zquestz commented 6 years ago

This is now up for review -> https://github.com/theantnest/bccaddress/pull/26

zquestz commented 6 years ago

Done