From 11fc1c1b390b6decedc5945af53d799e8270a1e9 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 23 Apr 2020 18:07:49 -0700 Subject: [PATCH] Enforce hex prefixing for address strings --- docs/modules/_account_.md | 8 ++++---- src/account.ts | 25 ++++++++++++++++--------- src/helpers.ts | 12 ++++++++++++ test/account.spec.ts | 39 ++++++++++++++++++++++++++++++++++++--- test/bytes.spec.ts | 6 ++++++ 5 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 src/helpers.ts diff --git a/docs/modules/_account_.md b/docs/modules/_account_.md index 720a3c3b..12012551 100644 --- a/docs/modules/_account_.md +++ b/docs/modules/_account_.md @@ -116,7 +116,7 @@ ___ *Defined in [account.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L20)* -Checks if the address is a valid. Accepts checksummed addresses too. +Checks if the hex-prefixed address is a valid. Accepts checksummed addresses too. **Parameters:** @@ -134,7 +134,7 @@ ___ *Defined in [account.ts:66](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L66)* -Checks if the address is a valid checksummed address. +Checks if the hex-prefixed address is a valid checksummed address. See toChecksumAddress' documentation for details about the eip1191ChainId parameter. @@ -193,7 +193,7 @@ ___ *Defined in [account.ts:27](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L27)* -Checks if a given address is a zero address. +Checks if a given hex-prefixed address is a zero address. **Parameters:** @@ -267,7 +267,7 @@ ___ *Defined in [account.ts:42](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L42)* -Returns a checksummed address. +Returns a checksummed address. (Input address must be hex-prefixed.) If a eip1191ChainId is provided, the chainId will be included in the checksum calculation. This has the effect of checksummed addresses for one chain having invalid checksums for others. diff --git a/src/account.ts b/src/account.ts index 8eabac82..96f829bc 100644 --- a/src/account.ts +++ b/src/account.ts @@ -2,8 +2,9 @@ const ethjsUtil = require('ethjs-util') import * as assert from 'assert' import * as secp256k1 from 'secp256k1' import * as BN from 'bn.js' -import { toBuffer, addHexPrefix, zeros, bufferToHex, unpad } from './bytes' +import { toBuffer, zeros, bufferToHex, unpad } from './bytes' import { keccak, keccak256, rlphash } from './hash' +import { assertIsHexString } from './helpers' /** * Returns a zero address. @@ -17,16 +18,18 @@ export const zeroAddress = function(): string { /** * Checks if the address is a valid. Accepts checksummed addresses too. */ -export const isValidAddress = function(address: string): boolean { - return /^0x[0-9a-fA-F]{40}$/.test(address) +export const isValidAddress = function(hexAddress: string): boolean { + assertIsHexString(hexAddress) + return /^0x[0-9a-fA-F]{40}$/.test(hexAddress) } /** * Checks if a given address is a zero address. */ -export const isZeroAddress = function(address: string): boolean { +export const isZeroAddress = function(hexAddress: string): boolean { + assertIsHexString(hexAddress) const zeroAddr = zeroAddress() - return zeroAddr === addHexPrefix(address) + return zeroAddr === hexAddress } /** @@ -39,8 +42,9 @@ export const isZeroAddress = function(address: string): boolean { * WARNING: Checksums with and without the chainId will differ. As of 2019-06-26, the most commonly * used variation in Ethereum was without the chainId. This may change in the future. */ -export const toChecksumAddress = function(address: string, eip1191ChainId?: number): string { - address = ethjsUtil.stripHexPrefix(address).toLowerCase() +export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: number): string { + assertIsHexString(hexAddress) + const address = ethjsUtil.stripHexPrefix(hexAddress).toLowerCase() const prefix = eip1191ChainId !== undefined ? eip1191ChainId.toString() + '0x' : '' @@ -63,8 +67,11 @@ export const toChecksumAddress = function(address: string, eip1191ChainId?: numb * * See toChecksumAddress' documentation for details about the eip1191ChainId parameter. */ -export const isValidChecksumAddress = function(address: string, eip1191ChainId?: number): boolean { - return isValidAddress(address) && toChecksumAddress(address, eip1191ChainId) === address +export const isValidChecksumAddress = function( + hexAddress: string, + eip1191ChainId?: number, +): boolean { + return isValidAddress(hexAddress) && toChecksumAddress(hexAddress, eip1191ChainId) === hexAddress } /** diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 00000000..d65cfa96 --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,12 @@ +const ethjsUtil = require('ethjs-util') + +/** + * Throws if a string is not hex prefixed + * @param {string} input string to check hex prefix of + */ +export const assertIsHexString = function(input: string): void { + const msg = `This method only supports 0x-prefixed hex strings but input was: ${input}` + if (!ethjsUtil.isHexString(input)) { + throw new Error(msg) + } +} diff --git a/test/account.spec.ts b/test/account.spec.ts index 20ac9d0e..edc94b1e 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -473,6 +473,14 @@ describe('.toChecksumAddress()', function() { } }) }) + + describe('input format', function() { + it('Should throw when the address is not hex-prefixed', function() { + assert.throws(function() { + toChecksumAddress('52908400098527886E0F7030069857D2E4169EE7'.toLowerCase()) + }) + }) + }) }) describe('.isValidChecksumAddress()', function() { @@ -513,6 +521,14 @@ describe('.isValidChecksumAddress()', function() { } }) }) + + describe('input format', function() { + it('Should throw when the address is not hex-prefixed', function() { + assert.throws(function() { + isValidChecksumAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + }) + }) + }) }) describe('.isValidAddress()', function() { @@ -521,10 +537,27 @@ describe('.isValidAddress()', function() { assert.equal(isValidAddress('0x52908400098527886E0F7030069857D2E4169EE7'), true) }) it('should return false', function() { - assert.equal(isValidAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a'), false) assert.equal(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6'), false) assert.equal(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6aa'), false) - assert.equal(isValidAddress('0X52908400098527886E0F7030069857D2E4169EE7'), false) - assert.equal(isValidAddress('x2f015c60e0be116b1f0cd534704db9c92118fb6a'), false) + }) + it('should throw when input is not hex prefixed', function() { + assert.throws(function() { + isValidAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + }) + assert.throws(function() { + isValidAddress('x2f015c60e0be116b1f0cd534704db9c92118fb6a') + }) + assert.throws(function() { + isValidAddress('0X52908400098527886E0F7030069857D2E4169EE7') + }) + }) + it('error message should have correct format', function() { + const input = '2f015c60e0be116b1f0cd534704db9c92118fb6a' + try { + isValidAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + } catch (err) { + assert(err.message.includes('only supports 0x-prefixed hex strings')) + assert(err.message.includes(input)) + } }) }) diff --git a/test/bytes.spec.ts b/test/bytes.spec.ts index d86b8782..9ddf1639 100644 --- a/test/bytes.spec.ts +++ b/test/bytes.spec.ts @@ -40,6 +40,12 @@ describe('is zero address', function() { const nonZeroAddress = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' assert.equal(isZeroAddress(nonZeroAddress), false) }) + + it('should throw when address is not hex-prefixed', function() { + assert.throws(function() { + isZeroAddress('0000000000000000000000000000000000000000') + }) + }) }) describe('unpad', function() {