include node_modules so release .zip is deployable

This commit is contained in:
2023-11-24 17:44:25 -05:00
parent 6c86cfe5d2
commit 8b11c41267
8963 changed files with 874175 additions and 1 deletions
+62
View File
@@ -0,0 +1,62 @@
# Changelog
All notable changes to this project will be documented in this file.
The project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
## 0.2.1 - 2022-09-27
### Fixed
- Fix encoding with uppercase prefix. Previously, such prefixes led to incorrect
checksum. With the fix, the prefix case is retained for the encoded string.
## 0.2.0 - 2021-11-21
No substantial changes compared to the 0.2.0-rc.0 release.
## 0.2.0-rc.0 - 2021-11-07
### Added
- Support [Bech32m encoding] for arbitrary data and Bitcoin addresses.
The new functionality is available on [the demo web page](https://slowli.github.io/bech32-buffer/).
## 0.1.3 - 2021-05-10
### Added
- Document TypeScript typings.
- Update readme to better document main package APIs.
### Internal improvements
- Update dev dependencies.
- Update Bootstrap / JS dependencies for the demo page.
## 0.1.2 - 2018-12-23
### Added
- Add TypeScript typings for the package.
### Internal improvements
- Update dev dependencies.
## 0.1.1 - 2018-12-15
### Added
- Test browser support and add a browser version of the package in the `dist` directory.
- Support Bitcoin addresses via `BitcoinAddress` class.
## 0.1.0 - 2017-07-23
The initial release of `bech32-buffer`.
[Bech32m encoding]: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+185
View File
@@ -0,0 +1,185 @@
# Bech32(m) Encoding for Arbitrary Buffers
[![Build status][workflow-image]][workflow-url]
[![Code coverage][coveralls-image]][coveralls-url]
[![Demo][demo-image]][demo-url]
[workflow-image]: https://github.com/slowli/bech32-buffer/workflows/Node.js%20CI/badge.svg?branch=master
[workflow-url]: https://github.com/slowli/bech32-buffer/actions
[coveralls-image]: https://img.shields.io/coveralls/slowli/bech32-buffer.svg
[coveralls-url]: https://coveralls.io/github/slowli/bech32-buffer
[demo-image]: https://img.shields.io/badge/demo-live-blue.svg
[demo-url]: https://slowli.github.io/bech32-buffer/
**Bech32** is a Bitcoin address format specified in [BIP 173][bip-173] and [BIP 350][bip-350].
Among its advantages are: better adaptability to QR codes and in voice conversations,
and improved error detection. This library generalizes Bech32 and its modified version
(Bech32m) to encode any reasonably short byte buffers.
## Usage
### Encoding data
```typescript
declare function encode(
prefix: string,
data: Uint8Array,
encoding: 'bech32' | 'bech32m' = 'bech32'
): string;
```
Encodes binary `data` with the specified human-readable `prefix` into a Bech32(m) string.
The case is preserved: if the prefix is uppercase, then the output will be uppercase
as well; otherwise, the output will be lowercase (including the case when the prefix does
not contain any letters).
#### Arguments
- **prefix:** string
Human-readable prefix to hint what kind of data Bech32 encodes. Must contain
ASCII chars in the range 33-126
- **data:** Uint8Array
Binary data to encode
- **encoding:** `bech32` or `bech32m`
Specifies whether to use the original Bech32 encoding from [BIP 173][bip-173]
or the modified encoding from [BIP 350][bip-350]; they differ
in how a checksum is computed. If omitted, the original encoding is used.
#### Return value
String containing:
1. `prefix`
2. `'1'` separator char
3. `data` encoded with the variant of base32 encoding used by Bech32, and
4. 6-char checksum calculated based on `prefix` and `data`
#### Example
```javascript
const bech32 = require('bech32-buffer');
const data = new Uint8Array(20);
bech32.encode('test', data);
// 'test1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqql6aptf'
```
### Decoding data
```typescript
declare function decode(message: string): {
prefix: string,
encoding: 'bech32' | 'bech32m',
data: Uint8Array
};
```
Extracts human-readable prefix and binary data from the Bech32-encoded string.
#### Arguments
- **data:** string
String to decode
#### Return value
An object with the following fields:
- **prefix:** string
Human-readable prefix
- **encoding:** `bech32` or `bech32m`
Encoding variant inferred from the checksum.
- **data:** Uint8Array
Binary data encoded into the input string
Decoding may fail for a variety of reasons (e.g., invalid checksum, or invalid
chars in the input). In this case, `decode()` throws an exception
with a descriptive message.
#### Example
```javascript
const bech32 = require('bech32-buffer');
const data = 'lost1qsyq7yqh9gk0szs5';
bech32.decode(data);
// {
// prefix: 'lost',
// encoding: 'bech32',
// data: Uint8Array([ 4, 8, 15, 16, 23, 42 ])
// }
```
### Bitcoin addresses
```typescript
declare class BitcoinAddress {
prefix: 'bc' | 'tb';
scriptVersion: number;
data: Uint8Array;
static decode(message: string): BitcoinAddress;
constructor(prefix: 'bc' | 'tb', scriptVersion: number, data: Uint8Array);
encode(): string;
type(): void | 'p2wsh' | 'p2wpkh';
}
```
Provides basic functionality to work with Bech32 encoding of Bitcoin addresses.
Addresses can be `decode`d from strings and `encode`d into strings.
It is also possible to check the `type` of an address. P2WSH and P2WPKH address
types are defined per [BIP 141]. Encoding constraints are defined per [BIP 173][bip-173]
and [BIP 350][bip-350].
#### Example
```javascript
const { BitcoinAddress } = require('bech32-buffer');
const address = BitcoinAddress.decode('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4');
// address.prefix === 'bc'
// address.scriptVersion === 0
// address.data.length === 20
// address.type() === 'p2wpkh'
```
## Use in Browsers
Use `dist/bech32-buffer.min.js` from the package distribution
or your favorite browserifier. In the first case,
the library will be available as a `bech32` global variable:
```html
<script src="bech32-buffer.min.js"></script>
<!-- later -->
<script>
bech32.encode('test', new Uint8Array(20));
</script>
```
Check out [the web demo](https://slowli.github.io/bech32-buffer/) to see how
**bech32-buffer** works in browser. It is also available in the `examples`
directory of the package.
## Acknowledgements
[BIP 173][bip-173] is authored by Pieter Wuille and Greg Maxwell and is licensed
under the 2-clause BSD license.
[BIP 350][bip-350] is authored by Pieter Wuille and is licensed
under the 2-clause BSD license.
There are at least 2 existing implementations of Bech32 for JavaScript:
- [The reference implementation][ref] by Pieter Wuille
- [Another implementation][bech32] available as the [`bech32` package][bech32-pkg]
Both implementations are Bitcoin-specific, and the reference implementation
is also not in the Npm / yarn package manager.
## License
**bech32-buffer** is available under [Apache-2.0 license](LICENSE).
[bip-173]: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
[bip-350]: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
[ref]: https://github.com/sipa/bech32/tree/master/ref/javascript
[bech32]: https://github.com/bitcoinjs/bech32
[bech32-pkg]: https://www.npmjs.com/package/bech32
[BIP 141]: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
+11
View File
@@ -0,0 +1,11 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.bech32 = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.createBitArray=createBitArray,exports.fromBits=fromBits,exports.toBits=toBits;function toBitArrayUnchecked(a){return a}function createBitArray(a){return toBitArrayUnchecked(new Uint8Array(a))}function convert(a,c,d,e,f){var g=(1<<e)-1,h=0,i=0,j=0;if(a.forEach(function(a){for(h=(h<<c)+a,i+=c;i>=e;)i-=e,d[j]=h>>i&g,j+=1}),f)0<i&&(d[j]=h<<e-i&g);else{if(i>=c)throw new Error("Excessive padding: ".concat(i," (max ").concat(c-1," allowed)"));if(0!=h%(1<<i))throw new Error("Non-zero padding")}}function toBits(a,b,c){if(8<b||1>b)throw new RangeError("Invalid bits per element; 1 to 8 expected");return convert(toBitArrayUnchecked(a),8,c,b,!0),c}function fromBits(a,b,c){if(8<b||1>b)throw new RangeError("Invalid bits per element; 1 to 8 expected");return convert(a,b,toBitArrayUnchecked(c),8,!1),c}
},{}],2:[function(require,module,exports){
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.CHECKSUM_LENGTH=void 0,exports.createChecksum=createChecksum,exports.decode=decode,exports.decodeWithPrefix=decodeWithPrefix,exports.detectCase=detectCase,exports.encode=encode,exports.expandPrefix=expandPrefix,exports.verifyChecksum=verifyChecksum;var _bitConverter=require("./bit-converter"),CHARSET="qpzry9x8gf2tvdw0s3jn54khce6mua7l",BECH32M_CHECKSUM=734539939,MIN_CHAR_CODE=33,MAX_CHAR_CODE=126,CHECKSUM_LENGTH=6;exports.CHECKSUM_LENGTH=CHECKSUM_LENGTH;var CHAR_LOOKUP=function(){for(var a=new Map,b=0;b<CHARSET.length;b+=1)a.set(CHARSET[b],b);return a}(),GEN=[996825010,642813549,513874426,1027748829,705979059];function polymod(a){return a.reduce(function(a,b){return GEN.reduce(function(b,c,d){return 0==(1&a>>25>>d)?b:b^c},(33554431&a)<<5^b)},1)}function expandPrefix(a,b){for(var c,d=0;d<a.length;d+=1)c=a.charCodeAt(d),b[d]=c>>5,b[d+a.length+1]=31&c;b[a.length]=0}function verifyChecksum(a){switch(polymod(a)){case 1:return"bech32";case BECH32M_CHECKSUM:return"bech32m";default:}}function createChecksum(a,b){var c;switch(b){case"bech32":c=1;break;case"bech32m":c=BECH32M_CHECKSUM;break;default:throw Error("Invalid encoding value: ".concat(b,"; expected bech32 or bech32m"));}for(var d,e=polymod(a)^c,f=0;f<CHECKSUM_LENGTH;f+=1)d=5*(5-f),a[a.length-CHECKSUM_LENGTH+f]=31&e>>d}function encode(a){return a.reduce(function(a,b){return a+CHARSET[b]},"")}function decode(a,b){for(var c,d=b||(0,_bitConverter.createBitArray)(a.length),e=0;e<a.length;e+=1){if(c=CHAR_LOOKUP.get(a[e]),void 0===c)throw new Error("Invalid char in message: ".concat(a[e]));d[e]=c}return d}function decodeWithPrefix(a,b){var c=b.length+2*a.length+1,d=(0,_bitConverter.createBitArray)(c);return expandPrefix(a,d.subarray(0,2*a.length+1)),decode(b,d.subarray(2*a.length+1)),d}function detectCase(a){for(var b,c=1<arguments.length&&void 0!==arguments[1]?arguments[1]:"message",d=!1,e=!1,f=0;f<a.length;f+=1){if(b=a.charCodeAt(f),b<MIN_CHAR_CODE||b>MAX_CHAR_CODE)throw new TypeError("Invalid char in ".concat(c,": ").concat(b,"; ")+"should be in ASCII range ".concat(MIN_CHAR_CODE,"-").concat(MAX_CHAR_CODE));e=e||65<=b&&90>=b,d=d||97<=b&&122>=b}if(d&&e)throw new TypeError("Mixed-case ".concat(c));else return e?"upper":d?"lower":null}
},{"./bit-converter":1}],3:[function(require,module,exports){
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.BitcoinAddress=void 0,exports.decode=decode,exports.decodeTo5BitArray=decodeTo5BitArray,exports.encode=encode,exports.encode5BitArray=encode5BitArray,exports.from5BitArray=from5BitArray,exports.to5BitArray=to5BitArray;var _bitConverter=require("./bit-converter"),_encoding=require("./encoding");function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function _defineProperties(a,b){for(var c,d=0;d<b.length;d++)c=b[d],c.enumerable=c.enumerable||!1,c.configurable=!0,"value"in c&&(c.writable=!0),Object.defineProperty(a,c.key,c)}function _createClass(a,b,c){return b&&_defineProperties(a.prototype,b),c&&_defineProperties(a,c),Object.defineProperty(a,"prototype",{writable:!1}),a}var MAX_ENC_LENGTH=90;function to5BitArray(a,b){var c=Math.ceil(8*a.length/5),d=b||(0,_bitConverter.createBitArray)(c);return(0,_bitConverter.toBits)(a,5,d)}function from5BitArray(a,b){var c=Math.floor(5*a.length/8),d=b||new Uint8Array(c);return(0,_bitConverter.fromBits)(a,5,d)}function encode5BitArray(a,b){var c,d=2<arguments.length&&void 0!==arguments[2]?arguments[2]:"bech32",e=2*a.length+1+b.length+_encoding.CHECKSUM_LENGTH;if(e-a.length>MAX_ENC_LENGTH)throw new Error("Message to be produced is too long (max ".concat(MAX_ENC_LENGTH," supported)"));var f=null!==(c=(0,_encoding.detectCase)(a,"prefix"))&&void 0!==c?c:"lower",g=(0,_bitConverter.createBitArray)(e);(0,_encoding.expandPrefix)(a.toLowerCase(),g.subarray(0,2*a.length+1));var h=g.subarray(2*a.length+1,g.length-_encoding.CHECKSUM_LENGTH);h.set(b),(0,_encoding.createChecksum)(g,d);var i=(0,_encoding.encode)(g.subarray(2*a.length+1));return"upper"===f&&(i=i.toUpperCase()),"".concat(a,"1").concat(i)}function encode(a,b){var c=2<arguments.length&&arguments[2]!==void 0?arguments[2]:"bech32";return encode5BitArray(a,to5BitArray(b),c)}function decodeTo5BitArray(a){if(a.length>MAX_ENC_LENGTH)throw new TypeError("Message too long; max ".concat(MAX_ENC_LENGTH," expected"));(0,_encoding.detectCase)(a);var b=a.toLowerCase(),c=b.lastIndexOf("1");if(0>c)throw new Error("No separator char (\"1\") found");if(c>a.length-_encoding.CHECKSUM_LENGTH-1)throw new Error("Data part of the message too short (at least ".concat(_encoding.CHECKSUM_LENGTH," chars expected)"));var d=b.substring(0,c),e=(0,_encoding.decodeWithPrefix)(d,b.substring(c+1)),f=(0,_encoding.verifyChecksum)(e);if(f===void 0)throw new Error("Invalid checksum");return{prefix:d,encoding:f,data:e.subarray(2*d.length+1,e.length-_encoding.CHECKSUM_LENGTH)}}function decode(a){var b=decodeTo5BitArray(a),c=b.prefix,d=b.encoding,e=b.data;return{prefix:c,encoding:d,data:from5BitArray(e)}}var BitcoinAddress=function(){var a=Math.ceil;function b(a,c,d){if(_classCallCheck(this,b),"bc"!==a&&"tb"!==a)throw new Error("Invalid human-readable prefix, \"bc\" or \"tb\" expected");if(0>c||16<c)throw new RangeError("Invalid scriptVersion, value in range [0, 16] expected");if(2>d.length||40<d.length)throw new RangeError("Invalid script length: expected 2 to 40 bytes");if(0===c&&20!==d.length&&32!==d.length)throw new Error("Invalid v0 script length: expected 20 or 32 bytes");this.prefix=a,this.scriptVersion=c,this.data=d}return _createClass(b,[{key:"type",value:function type(){if(0===this.scriptVersion)switch(this.data.length){case 20:return"p2wpkh";case 32:return"p2wsh";default:}}},{key:"encode",value:function(){var b=a(8*this.data.length/5),c=(0,_bitConverter.createBitArray)(b+1);c[0]=this.scriptVersion,to5BitArray(this.data,c.subarray(1));var d=0===this.scriptVersion?"bech32":"bech32m";return encode5BitArray(this.prefix,c,d)}}],[{key:"decode",value:function(a){var b=decodeTo5BitArray(a),c=b.prefix,d=b.data,e=b.encoding;if("bc"!==c&&"tb"!==c)throw new Error("Invalid human-readable prefix, \"bc\" or \"tb\" expected");var f=d[0];if(0===f&&"bech32"!==e)throw Error("Unexpected encoding ".concat(e," used for version 0 script"));if(0<f&&"bech32m"!==e)throw Error("Unexpected encoding ".concat(e," used for version ").concat(f," script"));return new this(c,f,from5BitArray(d.subarray(1)))}}]),b}();exports.BitcoinAddress=BitcoinAddress;
},{"./bit-converter":1,"./encoding":2}]},{},[3])(3)
});
+346
View File
@@ -0,0 +1,346 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="keywords" content="bech32, base32, bitcoin">
<meta name="description" content="Demo of bech32-buffer package capabilities">
<title>Bech32 Demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<style>
textarea {
resize: none;
height: 80px;
}
input[type=number] {
max-width: 5rem;
}
.content {
max-width: 1080px;
margin: 0 auto;
padding: 1rem;
}
.body-header {
padding: 1rem;
background: #f4f4f4;
border-bottom: 1px solid #eee;
margin-bottom: 2rem;
}
.page-footer {
margin: 4rem -0.5rem 0 -0.5rem;
padding: 2rem 0 1.5rem;
border-top: 1px solid rgba(0,0,0,0.15);
}
.align-height {
min-height: 152px;
}
.card-header.align-header-height {
height: 49px;
}
.qr-placeholder {
display: inline-block;
width: 132px;
height: 132px;
background: rgba(0,0,0,0.15);
color: white;
}
.qr-placeholder:before {
content: '?';
font-size: 72px;
line-height: 1;
height: 72px;
position: relative;
top: 50%;
margin-top: -36px;
display: inline-block;
}
#encode:after {
content: ' ↓';
}
#decode:after {
content: ' ↑';
}
@media (min-width: 768px) {
#encode:after {
content: ' →';
}
#decode:after {
content: ' ←';
}
}
</style>
</head>
<body>
<header class="body-header">
<div class="content">
<h1>bech32-buffer</h1>
<p class="lead">Bech32 is a Bitcoin address format proposed by Pieter Wuille and Greg Maxwell in <a href="https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki">BIP 173</a> and later amended by <a href="https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki">BIP 350</a>.
Besides Bitcoin addresses, Bech32 can encode any short binary data.</p>
</div>
</header>
<div class="content">
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="card">
<div class="card-header align-header-height">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="encode-btc-tab" data-bs-toggle="tab" data-bs-target="#encode-btc" type="button" role="tab" aria-controls="home" aria-selected="true">BTC Address</button>
</li>
<li class="nav-item">
<button class="nav-link" id="encode-data-tab" data-bs-toggle="tab" data-bs-target="#encode-data" type="button" role="tab" aria-controls="home" aria-selected="false">Data</button>
</li>
</ul>
</div>
<div class="card-block p-2">
<form>
<div class="align-height tab-content">
<div class="tab-pane" id="encode-data" role="tabpanel" aria-labelledby="encode-data-tab">
<div class="mb-3 row">
<label for="hrp" class="col-3 col-form-label">Tag</label>
<div class="col-9">
<input type="text" id="hrp" class="form-control" placeholder="Tag"/>
</div>
<div class="col-12 form-text text-muted small">A short tag describing what a Bech32-encoded string is used for.</div>
</div>
<div class="mb-3 row">
<label class="col-3 col-form-label">Encoding</label>
<div class="col-9">
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="encoding-options" id="encoding-bech32" autocomplete="off">
<label for="encoding-bech32" class="btn btn-outline-primary" title="Original encoding proposed in BIP 173">Bech32</label>
<input type="radio" class="btn-check" name="encoding-options" id="encoding-bech32m" autocomplete="off" checked>
<label for="encoding-bech32m" class="btn btn-outline-primary" title="Amended encoding from BIP 350">Bech32m</label>
</div>
</div>
</div>
</div>
<div class="tab-pane active" id="encode-btc" role="tabpanel" aria-labelledby="encode-btc-tab">
<div class="mb-3 row">
<label class="col-3 col-form-label">Network</label>
<div class="col-9">
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="btc-network-options" id="btc-mainnet" autocomplete="off" checked>
<label for="btc-mainnet" class="btn btn-outline-primary">Mainnet</label>
<input type="radio" class="btn-check" name="btc-network-options" id="btc-testnet" autocomplete="off">
<label for="btc-testnet" class="btn btn-outline-primary">Testnet</label>
</div>
</div>
</div>
<div class="mb-3 row">
<label for="script-version" class="col-3 col-form-label">Script ver.</label>
<div class="col-9">
<input type="number" id="script-version" min="0" max="16" size="6" value="0">
</div>
<div class="col-12 form-text">Note that version 0 scripts should have length 20 or 32 bytes.</div>
</div>
</div>
</div>
<div class="mb-4">
<label for="data" class="form-label">Data</label>
<textarea id="data" class="form-control" placeholder="Hex data"></textarea>
<div class="form-text text-muted small">Binary data (in hex) to be encoded into Bech32.</div>
</div>
<button type="button" id="encode" class="btn btn-primary" data-container="body" data-toggle="popover" data-placement="bottom" data-content="">Encode</button>
<a id="random" title="Generate random data" class="ms-3" href="#">Random data</a>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<h3 class="card-header align-header-height">Bech32</h3>
<div id="qrcode" class="card-img-top text-center py-2 align-height" title="QR code with the latest encoded or decoded message"><div class="qr-placeholder"></div></div>
<div class="card-block p-2">
<form>
<div class="mb-4">
<label for="encoded" class="form-label">Encoded data</label>
<textarea id="encoded" class="form-control" placeholder="Bech32 string"></textarea>
<div class="form-text text-muted small">Encoded Bech32 string.</div>
</div>
<button type="button" id="decode" class="btn btn-primary" data-container="body" data-toggle="popover" data-placement="top" data-content="">Decode</button>
</form>
</div>
</div>
</div>
</div>
<footer class="page-footer small text-muted">
<span class="copyright">© 2021 Alex Ostrovski</span>
<div class="float-none float-sm-end">
<a href="https://github.com/slowli/bech32-buffer">GitHub</a>&nbsp;
<a href="https://npmjs.com/package/bech32-buffer">Npm</a>&nbsp;
<a href="../dist/bech32-buffer.min.js" download="bech32-buffer.min.js">Browser version</a>
</div>
</footer>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
<script src="qrcode.min.js"></script>
<script src="../dist/bech32-buffer.min.js"></script>
<script>
/* eslint-env browser, jquery */
/* global bech32 */
(function () {
/**
* Converts a byte array into a hex string.
*/
function toHexString(buffer) {
return buffer.reduce((s, byte) => {
let hex = byte.toString(16);
if (hex.length === 1) hex = '0' + hex;
return s + hex;
}, '');
}
/**
* Converts a hex string into a byte array.
*
* @returns {?Uint8Array}
* decoded array, or `null` if the supplied string is not a valid hex string
*/
function fromHexString(str) {
if (str.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(str)) {
return null;
}
let buffer = new Uint8Array(str.length / 2);
for (let i = 0; i < buffer.length; i++) {
buffer[i] = parseInt(str.substr(2 * i, 2), 16);
}
return buffer;
}
const hrp = $('#hrp');
const data = $('#data');
const encoded = $('#encoded');
const encodeBtn = $('#encode');
const decodeBtn = $('#decode');
const randomBtn = $('#random');
const scriptVersion = $('#script-version');
const timeouts = {};
function report(type, btn, message) {
const id = btn[0].id;
const timeout = timeouts[id];
if (timeout) clearTimeout(timeout);
btn.attr('data-bs-content', '<span class="text-' + type + '">' + message + '</span>');
btn[0].popover.show();
timeouts[id] = setTimeout(function() {
btn[0].popover.hide();
delete timeouts[id];
}, 3000);
}
encodeBtn.click(function() {
let enc;
const btc = $('#encode-btc').hasClass('active');
try {
const buffer = fromHexString(data.val());
if (!buffer) throw new TypeError('Invalid hex encoding of data');
if (btc) {
const prefix = $('#btc-mainnet').prop('checked') ? 'bc' : 'tb';
const version = parseInt(scriptVersion.val());
const btcAddress = new bech32.BitcoinAddress(prefix, version, buffer);
enc = btcAddress.encode();
} else {
const encoding = $('#encoding-bech32').prop('checked') ? 'bech32' : 'bech32m';
enc = bech32.encode(hrp.val(), buffer, encoding);
}
encoded.val(enc);
} catch (e) {
report('danger', encodeBtn, 'Error: ' + e.message);
return;
}
report('success', encodeBtn, 'Data encoded (' + enc.length + ' chars)');
const qr = qrcode(0, 'M');
qr.addData(enc.toUpperCase(), 'Alphanumeric');
qr.make();
$('#qrcode').html(qr.createSvgTag(4, 0));
});
decodeBtn.click(function() {
let dec;
const btc = $('#encode-btc').hasClass('active');
try {
if (btc) {
dec = bech32.BitcoinAddress.decode(encoded.val());
scriptVersion.val(dec.scriptVersion);
var selectedNetwork = (dec.prefix === 'bc') ? $('#btc-mainnet') : $('#btc-testnet');
$('input[name=btc-network-options]').parent().removeClass('active');
selectedNetwork.prop('checked', true);
selectedNetwork.parent().addClass('active');
} else {
dec = bech32.decode(encoded.val());
hrp.val(dec.prefix);
var selectedEncoding = (dec.encoding === 'bech32') ? $('#encoding-bech32') : $('#encoding-bech32m');
$('input[name=encoding-options]').parent().removeClass('active');
selectedEncoding.prop('checked', true);
selectedEncoding.parent().addClass('active');
}
data.val(toHexString(dec.data));
} catch (e) {
report('danger', decodeBtn, 'Error: ' + e.message);
return;
}
report('success', decodeBtn, 'Data decoded (' + dec.data.length + ' bytes)');
const qr = qrcode(0, 'M');
qr.addData(encoded.val().toUpperCase(), 'Alphanumeric');
qr.make();
$('#qrcode').html(qr.createSvgTag(4, 0));
});
$('#encode,#decode').each(function () {
this.popover = new bootstrap.Popover(this, {
trigger: 'manual',
html: true
});
});
randomBtn.click(function(e) {
e.preventDefault();
const btc = $('#encode-btc').hasClass('active');
// Not secure! Don't use in production.
const length = (btc && parseInt(scriptVersion.val()) === 0)
? (Math.random() > 0.5 ? 32 : 20)
: 8 + Math.floor(Math.random() * 32);
const buffer = new Uint8Array(length);
for (let i = 0; i < length; i++) {
buffer[i] = Math.floor(Math.random() * 256);
}
data.val(toHexString(buffer));
});
scriptVersion.change(function() {
const version = parseInt(scriptVersion.val());
if (version < 0) scriptVersion.val(0);
if (version > 16) scriptVersion.val(16);
});
})();
</script>
</body>
</html>
+1991
View File
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
/**
* Virtual type for an array in which each element represents 5 bits.
*/
export declare type FiveBitArray = Uint8Array;
// As TypeScript (unlike Flow) always uses structural typing for determining type compatibility,
// there seems to be no way to express this better.
/**
* Encoding from the Bech32 family used during encoding / decoding.
*/
export declare type Encoding = 'bech32' | 'bech32m';
/**
* Converts a Uint8Array into a Uint8Array variant, in which each element
* encodes 5 bits of the original byte array.
*
* @param src
* Input to convert
* @param dst
* Optional output buffer. If specified, the sequence of 5-bit chunks will be written there;
* if not specified, the output buffer will be created from scratch. The length
* of `outBuffer` is not checked.
* @returns
* Output buffer consisting of 5-bit chunks
*/
export declare function to5BitArray(src: Uint8Array, dst?: FiveBitArray): FiveBitArray;
/**
* Converts a sequence of 5-bit chunks into an ordinary Uint8Array.
*
* @param src
* Input to convert
* @param dst
* Optional output buffer. If specified, the converted bytes will be written there;
* if not specified, the output buffer will be created from scratch. The length
* of `outBuffer` is not checked.
* @returns
* Output buffer
*/
export declare function from5BitArray(src: FiveBitArray, dst?: Uint8Array): Uint8Array;
/**
* Encodes binary data into Bech32 encoding.
*
* The case is preserved: if the prefix is uppercase, then the output will be uppercase
* as well; otherwise, the output will be lowercase (including the case when the prefix does
* not contain any letters).
*
* Ordinarily, you may want to use `encode` because it converts
* binary data to an array of 5-bit integers automatically.
*
* @param prefix
* Human-readable prefix to place at the beginning of the encoding
* @param data
* Array of 5-bit integers with data to encode
* @param encoding
* Encoding to use; influences the checksum computation. If not specified,
* Bech32 encoding will be used.
* @returns
* Bech32(m) encoding of data in the form `<prefix>1<base32 of data><checksum>`
* @throws If the prefix is mixed-case or contains chars that are not eligible for Bech32 encoding
*/
export declare function encode5BitArray(
prefix: string,
data: FiveBitArray,
encoding?: Encoding
): string;
/**
* Result of a decoding operation.
*/
export interface DecodeResult<T = Uint8Array> {
/** Human-readable prefix. */
prefix: string;
/** Variation of the Bech32 encoding inferred from the checksum. */
encoding: Encoding;
/** Decoded data. */
data: T;
}
/**
* Decodes data from Bech32 encoding into an array of 5-bit integers.
*
* Ordinarily, you may want to use `decode` because it automatically
* converts the array of 5-bit integers into an ordinary `Uint8Array`.
*
* @param message
* Bech32-encoded message
* @returns
* Decoded object with `prefix` and `data` fields, which contain the human-readable
* prefix and the array of 5-bit integers respectively.
*/
export declare function decodeTo5BitArray(message: string): DecodeResult<FiveBitArray>;
/**
* Encodes binary data into Bech32 encoding.
*
* The case is preserved: if the prefix is uppercase, then the output will be uppercase
* as well; otherwise, the output will be lowercase (including the case when the prefix does
* not contain any letters).
*
* @param prefix
* Human-readable prefix to place at the beginning of the encoding
* @param data
* Binary data to encode
* @param encoding
* Encoding to use; influences the checksum computation. If not specified,
* Bech32 encoding will be used.
* @returns
* Bech32 encoding of data in the form `<prefix>1<base32 of data><checksum>`
* @throws If the prefix is mixed-case or contains chars that are not eligible for Bech32 encoding
*/
export declare function encode(
prefix: string,
data: Uint8Array,
encoding?: Encoding
): string;
/**
* Decodes data from Bech32 encoding into an array of 5-bit integers.
*
* @param message
* Bech32-encoded message
* @returns
* Decoded object with `prefix` and `data` fields, which contain the human-readable
* prefix and the decoded binary data respectively.
*/
export declare function decode(message: string): DecodeResult;
/**
* Bitcoin address.
*/
export declare class BitcoinAddress {
/**
* Human-readable prefix. Equals `'bc'` (for mainnet addresses)
* or `'tb'` (for testnet addresses).
*/
prefix: 'bc' | 'tb';
/**
* Script version. An integer between 0 and 16 (inclusive).
*/
scriptVersion: number;
/**
* Script data. A byte string with length 2 to 40 (inclusive).
*/
data: Uint8Array;
/**
* Decodes a Bitcoin address from a Bech32 string.
* As per BIP 350, the original encoding is expected for version 0 scripts, while
* other script versions expect the modified encoding.
*
* This method does not check whether the address is well-formed;
* use `type()` method on returned address to find that out.
*/
static decode(message: string): BitcoinAddress;
/**
* Creates a new address based on provided data.
*
* Validation is performed on the fields as specified in their description (e.g.,
* it is checked that `scriptVersion` is between 0 and 16). Additionally,
* for `scriptVersion == 0` it is checked that `data` is either 20 or 32 bytes long.
*
* @throws {Error} If provided fields do not pass validation.
*/
constructor(prefix: 'bc' | 'tb', scriptVersion: number, data: Uint8Array);
/**
* Encodes this address in Bech32 or Bech32m format, depending on the script version.
* Version 0 scripts are encoded using original Bech32 encoding as per BIP 173,
* while versions 1-16 are encoded using the modified encoding as per BIP 350.
*/
encode(): string;
/**
* Guesses the address type based on its internal structure.
*/
type(): void | 'p2wsh' | 'p2wpkh';
}
+115
View File
@@ -0,0 +1,115 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createBitArray = createBitArray;
exports.fromBits = fromBits;
exports.toBits = toBits;
/* eslint-disable no-unused-vars */
/**
* Virtual type for bit arrays, i.e., arrays in which each element contains
* an integer in range `[0, 1 << L)`, where `1 <= L <= 8`.
*/
/* eslint-enable no-unused-vars */
/**
* Performs unchecked conversion from `Uint8Array` to `BitArray`.
* This function is translated as the indentity operation by Babel; it's needed purely
* for Flow type checks.
*
* @param {Uint8Array} src
* array to convert
* @returns {Uint8Array}
* `src` interpreted as a `BitArray` with the specified bitness
*
* @api private
*/
function toBitArrayUnchecked(src) {
return src;
}
/**
* Creates a new array with specified bitness.
*
* @param {number} len
* length of the created array
* @returns {Uint8Array}
*
* @api private
*/
function createBitArray(len) {
return toBitArrayUnchecked(new Uint8Array(len));
}
/**
* Converts an array from one number of bits per element to another.
*
* @api private
*/
function convert(src, srcBits, dst, dstBits, pad) {
var mask = (1 << dstBits) - 1;
var acc = 0;
var bits = 0;
var pos = 0;
src.forEach(function (b) {
// Pull next bits from the input buffer into accumulator.
acc = (acc << srcBits) + b;
bits += srcBits; // Push into the output buffer while there are enough bits in the accumulator.
while (bits >= dstBits) {
bits -= dstBits;
dst[pos] = acc >> bits & mask;
pos += 1;
}
});
if (pad) {
if (bits > 0) {
// `dstBits - rem.bits` is the number of trailing zero bits needed to be appended
// to accumulator bits to get the trailing bit group.
dst[pos] = acc << dstBits - bits & mask;
}
} else {
// Truncate the remaining padding, but make sure that it is zeroed and not
// overly long first.
if (bits >= srcBits) {
throw new Error("Excessive padding: ".concat(bits, " (max ").concat(srcBits - 1, " allowed)"));
}
if (acc % (1 << bits) !== 0) {
throw new Error('Non-zero padding');
}
}
}
/**
* Encodes a `Uint8Array` buffer as an array with a lesser number of bits per element.
*
* @api private
*/
function toBits(src, bits, dst) {
if (bits > 8 || bits < 1) {
throw new RangeError('Invalid bits per element; 1 to 8 expected');
} // `BitArray<8>` is equivalent to `Uint8Array`; unfortunately, Flow
// has problems expressing this, so the explicit conversion is performed here.
convert(toBitArrayUnchecked(src), 8, dst, bits, true);
return dst;
}
function fromBits(src, bits, dst) {
if (bits > 8 || bits < 1) {
throw new RangeError('Invalid bits per element; 1 to 8 expected');
}
convert(src, bits, toBitArrayUnchecked(dst), 8, false);
return dst;
}
+217
View File
@@ -0,0 +1,217 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CHECKSUM_LENGTH = void 0;
exports.createChecksum = createChecksum;
exports.decode = decode;
exports.decodeWithPrefix = decodeWithPrefix;
exports.detectCase = detectCase;
exports.encode = encode;
exports.expandPrefix = expandPrefix;
exports.verifyChecksum = verifyChecksum;
var _bitConverter = require("./bit-converter");
// Alphabet for Bech32
var CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; // Checksum constant for Bech32m.
var BECH32M_CHECKSUM = 0x2bc830a3; // Minimum char code that could be present in the encoded message
var MIN_CHAR_CODE = 33; // Maximum char code that could be present in the encoded message
var MAX_CHAR_CODE = 126;
var CHECKSUM_LENGTH = 6; // Reverse lookup for characters
exports.CHECKSUM_LENGTH = CHECKSUM_LENGTH;
var CHAR_LOOKUP = function () {
var lookup = new Map();
for (var i = 0; i < CHARSET.length; i += 1) {
lookup.set(CHARSET[i], i);
}
return lookup;
}(); // Poly generators
var GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
function polymod(values) {
return values.reduce(function (checksum, value) {
var bits = checksum >> 25;
var newChecksum = (checksum & 0x1ffffff) << 5 ^ value;
return GEN.reduce(function (chk, gen, i) {
return (bits >> i & 1) === 0 ? chk : chk ^ gen;
}, newChecksum);
},
/* initial checksum */
1);
}
/**
* Expands a prefix into the specified output buffer.
*/
function expandPrefix(prefix, outBuffer) {
for (var i = 0; i < prefix.length; i += 1) {
var code = prefix.charCodeAt(i);
outBuffer[i] = code >> 5;
outBuffer[i + prefix.length + 1] = code & 31;
}
outBuffer[prefix.length] = 0;
}
/**
* Verifies the checksum for a particular buffer.
*/
function verifyChecksum(buffer) {
switch (polymod(buffer)) {
case 1:
return 'bech32';
case BECH32M_CHECKSUM:
return 'bech32m';
default:
return undefined;
}
}
/**
* Creates a checksum for a buffer and writes it to the last 6 5-bit groups
* of the buffer.
*/
function createChecksum(buffer, encoding) {
var checksumConstant;
switch (encoding) {
case 'bech32':
checksumConstant = 1;
break;
case 'bech32m':
checksumConstant = BECH32M_CHECKSUM;
break;
default:
throw Error("Invalid encoding value: ".concat(encoding, "; expected bech32 or bech32m"));
}
var mod = polymod(buffer) ^ checksumConstant;
for (var i = 0; i < CHECKSUM_LENGTH; i += 1) {
var shift = 5 * (5 - i);
buffer[buffer.length - CHECKSUM_LENGTH + i] = mod >> shift & 31;
}
}
/**
* Encodes an array of 5-bit groups into a string.
*
* @param {Uint8Array} buffer
* @returns {string}
*
* @api private
*/
function encode(buffer) {
return buffer.reduce(function (acc, bits) {
return acc + CHARSET[bits];
}, '');
}
/**
* Decodes a string into an array of 5-bit groups.
*
* @param {string} message
* @param {Uint8Array} [dst]
* Optional array to write the output to. If not specified, the array is created.
* @returns {Uint8Array}
* Array with the result of decoding
*
* @throws {Error}
* if there are characters in `message` not present in the encoding alphabet
*
* @api private
*/
function decode(message, dst) {
var realDst = dst || (0, _bitConverter.createBitArray)(message.length);
for (var i = 0; i < message.length; i += 1) {
var idx = CHAR_LOOKUP.get(message[i]);
if (idx === undefined) {
throw new Error("Invalid char in message: ".concat(message[i]));
}
realDst[i] = idx;
}
return realDst;
}
/**
* Decodes a string and a human-readable prefix into an array of 5-bit groups.
* The prefix is expanded as specified by Bech32.
*
* @param {string} prefix
* @param {string} message
* @returns {Uint8Array}
* Array with the result of decoding
*
* @api private
*/
function decodeWithPrefix(prefix, message) {
var len = message.length + 2 * prefix.length + 1;
var dst = (0, _bitConverter.createBitArray)(len);
expandPrefix(prefix, dst.subarray(0, 2 * prefix.length + 1));
decode(message, dst.subarray(2 * prefix.length + 1));
return dst;
}
/**
* Detects the character case used in `message`. If the message doesn't
* contain either lower-case or upper-case chars, returns `null`.
*
* @param {string} message
* @param {string} messageDescription
* Human-readable description of the message to put into an error message, should any occur
* @returns {'lower'|'upper'|null}
* @throws if the message contains both lowercase and uppercase chars,
* or contains chars not valid for Bech32(m) encoding
*/
function detectCase(message) {
var messageDescription = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'message';
var hasLowerCase = false;
var hasUpperCase = false;
for (var i = 0; i < message.length; i += 1) {
var ord = message.charCodeAt(i); // 3. Allowed chars in the encoding
if (ord < MIN_CHAR_CODE || ord > MAX_CHAR_CODE) {
throw new TypeError("Invalid char in ".concat(messageDescription, ": ").concat(ord, "; ") + "should be in ASCII range ".concat(MIN_CHAR_CODE, "-").concat(MAX_CHAR_CODE));
}
hasUpperCase = hasUpperCase || ord >= 65 && ord <= 90;
hasLowerCase = hasLowerCase || ord >= 97 && ord <= 122;
}
if (hasLowerCase && hasUpperCase) {
throw new TypeError("Mixed-case ".concat(messageDescription));
} else if (hasUpperCase) {
return 'upper';
} else if (hasLowerCase) {
return 'lower';
} else {
return null;
}
}
+349
View File
@@ -0,0 +1,349 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.BitcoinAddress = void 0;
exports.decode = decode;
exports.decodeTo5BitArray = decodeTo5BitArray;
exports.encode = encode;
exports.encode5BitArray = encode5BitArray;
exports.from5BitArray = from5BitArray;
exports.to5BitArray = to5BitArray;
var _bitConverter = require("./bit-converter");
var _encoding = require("./encoding");
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
// Maximum encoded message length
var MAX_ENC_LENGTH = 90;
/**
* Converts a Uint8Array into a Uint8Array variant, in which each element
* encodes 5 bits of the original byte array.
*
* @param {Uint8Array} src
* Input to convert
* @param {?Uint8Array} dst
* Optional output buffer. If specified, the sequence of 5-bit chunks will be written there;
* if not specified, the output buffer will be created from scratch. The length
* of `outBuffer` is not checked.
* @returns {Uint8Array}
* Output buffer consisting of 5-bit chunks
*
* @api public
*/
function to5BitArray(src, dst) {
var len = Math.ceil(src.length * 8 / 5);
var realDst = dst || (0, _bitConverter.createBitArray)(len);
return (0, _bitConverter.toBits)(src, 5, realDst);
}
function from5BitArray(src, dst) {
var len = Math.floor(src.length * 5 / 8);
var realDst = dst || new Uint8Array(len);
return (0, _bitConverter.fromBits)(src, 5, realDst);
}
/**
* Encodes binary data into Bech32 encoding.
*
* The case is preserved: if the prefix is uppercase, then the output will be uppercase
* as well; otherwise, the output will be lowercase (including the case when the prefix does
* not contain any letters).
*
* Ordinarily, you may want to use [`encode`](#encode) because it converts
* binary data to an array of 5-bit integers automatically.
*
* @param {string} prefix
* Human-readable prefix to place at the beginning of the encoding
* @param {Uint8Array} data
* Array of 5-bit integers with data to encode
* @param {Encoding} encoding
* Encoding to use; influences the checksum computation. If not specified,
* Bech32 encoding will be used.
* @returns {string}
* Bech32 encoding of data in the form `<prefix>1<base32 of data><checksum>`
* @throws If the prefix is mixed-case or contains chars that are not eligible for Bech32 encoding
*
* @api public
*/
function encode5BitArray(prefix, data) {
var _detectCase;
var encoding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'bech32';
// 1. Allocate buffer for all operations
var len = 2 * prefix.length + 1 // expanded prefix
+ data.length // five-bit data encoding
+ _encoding.CHECKSUM_LENGTH; // checksum
if (len - prefix.length > MAX_ENC_LENGTH) {
throw new Error("Message to be produced is too long (max ".concat(MAX_ENC_LENGTH, " supported)"));
}
var prefixCase = (_detectCase = (0, _encoding.detectCase)(prefix, 'prefix')) !== null && _detectCase !== void 0 ? _detectCase : 'lower';
var buffer = (0, _bitConverter.createBitArray)(len); // 2. Expand the human-readable prefix into the beginning of the buffer
(0, _encoding.expandPrefix)(prefix.toLowerCase(), buffer.subarray(0, 2 * prefix.length + 1)); // 3. Copy `data` into the output
var dataBuffer = buffer.subarray(2 * prefix.length + 1, buffer.length - _encoding.CHECKSUM_LENGTH);
dataBuffer.set(data); // 4. Create the checksum
(0, _encoding.createChecksum)(buffer, encoding); // 5. Convert into string
var encoded = (0, _encoding.encode)(buffer.subarray(2 * prefix.length + 1));
if (prefixCase === 'upper') {
encoded = encoded.toUpperCase();
}
return "".concat(prefix, "1").concat(encoded);
}
/**
* Encodes binary data into Bech32 encoding.
*
* The case is preserved: if the prefix is uppercase, then the output will be uppercase
* as well; otherwise, the output will be lowercase (including the case when the prefix does
* not contain any letters).
*
* @param {string} prefix
* Human-readable prefix to place at the beginning of the encoding
* @param {Uint8Array} data
* Binary data to encode
* @param {Encoding} encoding
* Encoding to use; influences the checksum computation. If not specified,
* Bech32 encoding will be used.
* @returns {string}
* Bech32 encoding of data in the form `<prefix>1<base32 of data><checksum>`
* @throws If the prefix is mixed-case or contains chars that are not eligible for Bech32 encoding
*
* @api public
*/
function encode(prefix, data) {
var encoding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'bech32';
return encode5BitArray(prefix, to5BitArray(data), encoding);
}
/**
* Decodes data from Bech32 encoding into an array of 5-bit integers.
*
* Ordinarily, you may want to use [`decode`](#decode) because it automatically
* converts the array of 5-bit integers into an ordinary `Uint8Array`.
*
* @param {string} message
* Bech32-encoded message
* @returns {DecodeResult<FiveBitArray>}
* Decoded object with `prefix` and `data` fields, which contain the human-readable
* prefix and the array of 5-bit integers respectively.
*
* @api public
*/
function decodeTo5BitArray(message) {
// Check preconditions
// 1. Message length
if (message.length > MAX_ENC_LENGTH) {
throw new TypeError("Message too long; max ".concat(MAX_ENC_LENGTH, " expected"));
} // 2. Mixed case
(0, _encoding.detectCase)(message); // we don't care about the result, only about checks.
var lowerCaseMsg = message.toLowerCase(); // 4. Existence of the separator char
var sepIdx = lowerCaseMsg.lastIndexOf('1');
if (sepIdx < 0) {
throw new Error('No separator char ("1") found');
} // 5. Placing of the separator char in the message
if (sepIdx > message.length - _encoding.CHECKSUM_LENGTH - 1) {
throw new Error("Data part of the message too short (at least ".concat(_encoding.CHECKSUM_LENGTH, " chars expected)"));
}
var prefix = lowerCaseMsg.substring(0, sepIdx); // Checked within `decodeWithPrefix`:
// 6. Invalid chars in the data part of the message
var bitArray = (0, _encoding.decodeWithPrefix)(prefix, lowerCaseMsg.substring(sepIdx + 1)); // 7. Checksum
var encoding = (0, _encoding.verifyChecksum)(bitArray);
if (encoding === undefined) {
throw new Error('Invalid checksum');
}
return {
prefix: prefix,
encoding: encoding,
// Strip off the prefix from the front and the checksum from the end
data: bitArray.subarray(2 * prefix.length + 1, bitArray.length - _encoding.CHECKSUM_LENGTH)
};
}
/**
* Decodes data from Bech32 encoding into an array of 5-bit integers.
*
* @param {string} message
* Bech32-encoded message
* @returns {DecodeResult}
* Decoded object with `prefix` and `data` fields, which contain the human-readable
* prefix and the decoded binary data respectively.
*
* @api public
*/
function decode(message) {
var _decodeTo5BitArray = decodeTo5BitArray(message),
prefix = _decodeTo5BitArray.prefix,
encoding = _decodeTo5BitArray.encoding,
bitArray = _decodeTo5BitArray.data;
return {
prefix: prefix,
encoding: encoding,
data: from5BitArray(bitArray)
};
}
/**
* Bitcoin address.
*/
var BitcoinAddress = /*#__PURE__*/function () {
function BitcoinAddress(prefix, scriptVersion, data) {
_classCallCheck(this, BitcoinAddress);
if (prefix !== 'bc' && prefix !== 'tb') {
throw new Error('Invalid human-readable prefix, "bc" or "tb" expected');
}
if (scriptVersion < 0 || scriptVersion > 16) {
throw new RangeError('Invalid scriptVersion, value in range [0, 16] expected');
}
if (data.length < 2 || data.length > 40) {
throw new RangeError('Invalid script length: expected 2 to 40 bytes');
}
if (scriptVersion === 0 && data.length !== 20 && data.length !== 32) {
throw new Error('Invalid v0 script length: expected 20 or 32 bytes');
}
this.prefix = prefix;
this.scriptVersion = scriptVersion;
this.data = data;
}
/**
* Guesses the address type based on its internal structure.
*
* @returns {void | 'p2wpkh' | 'p2wsh'}
*/
_createClass(BitcoinAddress, [{
key: "type",
value: function type() {
if (this.scriptVersion !== 0) {
return undefined;
}
switch (this.data.length) {
case 20:
return 'p2wpkh';
case 32:
return 'p2wsh';
// should be unreachable, but it's JS, so you never know
default:
return undefined;
}
}
/**
* Encodes this address in Bech32 or Bech32m format, depending on the script version.
* Version 0 scripts are encoded using original Bech32 encoding as per BIP 173,
* while versions 1-16 are encoded using the modified encoding as per BIP 350.
*
* @returns {string}
* Bech32(m)-encoded address
*/
}, {
key: "encode",
value: function encode() {
// Bitcoin addresses use Bech32 in a peculiar way - script version is
// not a part of the serialized binary data, but is rather prepended as 5-bit value
// before the rest of the script. This necessitates some plumbing here.
var len = Math.ceil(this.data.length * 8 / 5);
var converted = (0, _bitConverter.createBitArray)(len + 1);
converted[0] = this.scriptVersion;
to5BitArray(this.data, converted.subarray(1));
var encoding = this.scriptVersion === 0 ? 'bech32' : 'bech32m';
return encode5BitArray(this.prefix, converted, encoding);
}
}], [{
key: "decode",
value:
/**
* Human-readable prefix. Equal to `'bc'` (for mainnet addresses)
* or `'tb'` (for testnet addresses).
*/
/**
* Script version. An integer between 0 and 16 (inclusive).
*/
/**
* Script data. A byte string with length 2 to 40 (inclusive).
*/
/**
* Decodes a Bitcoin address from a Bech32(m) string.
* As per BIP 350, the original encoding is expected for version 0 scripts, while
* other script versions expect the modified encoding.
*
* This method does not check whether the address is well-formed;
* use `type()` method on returned address to find that out.
*
* @param {string} message
* @returns {BitcoinAddress}
*/
function decode(message) {
var _decodeTo5BitArray2 = decodeTo5BitArray(message),
prefix = _decodeTo5BitArray2.prefix,
data = _decodeTo5BitArray2.data,
encoding = _decodeTo5BitArray2.encoding; // Extra check to satisfy Flow.
if (prefix !== 'bc' && prefix !== 'tb') {
throw new Error('Invalid human-readable prefix, "bc" or "tb" expected');
}
var scriptVersion = data[0];
if (scriptVersion === 0 && encoding !== 'bech32') {
throw Error("Unexpected encoding ".concat(encoding, " used for version 0 script"));
}
if (scriptVersion > 0 && encoding !== 'bech32m') {
throw Error("Unexpected encoding ".concat(encoding, " used for version ").concat(scriptVersion, " script"));
}
return new this(prefix, scriptVersion, from5BitArray(data.subarray(1)));
}
}]);
return BitcoinAddress;
}();
exports.BitcoinAddress = BitcoinAddress;
+87
View File
@@ -0,0 +1,87 @@
{
"name": "bech32-buffer",
"version": "0.2.1",
"description": "Bech32(m) encoding for byte buffers",
"repository": {
"type": "git",
"url": "https://github.com/slowli/bech32-buffer.git"
},
"bugs": {
"url": "https://github.com/slowli/bech32-buffer/issues"
},
"engines": {
"node": ">=8"
},
"main": "lib/index.js",
"files": [
"lib",
"dist",
"examples",
"index.d.ts",
"CHANGELOG.md"
],
"types": "index.d.ts",
"scripts": {
"test": "mocha -r @babel/register",
"test-browser": "karma start",
"cover": "cross-env NODE_ENV=test nyc mocha",
"report-cover": "nyc report",
"browser": "mkdirp dist && cross-env BABEL_ENV=browser browserify src/index.js -t [ babelify ] --standalone bech32 -o dist/bech32-buffer.min.js",
"minify-qr": "cross-env BABEL_ENV=browser browserify node_modules/qrcode-generator/qrcode.js -t [ babelify ] --standalone qrcode -o examples/qrcode.min.js",
"compile": "babel src/ -d lib/",
"prepare": "npm run compile && npm run browser",
"lint": "eslint --ignore-path .gitignore . && flow check",
"test-ts": "npm run compile && tsc -p test && node test/types.js"
},
"keywords": [
"bech32",
"bech32m",
"base32",
"bitcoin"
],
"author": "Alex Ostrovski <ostrovski.alex@gmail.com>",
"license": "Apache-2.0",
"devDependencies": {
"@babel/cli": "^7.18.6",
"@babel/core": "^7.19.1",
"@babel/eslint-parser": "^7.19.1",
"@babel/preset-env": "^7.19.1",
"@babel/preset-flow": "^7.18.6",
"@babel/register": "^7.18.6",
"@types/chai": "^4.2.22",
"babel-plugin-istanbul": "^6.0.0",
"babel-preset-minify": "^0.5.1",
"babelify": "^10.0.0",
"browserify": "^17.0.0",
"chai": "^4.3.4",
"chai-bytes": "^0.1.2",
"cross-env": "^7.0.3",
"dirty-chai": "^2.0.1",
"eslint": "^8.24.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.24.2",
"flow-bin": "^0.187.1",
"karma": "^6.4.0",
"karma-browserify": "^8.0.0",
"karma-chrome-launcher": "^3.1.0",
"karma-firefox-launcher": "^2.1.1",
"karma-mocha": "^2.0.1",
"karma-mocha-reporter": "^2.2.3",
"mkdirp": "^1.0.4",
"mocha": "^10.0.0",
"nyc": "^15.1.0",
"qrcode-generator": "^1.4.4",
"typescript": "^4.8.2"
},
"nyc": {
"require": [
"@babel/register"
],
"exclude": [
"lib",
"test"
],
"sourceMap": false,
"instrument": false
}
}