diff --git a/Gruntfile.js b/Gruntfile.js index fa59b80..b058081 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -10,6 +10,7 @@ module.exports = function (grunt) { output: "./bitaddress.org.html", tokens: [ { token: "//biginteger.js", file: "./src/biginteger.js" }, + { token: "//bech32.js", file: "./src/bech32.js"}, { token: "//bitcoinjs-lib.js", file: "./src/bitcoinjs-lib.js" }, { token: "//bitcoinjs-lib.address.js", file: "./src/bitcoinjs-lib.address.js" }, { token: "//bitcoinjs-lib.base58.js", file: "./src/bitcoinjs-lib.base58.js" }, diff --git a/src/bech32.js b/src/bech32.js new file mode 100644 index 0000000..a23fd0f --- /dev/null +++ b/src/bech32.js @@ -0,0 +1,149 @@ +//https://raw.githubusercontent.com/bitcoinjs/bech32/e1536ebcc8e6471ae69813ac72e2f14f7656c483/index.js +'use strict' +var ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l' + +// pre-compute lookup table +var ALPHABET_MAP = {} +for (var z = 0; z < ALPHABET.length; z++) { + var x = ALPHABET.charAt(z) + + if (ALPHABET_MAP[x] !== undefined) throw new TypeError(x + ' is ambiguous') + ALPHABET_MAP[x] = z +} + +function polymodStep (pre) { + var b = pre >> 25 + return ((pre & 0x1FFFFFF) << 5) ^ + (-((b >> 0) & 1) & 0x3b6a57b2) ^ + (-((b >> 1) & 1) & 0x26508e6d) ^ + (-((b >> 2) & 1) & 0x1ea119fa) ^ + (-((b >> 3) & 1) & 0x3d4233dd) ^ + (-((b >> 4) & 1) & 0x2a1462b3) +} + +function prefixChk (prefix) { + var chk = 1 + for (var i = 0; i < prefix.length; ++i) { + var c = prefix.charCodeAt(i) + if (c < 33 || c > 126) throw new Error('Invalid prefix (' + prefix + ')') + + chk = polymodStep(chk) ^ (c >> 5) + } + chk = polymodStep(chk) + + for (i = 0; i < prefix.length; ++i) { + var v = prefix.charCodeAt(i) + chk = polymodStep(chk) ^ (v & 0x1f) + } + return chk +} + +function encode (prefix, words, LIMIT) { + LIMIT = LIMIT || 90 + if ((prefix.length + 7 + words.length) > LIMIT) throw new TypeError('Exceeds length limit') + + prefix = prefix.toLowerCase() + + // determine chk mod + var chk = prefixChk(prefix) + var result = prefix + '1' + for (var i = 0; i < words.length; ++i) { + var x = words[i] + if ((x >> 5) !== 0) throw new Error('Non 5-bit word') + + chk = polymodStep(chk) ^ x + result += ALPHABET.charAt(x) + } + + for (i = 0; i < 6; ++i) { + chk = polymodStep(chk) + } + chk ^= 1 + + for (i = 0; i < 6; ++i) { + var v = (chk >> ((5 - i) * 5)) & 0x1f + result += ALPHABET.charAt(v) + } + + return result +} + +function decode (str, LIMIT) { + LIMIT = LIMIT || 90 + if (str.length < 8) throw new TypeError(str + ' too short') + if (str.length > LIMIT) throw new TypeError('Exceeds length limit') + + // don't allow mixed case + var lowered = str.toLowerCase() + var uppered = str.toUpperCase() + if (str !== lowered && str !== uppered) throw new Error('Mixed-case string ' + str) + str = lowered + + var split = str.lastIndexOf('1') + if (split === -1) throw new Error('No separator character for ' + str) + if (split === 0) throw new Error('Missing prefix for ' + str) + + var prefix = str.slice(0, split) + var wordChars = str.slice(split + 1) + if (wordChars.length < 6) throw new Error('Data too short') + + var chk = prefixChk(prefix) + var words = [] + for (var i = 0; i < wordChars.length; ++i) { + var c = wordChars.charAt(i) + var v = ALPHABET_MAP[c] + if (v === undefined) throw new Error('Unknown character ' + c) + chk = polymodStep(chk) ^ v + + // not in the checksum? + if (i + 6 >= wordChars.length) continue + words.push(v) + } + + if (chk !== 1) throw new Error('Invalid checksum for ' + str) + return { prefix: prefix, words: words } +} + +function convert (data, inBits, outBits, pad) { + var value = 0 + var bits = 0 + var maxV = (1 << outBits) - 1 + + var result = [] + for (var i = 0; i < data.length; ++i) { + value = (value << inBits) | data[i] + bits += inBits + + while (bits >= outBits) { + bits -= outBits + result.push((value >> bits) & maxV) + } + } + + if (pad) { + if (bits > 0) { + result.push((value << (outBits - bits)) & maxV) + } + } else { + if (bits >= inBits) throw new Error('Excess padding') + if ((value << (outBits - bits)) & maxV) throw new Error('Non-zero padding') + } + + return result +} + +function toWords (bytes) { + return convert(bytes, 8, 5, true) +} + +function fromWords (words) { + return convert(words, 5, 8, false) +} + +// Modified the module exports to window variables +window.bech32 = { + decode: decode, + encode: encode, + toWords: toWords, + fromWords: fromWords +} \ No newline at end of file diff --git a/src/bitaddress-ui.html b/src/bitaddress-ui.html index 0d6f752..29806d1 100644 --- a/src/bitaddress-ui.html +++ b/src/bitaddress-ui.html @@ -104,6 +104,9 @@ +