From 4f7829fb343fc7068d7e956ce373cb5f922fcac8 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 18 Mar 2019 15:06:38 +0100 Subject: [PATCH 1/4] types: add Address type --- README.md | 2 + docs/README.md | 1 + docs/classes/_address_.address.md | 181 ++++++++++++++++++++++++++++++ docs/modules/_address_.md | 9 ++ src/address.ts | 91 +++++++++++++++ src/bytes.ts | 2 +- src/index.ts | 5 + test/address.spec.ts | 113 +++++++++++++++++++ 8 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 docs/classes/_address_.address.md create mode 100644 docs/modules/_address_.md create mode 100644 src/address.ts create mode 100644 test/address.spec.ts diff --git a/README.md b/README.md index 8722aa69..791a3fc7 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ assert.equal(new BN('dead', 16).add(new BN('101010', 2)), 57047) - [account](docs/modules/_account_.md) - Private/public key and address-related functionality (creation, validation, conversion) +- [address](docs/modules/_address_.md) + - Address class and type - [bytes](docs/modules/_bytes_.md) - Byte-related helper and conversion functions - [constants](docs/modules/_constants_.md) diff --git a/docs/README.md b/docs/README.md index 5d7c9d69..9a7b907a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ ### Modules * ["account"](modules/_account_.md) +* ["address"](modules/_address_.md) * ["bytes"](modules/_bytes_.md) * ["constants"](modules/_constants_.md) * ["externals"](modules/_externals_.md) diff --git a/docs/classes/_address_.address.md b/docs/classes/_address_.address.md new file mode 100644 index 00000000..9e367281 --- /dev/null +++ b/docs/classes/_address_.address.md @@ -0,0 +1,181 @@ +[ethereumjs-util](../README.md) › ["address"](../modules/_address_.md) › [Address](_address_.address.md) + +# Class: Address + +## Hierarchy + +* **Address** + +## Index + +### Constructors + +* [constructor](_address_.address.md#constructor) + +### Properties + +* [buf](_address_.address.md#buf) + +### Methods + +* [isZero](_address_.address.md#iszero) +* [toString](_address_.address.md#tostring) +* [fromPrivateKey](_address_.address.md#static-fromprivatekey) +* [fromPublicKey](_address_.address.md#static-frompublickey) +* [fromString](_address_.address.md#static-fromstring) +* [generate](_address_.address.md#static-generate) +* [generate2](_address_.address.md#static-generate2) +* [zero](_address_.address.md#static-zero) + +## Constructors + +### constructor + +\+ **new Address**(`buf`: Buffer): *[Address](_address_.address.md)* + +*Defined in [address.ts:13](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L13)* + +**Parameters:** + +Name | Type | +------ | ------ | +`buf` | Buffer | + +**Returns:** *[Address](_address_.address.md)* + +## Properties + +### buf + +• **buf**: *Buffer* + +*Defined in [address.ts:13](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L13)* + +## Methods + +### isZero + +▸ **isZero**(): *boolean* + +*Defined in [address.ts:81](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L81)* + +Is address zero. + +**Returns:** *boolean* + +___ + +### toString + +▸ **toString**(): *string* + +*Defined in [address.ts:88](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L88)* + +Returns hex encoding of address. + +**Returns:** *string* + +___ + +### `Static` fromPrivateKey + +▸ **fromPrivateKey**(`privateKey`: Buffer): *[Address](_address_.address.md)* + +*Defined in [address.ts:50](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L50)* + +Returns an address for a given private key. + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`privateKey` | Buffer | A private key must be 256 bits wide | + +**Returns:** *[Address](_address_.address.md)* + +___ + +### `Static` fromPublicKey + +▸ **fromPublicKey**(`pubKey`: Buffer): *[Address](_address_.address.md)* + +*Defined in [address.ts:40](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L40)* + +Returns an address for a given public key. + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`pubKey` | Buffer | The two points of an uncompressed key | + +**Returns:** *[Address](_address_.address.md)* + +___ + +### `Static` fromString + +▸ **fromString**(`str`: string): *[Address](_address_.address.md)* + +*Defined in [address.ts:31](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L31)* + +Returns an Address object from a hex-encoded string. + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`str` | string | Hex-encoded address | + +**Returns:** *[Address](_address_.address.md)* + +___ + +### `Static` generate + +▸ **generate**(`from`: [Address](_address_.address.md), `nonce`: BN): *[Address](_address_.address.md)* + +*Defined in [address.ts:61](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L61)* + +Generates an address for a newly created contract. + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`from` | [Address](_address_.address.md) | The address which is creating this new address | +`nonce` | BN | The nonce of the from account | + +**Returns:** *[Address](_address_.address.md)* + +___ + +### `Static` generate2 + +▸ **generate2**(`from`: [Address](_address_.address.md), `salt`: Buffer, `initCode`: Buffer): *[Address](_address_.address.md)* + +*Defined in [address.ts:72](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L72)* + +Generates an address for a contract created using CREATE2. + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`from` | [Address](_address_.address.md) | The address which is creating this new address | +`salt` | Buffer | A salt | +`initCode` | Buffer | The init code of the contract being created | + +**Returns:** *[Address](_address_.address.md)* + +___ + +### `Static` zero + +▸ **zero**(): *[Address](_address_.address.md)* + +*Defined in [address.ts:23](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L23)* + +Returns the zero address. + +**Returns:** *[Address](_address_.address.md)* diff --git a/docs/modules/_address_.md b/docs/modules/_address_.md new file mode 100644 index 00000000..1c25d7b2 --- /dev/null +++ b/docs/modules/_address_.md @@ -0,0 +1,9 @@ +[ethereumjs-util](../README.md) › ["address"](_address_.md) + +# Module: "address" + +## Index + +### Classes + +* [Address](../classes/_address_.address.md) diff --git a/src/address.ts b/src/address.ts new file mode 100644 index 00000000..d052ce0d --- /dev/null +++ b/src/address.ts @@ -0,0 +1,91 @@ +const assert = require('assert') +import BN = require('bn.js') +import { toBuffer, zeros } from './bytes' +import { + isValidAddress, + pubToAddress, + privateToAddress, + generateAddress, + generateAddress2, +} from './account' + +export class Address { + public readonly buf: Buffer + + constructor(buf: Buffer) { + assert(buf.length === 20, 'Invalid address length') + this.buf = buf + } + + /** + * Returns the zero address. + */ + static zero(): Address { + return new Address(zeros(20)) + } + + /** + * Returns an Address object from a hex-encoded string. + * @param str - Hex-encoded address + */ + static fromString(str: string): Address { + assert(isValidAddress(str), 'Invalid address') + return new Address(toBuffer(str)) + } + + /** + * Returns an address for a given public key. + * @param pubKey The two points of an uncompressed key + */ + static fromPublicKey(pubKey: Buffer): Address { + assert(Buffer.isBuffer(pubKey), 'Public key should be Buffer') + const buf = pubToAddress(pubKey) + return new Address(buf) + } + + /** + * Returns an address for a given private key. + * @param privateKey A private key must be 256 bits wide + */ + static fromPrivateKey(privateKey: Buffer): Address { + assert(Buffer.isBuffer(privateKey), 'Private key should be Buffer') + const buf = privateToAddress(privateKey) + return new Address(buf) + } + + /** + * Generates an address for a newly created contract. + * @param from The address which is creating this new address + * @param nonce The nonce of the from account + */ + static generate(from: Address, nonce: BN): Address { + assert(BN.isBN(nonce)) + return new Address(generateAddress(from.buf, nonce.toArrayLike(Buffer))) + } + + /** + * Generates an address for a contract created using CREATE2. + * @param from The address which is creating this new address + * @param salt A salt + * @param initCode The init code of the contract being created + */ + static generate2(from: Address, salt: Buffer, initCode: Buffer): Address { + assert(Buffer.isBuffer(salt)) + assert(Buffer.isBuffer(initCode)) + return new Address(generateAddress2(from.buf, salt, initCode)) + } + + /** + * Is address zero. + */ + isZero(): boolean { + return this.buf.equals(Address.zero().buf) + } + + /** + * Returns hex encoding of address. + */ + toString(): string { + return '0x' + this.buf.toString('hex') + } +} diff --git a/src/bytes.ts b/src/bytes.ts index 94492aa4..6e938f06 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -111,7 +111,7 @@ const stripZeros = function(a: any): Buffer | number[] | string { export const toBuffer = function(v: any): Buffer { if (!Buffer.isBuffer(v)) { if (Array.isArray(v) || v instanceof Uint8Array) { - v = Buffer.from(v) + v = Buffer.from(v as Uint8Array) } else if (typeof v === 'string') { if (ethjsUtil.isHexString(v)) { v = Buffer.from(ethjsUtil.padToEven(ethjsUtil.stripHexPrefix(v)), 'hex') diff --git a/src/index.ts b/src/index.ts index 2597ae4a..5e27a0ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,11 @@ export * from './constants' */ export * from './account' +/** + * Address typeß + */ +export * from './address' + /** * Hash functions */ diff --git a/test/address.spec.ts b/test/address.spec.ts new file mode 100644 index 00000000..780d23f8 --- /dev/null +++ b/test/address.spec.ts @@ -0,0 +1,113 @@ +import * as assert from 'assert' +import { BN, toBuffer } from '../src' +import { Address } from '../src' +const eip1014Testdata = require('./testdata/eip1014Examples.json') + +describe('Address', () => { + const ZERO_ADDR_S = '0x0000000000000000000000000000000000000000' + + it('should validate address length', () => { + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a11' + assert.throws(() => Address.fromString(str)) + const shortStr = '0x2f015c60e0be116b1f0cd534704db9c92118fb' + assert.throws(() => Address.fromString(shortStr)) + const buf = toBuffer(str) + assert.throws(() => new Address(buf)) + }) + + it('should generate a zero address', () => { + const addr = Address.zero() + assert.deepEqual(addr.buf, toBuffer(ZERO_ADDR_S)) + assert.equal(addr.toString(), ZERO_ADDR_S) + }) + + it('should instantiate address from zero address string', () => { + const addr = Address.fromString(ZERO_ADDR_S) + assert.deepEqual(addr.toString(), ZERO_ADDR_S) + assert.equal(addr.isZero(), true) + }) + + it('should detect non-zero address', () => { + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' + const addr = Address.fromString(str) + assert.equal(addr.isZero(), false) + }) + + it('should instantiate from public key', () => { + const pubKey = Buffer.from( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', + 'hex', + ) + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' + const addr = Address.fromPublicKey(pubKey) + assert.equal(addr.toString(), str) + }) + + it('should fail to instantiate from invalid public key', () => { + const pubKey = Buffer.from( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', + 'hex', + ) + assert.throws(() => Address.fromPublicKey(pubKey)) + }) + + it('should instantiate from private key', () => { + const privateKey = Buffer.from([ + 234, + 84, + 189, + 197, + 45, + 22, + 63, + 136, + 201, + 58, + 176, + 97, + 87, + 130, + 207, + 113, + 138, + 46, + 251, + 158, + 81, + 167, + 152, + 154, + 171, + 27, + 8, + 6, + 126, + 156, + 28, + 95, + ]) + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' + const addr = Address.fromPrivateKey(privateKey) + assert.equal(addr.toString(), str) + }) + + it('should generate address for created contract', () => { + const from = Address.fromString('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') + const addr = Address.generate(from, new BN(14)) + assert.equal(addr.toString(), '0xd658a4b8247c14868f3c512fa5cbb6e458e4a989') + + const addr2 = Address.generate(from, new BN(0)) + assert.equal(addr2.toString(), '0xbfa69ba91385206bfdd2d8b9c1a5d6c10097a85b') + }) + + it('should generate address for CREATE2', () => { + for (let i = 0; i <= 6; i++) { + let e = eip1014Testdata[i] + it(`${e['comment']}: should generate the addresses provided`, function() { + const from = Address.fromString(e['address']) + const addr = Address.generate2(from, toBuffer(e['salt']), toBuffer(e['initCode'])) + assert.equal(addr.toString(), e['result']) + }) + } + }) +}) From 5b0af49190182ffb4a162b837123fbeb78c6c6a2 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Mon, 3 Aug 2020 21:04:38 -0700 Subject: [PATCH 2/4] add a prettier-ignore for privkey --- test/address.spec.ts | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/test/address.spec.ts b/test/address.spec.ts index 780d23f8..9dc23d68 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -52,40 +52,8 @@ describe('Address', () => { }) it('should instantiate from private key', () => { - const privateKey = Buffer.from([ - 234, - 84, - 189, - 197, - 45, - 22, - 63, - 136, - 201, - 58, - 176, - 97, - 87, - 130, - 207, - 113, - 138, - 46, - 251, - 158, - 81, - 167, - 152, - 154, - 171, - 27, - 8, - 6, - 126, - 156, - 28, - 95, - ]) + // prettier-ignore + const privateKey = Buffer.from([234, 84, 189, 197, 45, 22, 63, 136, 201, 58, 176, 97, 87, 130, 207, 113, 138, 46, 251, 158, 81, 167, 152, 154, 171, 27, 8, 6, 126, 156, 28, 95]) const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const addr = Address.fromPrivateKey(privateKey) assert.equal(addr.toString(), str) From 5a482935defa650d989232d3f92e0857081f1d63 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 4 Aug 2020 10:42:42 -0700 Subject: [PATCH 3/4] fix test to increase coverage (nested `it` wasn't being run) --- test/address.spec.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/test/address.spec.ts b/test/address.spec.ts index 9dc23d68..ede4bbda 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -69,13 +69,11 @@ describe('Address', () => { }) it('should generate address for CREATE2', () => { - for (let i = 0; i <= 6; i++) { - let e = eip1014Testdata[i] - it(`${e['comment']}: should generate the addresses provided`, function() { - const from = Address.fromString(e['address']) - const addr = Address.generate2(from, toBuffer(e['salt']), toBuffer(e['initCode'])) - assert.equal(addr.toString(), e['result']) - }) + for (const testdata of eip1014Testdata) { + const { address, salt, initCode, result } = testdata + const from = Address.fromString(address) + const addr = Address.generate2(from, toBuffer(salt), toBuffer(initCode)) + assert.equal(addr.toString(), result) } }) }) From 37a74c169152fce044421c2f3267880a1f498e3c Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Wed, 5 Aug 2020 09:26:05 -0700 Subject: [PATCH 4/4] Update src/index.ts --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 5e27a0ce..bf462b49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ export * from './constants' export * from './account' /** - * Address typeß + * Address type */ export * from './address'