Skip to content

Commit a9d0f59

Browse files
committed
Fix ADA (Cardano) bech32 addresses are not cleared as valid #4
1 parent 50d7b49 commit a9d0f59

File tree

8 files changed

+110
-35
lines changed

8 files changed

+110
-35
lines changed

.vscode/launch.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
66
"configurations": [
7+
{
8+
"name": "yarn test:node",
9+
"request": "launch",
10+
"runtimeArgs": [
11+
"test:node",
12+
],
13+
"runtimeExecutable": "yarn",
14+
"skipFiles": [
15+
"<node_internals>/**"
16+
],
17+
"type": "pwa-node"
18+
},
719
{
820
"type": "node",
921
"request": "launch",

dist/wallet-address-validator.js

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10285,7 +10285,9 @@ SafeBuffer.allocUnsafeSlow = function (size) {
1028510285
var cbor = require('cbor-js');
1028610286
var CRC = require('crc');
1028710287
var base58 = require('./crypto/base58');
10288+
var bech32 = require('./crypto/bech32');
1028810289

10290+
var DEFAULT_NETWORK_TYPE = 'prod';
1028910291

1029010292
function getDecoded(address) {
1029110293
try {
@@ -10297,28 +10299,53 @@ function getDecoded(address) {
1029710299
}
1029810300
}
1029910301

10300-
module.exports = {
10301-
isValidAddress: function (address) {
10302-
var decoded = getDecoded(address);
10302+
function isValidLegacyAddress(address) {
10303+
var decoded = getDecoded(address);
1030310304

10304-
if (!decoded || (!Array.isArray(decoded) && decoded.length != 2)) {
10305-
return false;
10306-
}
10305+
if (!decoded || (!Array.isArray(decoded) && decoded.length != 2)) {
10306+
return false;
10307+
}
1030710308

10308-
var tagged = decoded[0];
10309-
var validCrc = decoded[1];
10310-
if (typeof (validCrc) != 'number') {
10311-
return false;
10312-
}
10309+
var tagged = decoded[0];
10310+
var validCrc = decoded[1];
10311+
if (typeof (validCrc) != 'number') {
10312+
return false;
10313+
}
10314+
10315+
// get crc of the payload
10316+
var crc = CRC.crc32(tagged);
10317+
10318+
return crc == validCrc;
10319+
}
10320+
10321+
// it is not possible to use bitcoin ./crypto/segwit_addr library, the cardano address is too long for that
10322+
function isValidBech32Address(address, currency, networkType) {
10323+
if (!currency.segwitHrp) {
10324+
return false;
10325+
}
10326+
var hrp = currency.segwitHrp[networkType];
10327+
if (!hrp) {
10328+
return false;
10329+
}
1031310330

10314-
// get crc of the payload
10315-
var crc = CRC.crc32(tagged);
10331+
var dec = bech32.decode(address, 103);
1031610332

10317-
return crc == validCrc;
10333+
if (dec === null || dec.hrp !== hrp || dec.data.length < 1 || dec.data[0] > 16) {
10334+
return false;
10335+
}
10336+
10337+
return true;
10338+
}
10339+
10340+
module.exports = {
10341+
isValidAddress: function (address, currency, networkType) {
10342+
networkType = networkType || DEFAULT_NETWORK_TYPE;
10343+
return isValidLegacyAddress(address)
10344+
|| isValidBech32Address(address, currency, networkType);
1031810345
}
1031910346
};
1032010347

10321-
},{"./crypto/base58":137,"cbor-js":5,"crc":30}],130:[function(require,module,exports){
10348+
},{"./crypto/base58":137,"./crypto/bech32":138,"cbor-js":5,"crc":30}],130:[function(require,module,exports){
1032210349
var base58 = require('./crypto/base58')
1032310350

1032410351
const ALLOWED_CHARS = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
@@ -10734,7 +10761,8 @@ function encode (hrp, data) {
1073410761
return ret;
1073510762
}
1073610763

10737-
function decode (bechString) {
10764+
function decode (bechString, maxLength) {
10765+
maxLength=maxLength || 90;
1073810766
var p;
1073910767
var has_lower = false;
1074010768
var has_upper = false;
@@ -10754,7 +10782,7 @@ function decode (bechString) {
1075410782
}
1075510783
bechString = bechString.toLowerCase();
1075610784
var pos = bechString.lastIndexOf('1');
10757-
if (pos < 1 || pos + 7 > bechString.length || bechString.length > 90) {
10785+
if (pos < 1 || pos + 7 > bechString.length || bechString.length > maxLength) {
1075810786
return null;
1075910787
}
1076010788
var hrp = bechString.substring(0, pos);
@@ -14100,6 +14128,7 @@ var CURRENCIES = [
1410014128
}, {
1410114129
name: 'Cardano',
1410214130
symbol: 'ada',
14131+
segwitHrp: { prod: 'addr' },
1410314132
validator: ADAValidator,
1410414133
}, {
1410514134
name: 'Monero',

dist/wallet-address-validator.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"browser",
3636
"nodejs"
3737
],
38-
"version": "0.3.21",
38+
"version": "0.3.22",
3939
"author": "Martin <[email protected]>",
4040
"homepage": "https://github.com/trezor/trezor-address-validator",
4141
"license": "MIT",

src/ada_validator.js

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
var cbor = require('cbor-js');
22
var CRC = require('crc');
33
var base58 = require('./crypto/base58');
4+
var bech32 = require('./crypto/bech32');
45

6+
var DEFAULT_NETWORK_TYPE = 'prod';
57

68
function getDecoded(address) {
79
try {
@@ -13,23 +15,48 @@ function getDecoded(address) {
1315
}
1416
}
1517

16-
module.exports = {
17-
isValidAddress: function (address) {
18-
var decoded = getDecoded(address);
18+
function isValidLegacyAddress(address) {
19+
var decoded = getDecoded(address);
20+
21+
if (!decoded || (!Array.isArray(decoded) && decoded.length != 2)) {
22+
return false;
23+
}
24+
25+
var tagged = decoded[0];
26+
var validCrc = decoded[1];
27+
if (typeof (validCrc) != 'number') {
28+
return false;
29+
}
30+
31+
// get crc of the payload
32+
var crc = CRC.crc32(tagged);
33+
34+
return crc == validCrc;
35+
}
1936

20-
if (!decoded || (!Array.isArray(decoded) && decoded.length != 2)) {
21-
return false;
22-
}
37+
// it is not possible to use bitcoin ./crypto/segwit_addr library, the cardano address is too long for that
38+
function isValidBech32Address(address, currency, networkType) {
39+
if (!currency.segwitHrp) {
40+
return false;
41+
}
42+
var hrp = currency.segwitHrp[networkType];
43+
if (!hrp) {
44+
return false;
45+
}
2346

24-
var tagged = decoded[0];
25-
var validCrc = decoded[1];
26-
if (typeof (validCrc) != 'number') {
27-
return false;
28-
}
47+
var dec = bech32.decode(address, 103);
2948

30-
// get crc of the payload
31-
var crc = CRC.crc32(tagged);
49+
if (dec === null || dec.hrp !== hrp || dec.data.length < 1 || dec.data[0] > 16) {
50+
return false;
51+
}
3252

33-
return crc == validCrc;
53+
return true;
54+
}
55+
56+
module.exports = {
57+
isValidAddress: function (address, currency, networkType) {
58+
networkType = networkType || DEFAULT_NETWORK_TYPE;
59+
return isValidLegacyAddress(address)
60+
|| isValidBech32Address(address, currency, networkType);
3461
}
3562
};

src/crypto/bech32.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ function encode (hrp, data) {
7878
return ret;
7979
}
8080

81-
function decode (bechString) {
81+
function decode (bechString, maxLength) {
82+
maxLength=maxLength || 90;
8283
var p;
8384
var has_lower = false;
8485
var has_upper = false;
@@ -98,7 +99,7 @@ function decode (bechString) {
9899
}
99100
bechString = bechString.toLowerCase();
100101
var pos = bechString.lastIndexOf('1');
101-
if (pos < 1 || pos + 7 > bechString.length || bechString.length > 90) {
102+
if (pos < 1 || pos + 7 > bechString.length || bechString.length > maxLength) {
102103
return null;
103104
}
104105
var hrp = bechString.substring(0, pos);

src/currencies.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ var CURRENCIES = [
301301
}, {
302302
name: 'Cardano',
303303
symbol: 'ada',
304+
segwitHrp: { prod: 'addr' },
304305
validator: ADAValidator,
305306
}, {
306307
name: 'Monero',

test/wallet_address_validator.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ describe('WAValidator.validate()', function () {
383383
valid('DdzFFzCqrhsfdzUZxvuBkhV8Lpm9p43p9ubh79GCTkxJikAjKh51qhtCFMqUniC5tv5ZExyvSmAte2Du2tGimavSo6qSgXbjiy8qZRTg', 'ada');
384384
valid('Ae2tdPwUPEZKmwoy3AU3cXb5Chnasj6mvVNxV1H11997q3VW5ihbSfQwGpm', 'ada');
385385
valid('4swhHtxKapQbj3TZEipgtp7NQzcRWDYqCxXYoPQWjGyHmhxS1w1TjUEszCQT1sQucGwmPQMYdv1FYs3d51KgoubviPBf', 'cardano');
386+
valid('addr1qxnv5u3vrx2t37h3u27qd5ukgcjmrl4f8mu9f5sza3h20cxsfjh80un9kvlggfcdw8fp5kqp9tztqnee9msd0qsafhdsyqclvk', 'cardano');
387+
valid('ADDR1QXNV5U3VRX2T37H3U27QD5UKGCJMRL4F8MU9F5SZA3H20CXSFJH80UN9KVLGGFCDW8FP5KQP9TZTQNEE9MSD0QSAFHDSYQCLVK', 'cardano');
388+
valid('addr1qxclz0u9guazk70l9vv3xf67wx3psx3dekasvy43xfvz56qcs6f7ssw2x0fcesudyj8h224rnzkae2lqlnw8f3353t3sjggfx0', 'cardano');
386389
});
387390

388391
it('should return true for correct monero addresses', function () {
@@ -911,6 +914,8 @@ describe('WAValidator.validate()', function () {
911914
invalid('Ae2tdPwUPEYxYNJw1He1esdZYvjmr4NtPzUsGTiqL9zd8ohjZYQcwu6lom7', 'cardano');
912915
invalid('DdzFFzCqrhsfdzUZxvuBkhV8Lpm9p43p9ubh79GCTkxJikAjKh51qhtCFMqUniC5tv5ZExyvSmAte2Du2tGimavSo6qSgXbjiy8qZRTg1', 'cardano');
913916
invalid('DdzFFzCqrhsfdzUZxvuBkhV8Lpm9p43p9ubh79GCTkxJikAjKh51qhtCFMqUniC5tv5ZExyvSmAte2Du2tGimavSo6qSgXbjiy8qZRT', 'ada');
917+
invalid('ADDR1qXNV5U3VRX2T37H3U27QD5UKGCJMRL4F8MU9F5SZA3H20CXSFJH80UN9KVLGGFCDW8FP5KQP9TZTQNEE9MSD0QSAFHDSYQCLVK', 'cardano');
918+
invalid('addr1qxnv5u3vrx2t37h3u27qd5ukgcjmrl4f8mu9f5sza3h20cxsfjh80un9kvlggfcdw8fp5kqp9tztqnee9msd0qsafhdsyqclvl', 'cardano');
914919
//invalid('t2YNzUUx8mWBCRYPRezvA363EYXyEpHokyi', 'komodo', 'testnet');
915920
});
916921

0 commit comments

Comments
 (0)