├── .gitignore ├── test ├── mocha.opts ├── fixtures │ └── coinstring.json └── coinstring.test.js ├── .npmignore ├── .min-wd ├── .travis.yml ├── README.md ├── LICENSE.md ├── package.json ├── lib └── coinstring.js └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --ui bdd 2 | --reporter spec 3 | --timeout 2000 -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | test/ 3 | .DS_Store 4 | component.json 5 | bower.json 6 | .min-wd 7 | .travis.yml -------------------------------------------------------------------------------- /.min-wd: -------------------------------------------------------------------------------- 1 | { 2 | "hostname" : "localhost", 3 | "port" : 4444, 4 | "browsers" : [{ 5 | "name" : "chrome" 6 | }] 7 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | before_install: 3 | - "npm install npm -g" 4 | node_js: 5 | - 0.11 6 | - 0.10 7 | env: 8 | - TEST_SUITE=unit 9 | - TEST_SUITE=coveralls 10 | script: "npm run-script $TEST_SUITE" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATED. Use [bs58check](https://github.com/bitcoinjs/bs58check) instead. 2 | 3 | coinstring 4 | ========== 5 | 6 | [![build status](https://secure.travis-ci.org/cryptocoinjs/coinstring.png)](http://travis-ci.org/cryptocoinjs/coinstring) 7 | [![Coverage Status](https://img.shields.io/coveralls/cryptocoinjs/coinstring.svg)](https://coveralls.io/r/cryptocoinjs/coinstring) 8 | [![Version](http://img.shields.io/npm/v/coinstring.svg)](https://www.npmjs.org/package/coinstring) 9 | 10 | JavaScript component that's used to generate relevant addresses, wallet import formats, BIP32 encodings, and base 58 check encoding 11 | used by various crypto currencies. The difference between this and base58 check encoding is not much other than base 58 check encoding 12 | specifies that the version should only have one byte. This means that base 58 check encoding technically would NOT work for BIP 32 13 | addresses, but this module does work with BIP 32 addresses. 14 | 15 | Works in Node.js and the browser. 16 | 17 | ### Official documentation: 18 | 19 | http://cryptocoinjs.com/modules/currency/coinstring/ 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) cryptocoinjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coinstring", 3 | "version": "2.3.1", 4 | "license": "MIT", 5 | "description": "Create and parse crypto currency addresses and wallet import formats.", 6 | "keywords": [ 7 | "cryptography", 8 | "crypto", 9 | "bitcoin", 10 | "litecoin", 11 | "dogecoin", 12 | "currency", 13 | "cryptocurrency", 14 | "address", 15 | "wif", 16 | "base58", 17 | "bip32" 18 | ], 19 | "devDependencies": { 20 | "coveralls": "^2.10.0", 21 | "istanbul": "^0.2.10", 22 | "mocha": "^2.2.5", 23 | "mocha-lcov-reporter": "0.0.1", 24 | "mochify": "^2.1.0", 25 | "standard": "4.x" 26 | }, 27 | "repository": { 28 | "url": "https://github.com/cryptocoinjs/coinstring", 29 | "type": "git" 30 | }, 31 | "main": "./lib/coinstring.js", 32 | "dependencies": { 33 | "bs58": "^2.0.1", 34 | "create-hash": "^1.1.1" 35 | }, 36 | "scripts": { 37 | "browser-test": "mochify --wd -R spec", 38 | "test": "standard && mocha --ui bdd", 39 | "unit": "mocha", 40 | "coverage": "istanbul cover _mocha -- --reporter list test/*.js", 41 | "coveralls": "npm run-script coverage && node coveralls < coverage/lcov.info" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/coinstring.js: -------------------------------------------------------------------------------- 1 | var base58 = require('bs58') 2 | var createHash = require('create-hash') 3 | 4 | function encode (payload, version) { 5 | if (Array.isArray(payload) || payload instanceof Uint8Array) { 6 | payload = new Buffer(payload) 7 | } 8 | 9 | var buf 10 | if (version != null) { 11 | if (typeof version === 'number') { 12 | version = new Buffer([version]) 13 | } 14 | buf = Buffer.concat([version, payload]) 15 | } else { 16 | buf = payload 17 | } 18 | 19 | var checksum = sha256x2(buf).slice(0, 4) 20 | var result = Buffer.concat([buf, checksum]) 21 | return base58.encode(result) 22 | } 23 | 24 | function decode (base58str, version) { 25 | var arr = base58.decode(base58str) 26 | var buf = new Buffer(arr) 27 | var versionLength 28 | 29 | if (version == null) { 30 | versionLength = 0 31 | } else { 32 | if (typeof version === 'number') version = new Buffer([version]) 33 | 34 | versionLength = version.length 35 | var versionCompare = buf.slice(0, versionLength) 36 | if (versionCompare.toString('hex') !== version.toString('hex')) { 37 | throw new Error('Invalid version') 38 | } 39 | } 40 | 41 | var checksum = buf.slice(-4) 42 | var endPos = buf.length - 4 43 | var bytes = buf.slice(0, endPos) 44 | 45 | var newChecksum = sha256x2(bytes).slice(0, 4) 46 | if (checksum.toString('hex') !== newChecksum.toString('hex')) { 47 | throw new Error('Invalid checksum') 48 | } 49 | 50 | return bytes.slice(versionLength) 51 | } 52 | 53 | function isValid (base58str, version) { 54 | try { 55 | decode(base58str, version) 56 | } catch (e) { 57 | return false 58 | } 59 | 60 | return true 61 | } 62 | 63 | function createEncoder (version) { 64 | return function (payload) { 65 | return encode(payload, version) 66 | } 67 | } 68 | 69 | function createDecoder (version) { 70 | return function (base58str) { 71 | return decode(base58str, version) 72 | } 73 | } 74 | 75 | function createValidator (version) { 76 | return function (base58str) { 77 | return isValid(base58str, version) 78 | } 79 | } 80 | 81 | function sha256x2 (buffer) { 82 | var sha = createHash('sha256').update(buffer).digest() 83 | return createHash('sha256').update(sha).digest() 84 | } 85 | 86 | module.exports = { 87 | encode: encode, 88 | decode: decode, 89 | isValid: isValid, 90 | createEncoder: createEncoder, 91 | createDecoder: createDecoder, 92 | createValidator: createValidator 93 | } 94 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2.3.0 / 2015-06-04 2 | ------------------ 3 | - use `createHash` directly https://github.com/cryptocoinjs/coinstring/pull/5 4 | - JavaScript Standard Style 5 | 6 | 2.2.0 / 2014-12-23 7 | ------------------ 8 | - `bs58` from `1.x` to `2.x` (decode returns `Array` instead of `Buffer`) 9 | 10 | 2.1.0 / 2014-12-16 11 | ------------------ 12 | - remove `browser` field, and `crypto-browserify` devDep 13 | - removed Makefile; replaced with npm scripts 14 | 15 | 2.0.0 / 2014-06-25 16 | ------------------ 17 | * changed `encode(payload, version)` to `encode(payload, [version])` 18 | * changed how `decode` works (**broke compatibility** hence major version bump) 19 | 20 | old version returned `{payload: ..., version: ...}`, this version now returns 21 | just `payload`, if `version` is passed into `decode`, it's trimmed off of `payload` 22 | 23 | 1.0.1 / 2014-06-06 24 | ------------------ 25 | * throw error if `version` is not present for `encode()` 26 | 27 | 1.0.0 / 2014-06-06 28 | ------------------ 29 | * upgraded `mochify` for dev deps 30 | * removed semicolons per http://cryptocoinjs.com/about/contributing/#semicolons 31 | * removed `terst` per http://cryptocoinjs.com/about/contributing/#testing 32 | * removed `binstring` from dev deps 33 | * added travis ci 34 | * added coveralls 35 | * upgraded `bs58` from `0.3.x` to `^1.0.0` 36 | * removed `crypto-hashing` dep 37 | * `coinstring()` renamed to `cs.encode()` 38 | * added methods `createEncoder()` and `createDecoder()` 39 | * changed method signature of `encode(version, payload)` to `encode(payload,version)` 40 | * changed method signature of `decode(version, base58str)` to `decode(base58str, version)` 41 | * added method `createValidator()` 42 | * changed method signature of `validate(version, base58str)` to `validate(base58str, version)` 43 | * renamed method `validate()` to `isValid()` 44 | * renamed return from `decode()` object field `bytes` to `payload` 45 | * added `Buffer` support for version input => BIP32 support 46 | 47 | 0.2.0 / 2014-03-10 48 | ------------------ 49 | * changed input to decode, made `version` optional 50 | * changed output to decode, returns an object with the properties `version` and `bytes` 51 | 52 | 0.1.0 / 2014-03-10 53 | ------------------ 54 | * added browser tests 55 | * added support for `coinstring()` method to input a type `Array` or `Uint8Array` 56 | * export `crypto-hashing`, non-standard practice... thoughts? 57 | * upgraded `crypto-hashing` to `0.3.0`, `ripemd160` now works in the browser 58 | 59 | 0.0.1 / 2014-03-07 60 | ------------------ 61 | * initial release -------------------------------------------------------------------------------- /test/fixtures/coinstring.json: -------------------------------------------------------------------------------- 1 | { 2 | "valid": [ 3 | { 4 | "hex": "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd", 5 | "base58": "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD", 6 | "version": "0x80", 7 | "description": "bitcoin private non-compressed key / wallet import format" 8 | }, 9 | { 10 | "hex": "801184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd", 11 | "base58": "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD", 12 | "version": null, 13 | "description": "bitcoin private non-compressed key / wallet import format - no version" 14 | }, 15 | { 16 | "hex": "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd01", 17 | "base58": "KwomKti1X3tYJUUMb1TGSM2mrZk1wb1aHisUNHCQXTZq5auC2qc3", 18 | "version": "0x80", 19 | "description": "bitcoin private compressed key / wallet import format" 20 | }, 21 | { 22 | "hex": "3c176e659bea0f29a3e9bf7880c112b1b31b4dc8", 23 | "base58": "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS", 24 | "version": "0x00", 25 | "description": "bitcoin hash160 / address" 26 | }, 27 | { 28 | "hex": "a1c2f92a9dacbd2991c3897724a93f338e44bdc1", 29 | "base58": "1FkKMsKNJqWSDvTvETqcCeHcUQQ64kSC6s", 30 | "version": "0x00", 31 | "description": "bitcoin hash160 / address" 32 | }, 33 | { 34 | "hex": "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd", 35 | "base58": "6JGLNEiEYR6pFGq84gwceHWYLcyKaLWzaymVajjCPPUEGAR2MTT", 36 | "version": "0x9e", 37 | "description": "dogecoin private non-compressed key / wallet import format" 38 | }, 39 | { 40 | "hex": "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd01", 41 | "base58": "QPCgUjWzmfNfXzsQBHJ4KZsPKbmaz99PAyZP9ubFFpBBXWuSQh6n", 42 | "version": "0x9e", 43 | "description": "dogecoin private compressed key / wallet import format" 44 | }, 45 | { 46 | "hex": "3c176e659bea0f29a3e9bf7880c112b1b31b4dc8", 47 | "base58": "DAcq9oJpZZAjr56RmF7Y5zmWboZWQ4HAsW", 48 | "version": "0x1e", 49 | "description": "dogecoin hash160 / address" 50 | }, 51 | { 52 | "hex": "a1c2f92a9dacbd2991c3897724a93f338e44bdc1", 53 | "base58": "DKtQu8G1cFQikveWy3qAkQTDMY8PKVU18Z", 54 | "version": "0x1e", 55 | "description": "dogecoin hash160 / address" 56 | }, 57 | { 58 | "hex": "000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", 59 | "base58": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 60 | "version": "0x0488ade4", 61 | "description": "bip 32 private address" 62 | }, 63 | { 64 | "hex": "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", 65 | "base58": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", 66 | "version": null, 67 | "description": "bip 32 private address" 68 | }, 69 | { 70 | "hex": "000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", 71 | "base58": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 72 | "version": "0x0488b21e", 73 | "description": "bip 32 public address" 74 | } 75 | ], 76 | "invalid": [ 77 | { 78 | "base58": "16ujcynbg9gtk4uq2f7yyebuifqczolmgs", 79 | "version": "0x00", 80 | "description": "lowercase", 81 | "match": "non-base58 character" 82 | }, 83 | { 84 | "base58": "5hx15hfgyep2cfpxsjke2fxjscvn5deiyoeggf6jzjgbtrnqfid", 85 | "version": "0x80", 86 | "description": "lowercase", 87 | "match": "invalid version" 88 | }, 89 | { 90 | "base58": "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD", 91 | "version": "0x01", 92 | "description": "invalid version", 93 | "match": "invalid version" 94 | }, 95 | { 96 | "base58": "5hx15hfgyep2cfpxsjke2fxjscvn5deiyoeggf6jzjgbtrnqfid", 97 | "version": "0x02", 98 | "description": "invalid version", 99 | "match": "invalid version" 100 | }, 101 | { 102 | "base58": "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTQzFWrw", 103 | "version": "0x80", 104 | "description": "invalid checksum", 105 | "match": "invalid checksum" 106 | } 107 | ] 108 | } -------------------------------------------------------------------------------- /test/coinstring.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var cs = require('../') 3 | var fixtures = require('./fixtures/coinstring') 4 | 5 | /* global describe, it */ 6 | 7 | describe('coinstring', function () { 8 | describe('+ encode()', function () { 9 | fixtures.valid.forEach(function (f) { 10 | it('should encode ' + f.description, function () { 11 | if (f.version) { 12 | var inp = new Buffer(f.hex, 'hex') 13 | var version = parseInt(f.version, 16) 14 | var versionBuffer = new Buffer(f.version.substr(2), 'hex') // chop off '0x' 15 | 16 | if (f.version.length <= 4) {// skip bip32 here 17 | assert.equal(cs.encode(inp, version), f.base58) 18 | assert.equal(cs.encode([].slice.call(inp), version), f.base58) 19 | assert.equal(cs.encode(new Uint8Array([].slice.call(inp)), version), f.base58) 20 | } 21 | 22 | assert.equal(cs.encode(inp, versionBuffer), f.base58) 23 | } else { 24 | assert.equal(cs.encode(new Buffer(f.hex, 'hex')), f.base58) 25 | } 26 | }) 27 | }) 28 | 29 | describe('> when no version', function () { 30 | it('should still encode it', function () { 31 | var f = fixtures.valid[0] 32 | var inp = new Buffer(f.hex, 'hex') 33 | var version = new Buffer(f.version.substr(2), 'hex') 34 | var payload = Buffer.concat([version, inp]) 35 | assert.equal(cs.encode(payload), f.base58) 36 | }) 37 | }) 38 | }) 39 | 40 | describe('+ decode()', function () { 41 | fixtures.valid.forEach(function (f) { 42 | it('should decode ' + f.description, function () { 43 | if (f.version) { 44 | var version = parseInt(f.version, 16) 45 | var bufferVersion = new Buffer(f.version.substr(2), 'hex') 46 | var res = {} 47 | 48 | if (f.version.length <= 4) { // skip bip32 49 | res = cs.decode(f.base58, version) 50 | assert.equal(res.toString('hex'), f.hex) 51 | 52 | res = cs.decode(f.base58) 53 | // must slice off version 54 | assert.equal(res.slice(bufferVersion.length).toString('hex'), f.hex) 55 | } 56 | 57 | res = cs.decode(f.base58, bufferVersion) 58 | assert.equal(res.toString('hex'), f.hex) 59 | } else { // f.version == null 60 | assert.equal(cs.decode(f.base58).toString('hex'), f.hex) 61 | } 62 | }) 63 | }) 64 | 65 | describe('> when invalid input', function () { 66 | fixtures.invalid.forEach(function (f) { 67 | it(f.description + ': ' + f.base58 + ' should throw an error', function () { 68 | assert.throws(function () { 69 | var version = parseInt(f.version, 16) 70 | assert(cs.decode(f.base58, version)) 71 | }, new RegExp(f.match, 'i')) 72 | }) 73 | }) 74 | }) 75 | }) 76 | 77 | describe('+ isValid()', function () { 78 | fixtures.valid.forEach(function (f) { 79 | it('should validate ' + f.description, function () { 80 | if (f.version == null) return // can't check isValid if no version 81 | 82 | var version = parseInt(f.version, 16) 83 | var versionBuffer = new Buffer(f.version.substr(2), 'hex') 84 | if (f.version.length <= 4) { // skip bip 32 85 | assert(cs.isValid(f.base58, version)) 86 | } 87 | assert(cs.isValid(f.base58, versionBuffer)) 88 | }) 89 | }) 90 | 91 | describe('> when invalid input', function () { 92 | fixtures.invalid.forEach(function (f) { 93 | it(f.description + ' should return false', function () { 94 | var version = parseInt(f.version, 16) 95 | assert(!cs.isValid(f.base58, version)) 96 | }) 97 | }) 98 | }) 99 | }) 100 | 101 | describe('+ createEncoder()', function () { 102 | fixtures.valid.forEach(function (f) { 103 | it('should create an encoder ' + f.description, function () { 104 | if (f.version == null) return // can't create decoder if no version 105 | 106 | var inp = new Buffer(f.hex, 'hex') 107 | var version = parseInt(f.version, 16) 108 | var versionBuffer = new Buffer(f.version.substr(2), 'hex') 109 | 110 | if (f.version.length <= 4) {// skip bip 32 111 | var encode = cs.createEncoder(version) 112 | assert.equal(encode(inp), f.base58) 113 | } 114 | 115 | encode = cs.createEncoder(versionBuffer) 116 | assert.equal(encode(inp), f.base58) 117 | }) 118 | }) 119 | }) 120 | 121 | describe('+ createDecoder()', function () { 122 | fixtures.valid.forEach(function (f) { 123 | it('should create a decoder ' + f.description, function () { 124 | if (f.version == null) return // can't create decoder if no version 125 | 126 | // var inp = new Buffer(f.hex, 'hex') 127 | var version = parseInt(f.version, 16) 128 | var versionBuffer = new Buffer(f.version.substr(2), 'hex') 129 | 130 | if (f.version.length <= 4) { // skip bip 32 131 | var decode = cs.createDecoder(version) 132 | assert.equal(decode(f.base58).toString('hex'), f.hex) 133 | } 134 | 135 | decode = cs.createDecoder(versionBuffer) 136 | assert.equal(decode(f.base58).toString('hex'), f.hex) 137 | }) 138 | }) 139 | }) 140 | 141 | describe('+ createValidator()', function () { 142 | fixtures.valid.forEach(function (f) { 143 | it('should create a validator ' + f.description, function () { 144 | if (f.version == null) return // can't create validator if no version 145 | 146 | var version = parseInt(f.version, 16) 147 | var versionBuffer = new Buffer(f.version.substr(2), 'hex') 148 | if (f.version.length <= 4) { // skip bip 32 149 | var isValid = cs.createValidator(version) 150 | assert(isValid(f.base58)) 151 | } 152 | 153 | isValid = cs.createValidator(versionBuffer) 154 | assert(isValid(f.base58)) 155 | }) 156 | }) 157 | 158 | describe('> when invalid input', function () { 159 | fixtures.invalid.forEach(function (f) { 160 | it(f.description + ' should return false', function () { 161 | var version = parseInt(f.version, 16) 162 | var versionBuffer = new Buffer(f.version.substr(2), 'hex') 163 | 164 | if (f.version.length <= 4) { // skip bip 32 165 | var isValid = cs.createValidator(version) 166 | assert(!isValid(f.base58)) 167 | } 168 | 169 | isValid = cs.createValidator(versionBuffer) 170 | assert(!isValid(f.base58)) 171 | }) 172 | }) 173 | }) 174 | }) 175 | }) 176 | --------------------------------------------------------------------------------