Skip to content
This repository was archived by the owner on May 26, 2023. It is now read-only.

Commit 9d787fc

Browse files
committed
feat(crypto): incorporate changes from #65
1 parent a330072 commit 9d787fc

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

packages/zilliqa-js-crypto/src/util.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import elliptic from 'elliptic';
22
import hashjs from 'hash.js';
33

4+
import { validation } from '@zilliqa-js/util';
5+
46
const secp256k1 = elliptic.ec('secp256k1');
57
/**
68
* getAddressFromPrivateKey
@@ -63,6 +65,46 @@ export const getAddressFromPublicKey = (publicKey: string) => {
6365
.slice(24);
6466
};
6567

68+
/**
69+
* toChecksumAddress
70+
*
71+
* takes hex-encoded string and returns the corresponding address
72+
*
73+
* @param {string} address
74+
* @returns {string}
75+
*/
76+
export const toChecksumAddress = (address: string): string => {
77+
address = address.toLowerCase().replace('0x', '');
78+
const hash = hashjs
79+
.sha256()
80+
.update(address, 'hex')
81+
.digest('hex');
82+
let ret = '0x';
83+
for (let i = 0; i < address.length; i++) {
84+
if (parseInt(hash[i], 16) >= 8) {
85+
ret += address[i].toUpperCase();
86+
} else {
87+
ret += address[i];
88+
}
89+
}
90+
return ret;
91+
};
92+
93+
/**
94+
* isValidChecksumAddress
95+
*
96+
* takes hex-encoded string and returns boolean if address is checksumed
97+
*
98+
* @param {string} address
99+
* @returns {boolean}
100+
*/
101+
export const isValidChecksumAddress = (address: string): boolean => {
102+
return (
103+
validation.isAddress(address.replace('0x', '')) &&
104+
toChecksumAddress(address) === address
105+
);
106+
};
107+
66108
/**
67109
* verifyPrivateKey
68110
*

packages/zilliqa-js-crypto/test/address.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { addresses } from './address.fixtures';
2+
import { checksummedStore } from './checksum.fixtures';
23
import * as crypto from '../src/index';
34

45
describe('addresses', () => {
@@ -13,4 +14,26 @@ describe('addresses', () => {
1314
expect(addressFromPub.toUpperCase()).toEqual(address);
1415
});
1516
});
17+
18+
it('should return a valid 0x prefixed checksummed address', () => {
19+
checksummedStore.forEach(({ original: address, good: expected }) => {
20+
const actual = crypto.toChecksumAddress(address);
21+
expect(actual).toEqual(expected);
22+
expect(actual.substr(0, 2)).toEqual('0x');
23+
});
24+
});
25+
26+
it('should return true when a valid checksummed address is tested', () => {
27+
checksummedStore.forEach(({ good: checksummed }) => {
28+
const actual = crypto.isValidChecksumAddress(checksummed);
29+
expect(actual).toBeTruthy();
30+
});
31+
});
32+
33+
it('should return false when an invalid checksummed address is tested', () => {
34+
checksummedStore.forEach(({ bad: badlychecksummed }) => {
35+
const actual = crypto.isValidChecksumAddress(badlychecksummed);
36+
expect(actual).toBeFalsy();
37+
});
38+
});
1639
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
export const checksummedStore = [
2+
{
3+
original: '4BAF5FADA8E5DB92C3D3242618C5B47133AE003C',
4+
good_no0x: '4BaF5fADa8E5Db92c3D3242618c5b47133Ae003c',
5+
good: '0x4BaF5fADa8E5Db92c3D3242618c5b47133Ae003c',
6+
bad: '0x4baF5fADa8E5Db92c3D3242618c5b47133Ae003c', // first b is lowercase
7+
},
8+
{
9+
original: '448261915A80CDE9BDE7C7A791685200D3A0BF4E',
10+
good_no0x: '448261915a80CDe9bde7C7A791685200d3A0BF4e',
11+
good: '0x448261915a80CDe9bde7C7A791685200d3A0BF4e',
12+
bad: '0x448261915a80cDe9bde7C7A791685200d3A0BF4e', // first c is lowercase
13+
},
14+
{
15+
original: 'DED02FD979FC2E55C0243BD2F52DF022C40ADA1E',
16+
good_no0x: 'DED02FD979fC2e55c0243Bd2f52DF022C40aDa1E',
17+
good: '0xDED02FD979fC2e55c0243Bd2f52DF022C40aDa1E',
18+
bad: '0xdED02FD979fC2e55c0243Bd2f52DF022C40aDa1E', // first d is lowercase
19+
},
20+
{
21+
original: '13F06E60297BEA6A3C402F6F64C416A6B31E586E',
22+
good_no0x: '13f06E60297bEA6A3C402F6F64c416a6B31e586e',
23+
good: '0x13f06E60297bEA6A3C402F6F64c416a6B31e586e',
24+
bad: '0x13F06E60297bEA6A3C402F6F64c416a6B31e586e', // first f is uppercase
25+
},
26+
{
27+
original: '1A90C25307C3CC71958A83FA213A2362D859CF33',
28+
good_no0x: '1a90c25307c3Cc71958A83fa213a2362D859cF33',
29+
good: '0x1a90c25307c3Cc71958A83fa213a2362D859cF33',
30+
bad: '0x1A90c25307c3Cc71958A83fa213a2362D859cF33', // first a is uppercase
31+
},
32+
{
33+
original: '625ABAEBD87DAE9AB128F3B3AE99688813D9C5DF',
34+
good_no0x: '625aBAEBd87Dae9AB128F3b3ae99688813d9C5Df',
35+
good: '0x625aBAEBd87Dae9AB128F3b3ae99688813d9C5Df',
36+
bad: '0x625AbAEBd87Dae9AB128F3b3ae99688813d9C5Df', // first A is uppercase, b is lowercase
37+
},
38+
{
39+
original: '36BA34097F861191C48C839C9B1A8B5912F583CF',
40+
good_no0x: '36BA34097f861191c48c839c9B1A8B5912f583cf',
41+
good: '0x36BA34097f861191c48c839c9B1A8B5912f583cf',
42+
bad: '0x36bA34097f861191c48c839c9B1A8B5912f583cF', // first b is lowercase, last F is uppercase
43+
},
44+
{
45+
original: 'D2453AE76C9A86AAE544FCA699DBDC5C576AEF3A',
46+
good_no0x: 'D2453AE76c9a86AAE544FCa699DBdC5C576aEf3A',
47+
good: '0xD2453AE76c9a86AAE544FCa699DBdC5C576aEf3A',
48+
bad: '0xd2453aE76c9a86AaE544FCA699DBdC5C576Aef3A', // random lowercase / upperacase
49+
},
50+
{
51+
original: '72220E84947C36118CDBC580454DFAA3B918CD97',
52+
good_no0x: '72220E84947c36118CDbc580454DfaA3B918cd97',
53+
good: '0x72220E84947c36118CDbc580454DfaA3B918cd97',
54+
bad: '0x72220e84947C36118cdBC580454dFAa3b918CD97', // inverted cases
55+
},
56+
{
57+
original: '50F92304C892D94A385CA6CE6CD6950CE9A36839',
58+
good_no0x: '50f92304c892d94A385Ca6ce6cD6950ce9A36839',
59+
good: '0x50f92304c892d94A385Ca6ce6cD6950ce9A36839',
60+
bad: '0x50F92304c892D94A385Ca6ce6CD6950ce9A36839', // random lowercase / upperacase
61+
},
62+
];

0 commit comments

Comments
 (0)