Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Monero (XMR) stagenet addresses #62

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 90 additions & 51 deletions dist/wallet-address-validator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(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.WAValidator = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){
(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.WAValidator = 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){
// base-x encoding
// Forked from https://github.com/cryptocoinjs/bs58
// Originally written by Mike Hearn for BitcoinJ
Expand Down Expand Up @@ -114,65 +114,97 @@ for (var i = 0, len = code.length; i < len; ++i) {
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63

function placeHoldersCount (b64) {
function getLens (b64) {
var len = b64.length

if (len % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}

// the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
// Trim off extra bytes after placeholder bytes are found
// See: https://github.com/beatgammit/base64-js/issues/42
var validLen = b64.indexOf('=')
if (validLen === -1) validLen = len

var placeHoldersLen = validLen === len
? 0
: 4 - (validLen % 4)

return [validLen, placeHoldersLen]
}

// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
// base64 is 4/3 + up to two characters of the original data
return (b64.length * 3 / 4) - placeHoldersCount(b64)
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function _byteLength (b64, validLen, placeHoldersLen) {
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function toByteArray (b64) {
var i, l, tmp, placeHolders, arr
var len = b64.length
placeHolders = placeHoldersCount(b64)
var tmp
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]

arr = new Arr((len * 3 / 4) - placeHolders)
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))

var curByte = 0

// if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? len - 4 : len
var len = placeHoldersLen > 0
? validLen - 4
: validLen

var L = 0
for (var i = 0; i < len; i += 4) {
tmp =
(revLookup[b64.charCodeAt(i)] << 18) |
(revLookup[b64.charCodeAt(i + 1)] << 12) |
(revLookup[b64.charCodeAt(i + 2)] << 6) |
revLookup[b64.charCodeAt(i + 3)]
arr[curByte++] = (tmp >> 16) & 0xFF
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}

for (i = 0; i < l; i += 4) {
tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
arr[L++] = (tmp >> 16) & 0xFF
arr[L++] = (tmp >> 8) & 0xFF
arr[L++] = tmp & 0xFF
if (placeHoldersLen === 2) {
tmp =
(revLookup[b64.charCodeAt(i)] << 2) |
(revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[curByte++] = tmp & 0xFF
}

if (placeHolders === 2) {
tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[L++] = tmp & 0xFF
} else if (placeHolders === 1) {
tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[L++] = (tmp >> 8) & 0xFF
arr[L++] = tmp & 0xFF
if (placeHoldersLen === 1) {
tmp =
(revLookup[b64.charCodeAt(i)] << 10) |
(revLookup[b64.charCodeAt(i + 1)] << 4) |
(revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}

return arr
}

function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
return lookup[num >> 18 & 0x3F] +
lookup[num >> 12 & 0x3F] +
lookup[num >> 6 & 0x3F] +
lookup[num & 0x3F]
}

function encodeChunk (uint8, start, end) {
var tmp
var output = []
for (var i = start; i < end; i += 3) {
tmp = ((uint8[i] << 16) & 0xFF0000) + ((uint8[i + 1] << 8) & 0xFF00) + (uint8[i + 2] & 0xFF)
tmp =
((uint8[i] << 16) & 0xFF0000) +
((uint8[i + 1] << 8) & 0xFF00) +
(uint8[i + 2] & 0xFF)
output.push(tripletToBase64(tmp))
}
return output.join('')
Expand All @@ -182,31 +214,34 @@ function fromByteArray (uint8) {
var tmp
var len = uint8.length
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
var output = ''
var parts = []
var maxChunkLength = 16383 // must be multiple of 3

// go through the array every three bytes, we'll deal with trailing stuff later
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
parts.push(encodeChunk(
uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
))
}

// pad the end with zeros, but make sure to not forget the extra bytes
if (extraBytes === 1) {
tmp = uint8[len - 1]
output += lookup[tmp >> 2]
output += lookup[(tmp << 4) & 0x3F]
output += '=='
parts.push(
lookup[tmp >> 2] +
lookup[(tmp << 4) & 0x3F] +
'=='
)
} else if (extraBytes === 2) {
tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
output += lookup[tmp >> 10]
output += lookup[(tmp >> 4) & 0x3F]
output += lookup[(tmp << 2) & 0x3F]
output += '='
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
parts.push(
lookup[tmp >> 10] +
lookup[(tmp >> 4) & 0x3F] +
lookup[(tmp << 2) & 0x3F] +
'='
)
}

parts.push(output)

return parts.join('')
}

Expand Down Expand Up @@ -1951,7 +1986,7 @@ function numberIsNaN (obj) {
},{"base64-js":2,"ieee754":4}],4:[function(require,module,exports){
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
var e, m
var eLen = nBytes * 8 - mLen - 1
var eLen = (nBytes * 8) - mLen - 1
var eMax = (1 << eLen) - 1
var eBias = eMax >> 1
var nBits = -7
Expand All @@ -1964,12 +1999,12 @@ exports.read = function (buffer, offset, isLE, mLen, nBytes) {
e = s & ((1 << (-nBits)) - 1)
s >>= (-nBits)
nBits += eLen
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}

m = e & ((1 << (-nBits)) - 1)
e >>= (-nBits)
nBits += mLen
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}

if (e === 0) {
e = 1 - eBias
Expand All @@ -1984,7 +2019,7 @@ exports.read = function (buffer, offset, isLE, mLen, nBytes) {

exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
var e, m, c
var eLen = nBytes * 8 - mLen - 1
var eLen = (nBytes * 8) - mLen - 1
var eMax = (1 << eLen) - 1
var eBias = eMax >> 1
var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
Expand Down Expand Up @@ -2017,7 +2052,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
m = 0
e = eMax
} else if (e + eBias >= 1) {
m = (value * c - 1) * Math.pow(2, mLen)
m = ((value * c) - 1) * Math.pow(2, mLen)
e = e + eBias
} else {
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
Expand Down Expand Up @@ -5701,8 +5736,8 @@ var CURRENCIES = [{
},{
name: 'monero',
symbol: 'xmr',
addressTypes: {prod: ['18'], testnet: ['53']},
iAddressTypes: {prod: ['19'], testnet: ['54']},
addressTypes: {prod: ['18'], testnet: ['53'], stagenet: ['24']},
iAddressTypes: {prod: ['19'], testnet: ['54'], stagenet: ['25']},
validator: XMRValidator
},{
name: 'nano',
Expand Down Expand Up @@ -5783,8 +5818,12 @@ function validateNetwork(decoded, currency, networkType, addressType){
return parseInt(decoded.substr(0,2), 16) == network.prod[0];
case 'testnet':
return parseInt(decoded.substr(0,2), 16) == network.testnet[0];
case 'stagenet':
return parseInt(decoded.substr(0,2), 16) == network.stagenet[0];
case 'both':
return parseInt(decoded.substr(0,2), 16) == network.prod[0] || parseInt(decoded.substr(0,2), 16) == network.testnet[0];
return parseInt(decoded.substr(0,2), 16) == network.prod[0]
|| parseInt(decoded.substr(0,2), 16) == network.testnet[0]
|| parseInt(decoded.substr(0,2), 16) == network.stagenet[0];
default:
return false;
}
Expand Down Expand Up @@ -5903,4 +5942,4 @@ module.exports = {
};

},{"./currencies":18}]},{},[23])(23)
});
});
2 changes: 1 addition & 1 deletion dist/wallet-address-validator.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/currencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@ var CURRENCIES = [{
},{
name: 'monero',
symbol: 'xmr',
addressTypes: {prod: ['18'], testnet: ['53']},
iAddressTypes: {prod: ['19'], testnet: ['54']},
addressTypes: {prod: ['18'], testnet: ['53'], stagenet: ['24']},
iAddressTypes: {prod: ['19'], testnet: ['54'], stagenet: ['25']},
validator: XMRValidator
},{
name: 'nano',
Expand Down
6 changes: 5 additions & 1 deletion src/monero_validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ function validateNetwork(decoded, currency, networkType, addressType){
return parseInt(decoded.substr(0,2), 16) == network.prod[0];
case 'testnet':
return parseInt(decoded.substr(0,2), 16) == network.testnet[0];
case 'stagenet':
return parseInt(decoded.substr(0,2), 16) == network.stagenet[0];
case 'both':
return parseInt(decoded.substr(0,2), 16) == network.prod[0] || parseInt(decoded.substr(0,2), 16) == network.testnet[0];
return parseInt(decoded.substr(0,2), 16) == network.prod[0]
|| parseInt(decoded.substr(0,2), 16) == network.testnet[0]
|| parseInt(decoded.substr(0,2), 16) == network.stagenet[0];
default:
return false;
}
Expand Down
6 changes: 6 additions & 0 deletions test/wallet_address_validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ describe('WAValidator.validate()', function () {
//integrated addresses
valid('4Gd4DLiXzBmbVX2FZZ3Cvu6fUaWACup1qDowprUCje1kSP4FmbftiJMSfV8kWZXNqmVwj4m52xqtgFNUudVmsmGkGvkLcCibWfVUfUFVB7', 'monero');
valid('4J5sF94AzXgFgx8LuWc9dcWkJkGkD3cL3L2AuhX6QA9jFvSxxj6QhHqHXqM2b2Go7G8RyDzEbHxYd9G26XUUbuJChipEyBz9fENMU2Ua9b', 'XMR');

//stagenet public address
valid('5BAP9qLbRseYrGneYVRaFANMajuaD4KZrf3fGWvt5cVDR1xUXm6qoFYLkgU6Vp12fs2R24r4269inAWHFEdsLnE87rGCxYK', 'XMR', 'stagenet');

//stagenet integrated address
valid('5Ls4AeA639AYrGneYVRaFANMajuaD4KZrf3fGWvt5cVDR1xUXm6qoFYLkgU6Vp12fs2R24r4269inAWHFEdsLnE8BCU5Q2gDkbq2HYDrn2', 'monero', 'both');
});

it('should return true for correct nano addresses', function () {
Expand Down