From a3bfdc8858dfd89ebc7d154e62da45a6ddbdebd3 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Fri, 25 Sep 2020 14:20:15 -0700 Subject: [PATCH 01/46] add Account class and tests --- CHANGELOG.md | 23 +- README.md | 1 + docs/README.md | 1 + docs/classes/_account_.account.md | 167 +++++++++++ docs/interfaces/_account_.accountdata.md | 48 ++++ docs/modules/__types_ethjs_util_index_.md | 5 + docs/modules/_account_.md | 36 ++- docs/modules/_externals_.md | 1 - docs/modules/_hash_.md | 22 +- package.json | 2 +- src/account.ts | 156 +++++++++-- src/index.ts | 2 +- test/account.spec.ts | 327 +++++++++++++--------- typedoc.js | 6 +- 14 files changed, 606 insertions(+), 191 deletions(-) create mode 100644 docs/classes/_account_.account.md create mode 100644 docs/interfaces/_account_.accountdata.md create mode 100644 docs/modules/__types_ethjs_util_index_.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a0e598..d4474e09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,28 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [7.0.6] - [UNRELEASED] -[ ADD REFERENCES TO YOUR WORK HERE UPON PRs. PLEASE ADOPT THE VERSION IF YOUR PR REQUIRES. ] +This release adds a new `Account` class intended as a modern replacement for `ethereumjs-account`. It has a shape of `Account(nonce?: BN, balance?: BN, stateRoot?: Buffer, codeHash?: Buffer)`. + +**Instantiation** + +The static factory methods assist in creating an `Account` object from varying data types: `Object: fromAccountData`, `RLP: fromRlpSerializedAccount`, and `Array: fromValuesArray`. + +**Methods**: `isEmpty(): boolean`, `isContract(): boolean`, `serialize(): Buffer` + +Example usage: + +```typescript +import { Account, BN } from 'ethereumjs-util' + +const account = new Account( + new BN(0), // nonce, default: 0 + new BN(10).pow(new BN(18)), // balance, default: 0 + undefined, // stateRoot, default: KECCAK256_RLP (hash of RLP of null) + undefined, // codeHash, default: KECCAK256_NULL (hash of null) +) +``` + +For more info see the documentation or examples of usage in `test/account.spec.ts`. ## [7.0.5] - 2020-09-09 diff --git a/README.md b/README.md index 95733ae0..654c9561 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ assert.equal(new BN('dead', 16).add(new BN('101010', 2)), 57047) ### Modules - [account](docs/modules/_account_.md) + - Account class - Private/public key and address-related functionality (creation, validation, conversion) - [address](docs/modules/_address_.md) - Address class and type diff --git a/docs/README.md b/docs/README.md index 9a7b907a..7dff9c06 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,7 @@ ### Modules +* ["@types/ethjs-util/index"](modules/__types_ethjs_util_index_.md) * ["account"](modules/_account_.md) * ["address"](modules/_address_.md) * ["bytes"](modules/_bytes_.md) diff --git a/docs/classes/_account_.account.md b/docs/classes/_account_.account.md new file mode 100644 index 00000000..4f3d33d8 --- /dev/null +++ b/docs/classes/_account_.account.md @@ -0,0 +1,167 @@ +[ethereumjs-util](../README.md) › ["account"](../modules/_account_.md) › [Account](_account_.account.md) + +# Class: Account + +## Hierarchy + +* **Account** + +## Index + +### Constructors + +* [constructor](_account_.account.md#constructor) + +### Properties + +* [balance](_account_.account.md#balance) +* [codeHash](_account_.account.md#codehash) +* [nonce](_account_.account.md#nonce) +* [stateRoot](_account_.account.md#stateroot) + +### Methods + +* [isContract](_account_.account.md#iscontract) +* [isEmpty](_account_.account.md#isempty) +* [serialize](_account_.account.md#serialize) +* [fromAccountData](_account_.account.md#static-fromaccountdata) +* [fromRlpSerializedAccount](_account_.account.md#static-fromrlpserializedaccount) +* [fromValuesArray](_account_.account.md#static-fromvaluesarray) + +## Constructors + +### constructor + +\+ **new Account**(`nonce`: BN‹›, `balance`: BN‹›, `stateRoot`: Buffer‹›, `codeHash`: Buffer‹›): *[Account](_account_.account.md)* + +*Defined in [account.ts:77](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L77)* + +This constructor takes the values, validates and assigns them. +Use the static factory methods to assist in creating an Account from varying data types. + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`nonce` | BN‹› | new BN(0) | +`balance` | BN‹› | new BN(0) | +`stateRoot` | Buffer‹› | KECCAK256_RLP | +`codeHash` | Buffer‹› | KECCAK256_NULL | + +**Returns:** *[Account](_account_.account.md)* + +## Properties + +### balance + +• **balance**: *BN* + +*Defined in [account.ts:43](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L43)* + +___ + +### codeHash + +• **codeHash**: *Buffer* + +*Defined in [account.ts:45](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L45)* + +___ + +### nonce + +• **nonce**: *BN* + +*Defined in [account.ts:42](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L42)* + +___ + +### stateRoot + +• **stateRoot**: *Buffer* + +*Defined in [account.ts:44](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L44)* + +## Methods + +### isContract + +▸ **isContract**(): *boolean* + +*Defined in [account.ts:112](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L112)* + +Returns a `Boolean` deteremining if the account is a contract. + +**Returns:** *boolean* + +___ + +### isEmpty + +▸ **isEmpty**(): *boolean* + +*Defined in [account.ts:119](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L119)* + +Returns a `Boolean` determining if the account is empty. + +**Returns:** *boolean* + +___ + +### serialize + +▸ **serialize**(): *Buffer* + +*Defined in [account.ts:105](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L105)* + +Returns the RLP serialization of the account as a `Buffer`. + +**Returns:** *Buffer* + +___ + +### `Static` fromAccountData + +▸ **fromAccountData**(`accountData`: [AccountData](../interfaces/_account_.accountdata.md)): *[Account](_account_.account.md)‹›* + +*Defined in [account.ts:47](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L47)* + +**Parameters:** + +Name | Type | +------ | ------ | +`accountData` | [AccountData](../interfaces/_account_.accountdata.md) | + +**Returns:** *[Account](_account_.account.md)‹›* + +___ + +### `Static` fromRlpSerializedAccount + +▸ **fromRlpSerializedAccount**(`serialized`: Buffer): *[Account](_account_.account.md)‹›* + +*Defined in [account.ts:58](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L58)* + +**Parameters:** + +Name | Type | +------ | ------ | +`serialized` | Buffer | + +**Returns:** *[Account](_account_.account.md)‹›* + +___ + +### `Static` fromValuesArray + +▸ **fromValuesArray**(`values`: Buffer[]): *[Account](_account_.account.md)‹›* + +*Defined in [account.ts:68](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L68)* + +**Parameters:** + +Name | Type | +------ | ------ | +`values` | Buffer[] | + +**Returns:** *[Account](_account_.account.md)‹›* diff --git a/docs/interfaces/_account_.accountdata.md b/docs/interfaces/_account_.accountdata.md new file mode 100644 index 00000000..698b49df --- /dev/null +++ b/docs/interfaces/_account_.accountdata.md @@ -0,0 +1,48 @@ +[ethereumjs-util](../README.md) › ["account"](../modules/_account_.md) › [AccountData](_account_.accountdata.md) + +# Interface: AccountData + +## Hierarchy + +* **AccountData** + +## Index + +### Properties + +* [balance](_account_.accountdata.md#optional-balance) +* [codeHash](_account_.accountdata.md#optional-codehash) +* [nonce](_account_.accountdata.md#optional-nonce) +* [stateRoot](_account_.accountdata.md#optional-stateroot) + +## Properties + +### `Optional` balance + +• **balance**? : *BNLike* + +*Defined in [account.ts:19](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L19)* + +___ + +### `Optional` codeHash + +• **codeHash**? : *BufferLike* + +*Defined in [account.ts:21](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L21)* + +___ + +### `Optional` nonce + +• **nonce**? : *BNLike* + +*Defined in [account.ts:18](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L18)* + +___ + +### `Optional` stateRoot + +• **stateRoot**? : *BufferLike* + +*Defined in [account.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L20)* diff --git a/docs/modules/__types_ethjs_util_index_.md b/docs/modules/__types_ethjs_util_index_.md new file mode 100644 index 00000000..45cd1a7f --- /dev/null +++ b/docs/modules/__types_ethjs_util_index_.md @@ -0,0 +1,5 @@ +[ethereumjs-util](../README.md) › ["@types/ethjs-util/index"](__types_ethjs_util_index_.md) + +# Module: "@types/ethjs-util/index" + + diff --git a/docs/modules/_account_.md b/docs/modules/_account_.md index b7a054eb..08125b1d 100644 --- a/docs/modules/_account_.md +++ b/docs/modules/_account_.md @@ -4,6 +4,14 @@ ## Index +### Classes + +* [Account](../classes/_account_.account.md) + +### Interfaces + +* [AccountData](../interfaces/_account_.accountdata.md) + ### Variables * [publicToAddress](_account_.md#const-publictoaddress) @@ -30,7 +38,7 @@ • **publicToAddress**: *pubToAddress* = pubToAddress -*Defined in [account.ts:160](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L160)* +*Defined in [account.ts:257](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L257)* ## Functions @@ -38,7 +46,7 @@ ▸ **generateAddress**(`from`: Buffer, `nonce`: Buffer): *Buffer* -*Defined in [account.ts:82](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L82)* +*Defined in [account.ts:179](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L179)* Generates an address of a newly created contract. @@ -57,7 +65,7 @@ ___ ▸ **generateAddress2**(`from`: Buffer, `salt`: Buffer, `initCode`: Buffer): *Buffer* -*Defined in [account.ts:103](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L103)* +*Defined in [account.ts:200](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L200)* Generates an address for a contract created using CREATE2. @@ -77,7 +85,7 @@ ___ ▸ **importPublic**(`publicKey`: Buffer): *Buffer* -*Defined in [account.ts:183](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L183)* +*Defined in [account.ts:280](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L280)* Converts a public key to the Ethereum format. @@ -95,7 +103,7 @@ ___ ▸ **isValidAddress**(`hexAddress`: string): *boolean* -*Defined in [account.ts:21](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L21)* +*Defined in [account.ts:127](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L127)* Checks if the address is a valid. Accepts checksummed addresses too. @@ -113,7 +121,7 @@ ___ ▸ **isValidChecksumAddress**(`hexAddress`: string, `eip1191ChainId?`: undefined | number): *boolean* -*Defined in [account.ts:70](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L70)* +*Defined in [account.ts:167](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L167)* Checks if the address is a valid checksummed address. @@ -134,7 +142,7 @@ ___ ▸ **isValidPrivate**(`privateKey`: Buffer): *boolean* -*Defined in [account.ts:121](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L121)* +*Defined in [account.ts:218](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L218)* Checks if the private key satisfies the rules of the curve secp256k1. @@ -152,7 +160,7 @@ ___ ▸ **isValidPublic**(`publicKey`: Buffer, `sanitize`: boolean): *boolean* -*Defined in [account.ts:131](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L131)* +*Defined in [account.ts:228](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L228)* Checks if the public key satisfies the rules of the curve secp256k1 and the requirements of Ethereum. @@ -172,7 +180,7 @@ ___ ▸ **isZeroAddress**(`hexAddress`: string): *boolean* -*Defined in [account.ts:29](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L29)* +*Defined in [account.ts:300](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L300)* Checks if a given address is a zero address. @@ -190,7 +198,7 @@ ___ ▸ **privateToAddress**(`privateKey`: Buffer): *Buffer* -*Defined in [account.ts:166](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L166)* +*Defined in [account.ts:263](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L263)* Returns the ethereum address of a given private key. @@ -208,7 +216,7 @@ ___ ▸ **privateToPublic**(`privateKey`: Buffer): *Buffer* -*Defined in [account.ts:174](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L174)* +*Defined in [account.ts:271](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L271)* Returns the ethereum public key of a given private key. @@ -226,7 +234,7 @@ ___ ▸ **pubToAddress**(`pubKey`: Buffer, `sanitize`: boolean): *Buffer* -*Defined in [account.ts:151](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L151)* +*Defined in [account.ts:248](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L248)* Returns the ethereum address of a given public key. Accepts "Ethereum public keys" and SEC1 encoded keys. @@ -246,7 +254,7 @@ ___ ▸ **toChecksumAddress**(`hexAddress`: string, `eip1191ChainId?`: undefined | number): *string* -*Defined in [account.ts:45](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L45)* +*Defined in [account.ts:142](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L142)* Returns a checksummed address. @@ -272,7 +280,7 @@ ___ ▸ **zeroAddress**(): *string* -*Defined in [account.ts:12](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L12)* +*Defined in [account.ts:291](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L291)* Returns a zero address. diff --git a/docs/modules/_externals_.md b/docs/modules/_externals_.md index 2d9a6773..6381f333 100644 --- a/docs/modules/_externals_.md +++ b/docs/modules/_externals_.md @@ -3,5 +3,4 @@ # Module: "externals" Re-exports commonly used modules: -* Adds [`ethjs-util`](https://github.com/ethjs/ethjs-util) methods. * Exports [`BN`](https://github.com/indutny/bn.js), [`rlp`](https://github.com/ethereumjs/rlp). diff --git a/docs/modules/_hash_.md b/docs/modules/_hash_.md index da56fa04..5f515614 100644 --- a/docs/modules/_hash_.md +++ b/docs/modules/_hash_.md @@ -44,7 +44,7 @@ ___ ▸ **keccak256**(`a`: Buffer): *Buffer* -*Defined in [hash.ts:24](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L24)* +*Defined in [hash.ts:38](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L38)* Creates Keccak-256 hash of the input, alias for keccak(a, 256). @@ -62,7 +62,7 @@ ___ ▸ **keccakFromArray**(`a`: number[], `bits`: number): *Buffer‹›* -*Defined in [hash.ts:54](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L54)* +*Defined in [hash.ts:68](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L68)* Creates Keccak hash of a number array input @@ -81,7 +81,7 @@ ___ ▸ **keccakFromHexString**(`a`: string, `bits`: number): *Buffer‹›* -*Defined in [hash.ts:44](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L44)* +*Defined in [hash.ts:58](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L58)* Creates Keccak hash of an 0x-prefixed string input @@ -100,7 +100,7 @@ ___ ▸ **keccakFromString**(`a`: string, `bits`: number): *Buffer‹›* -*Defined in [hash.ts:33](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L33)* +*Defined in [hash.ts:47](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L47)* Creates Keccak hash of a utf-8 string input @@ -119,7 +119,7 @@ ___ ▸ **ripemd160**(`a`: Buffer, `padded`: boolean): *Buffer* -*Defined in [hash.ts:102](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L102)* +*Defined in [hash.ts:116](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L116)* Creates RIPEMD160 hash of a Buffer input. @@ -138,7 +138,7 @@ ___ ▸ **ripemd160FromArray**(`a`: number[], `padded`: boolean): *Buffer* -*Defined in [hash.ts:122](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L122)* +*Defined in [hash.ts:136](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L136)* Creates RIPEMD160 hash of a number[] input. @@ -157,7 +157,7 @@ ___ ▸ **ripemd160FromString**(`a`: string, `padded`: boolean): *Buffer* -*Defined in [hash.ts:112](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L112)* +*Defined in [hash.ts:126](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L126)* Creates RIPEMD160 hash of a string input. @@ -176,7 +176,7 @@ ___ ▸ **rlphash**(`a`: rlp.Input): *Buffer* -*Defined in [hash.ts:148](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L148)* +*Defined in [hash.ts:162](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L162)* Creates SHA-3 hash of the RLP encoded version of the input. @@ -194,7 +194,7 @@ ___ ▸ **sha256**(`a`: Buffer): *Buffer* -*Defined in [hash.ts:63](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L63)* +*Defined in [hash.ts:77](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L77)* Creates SHA256 hash of a Buffer input. @@ -212,7 +212,7 @@ ___ ▸ **sha256FromArray**(`a`: number[]): *Buffer* -*Defined in [hash.ts:81](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L81)* +*Defined in [hash.ts:95](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L95)* Creates SHA256 hash of a number[] input. @@ -230,7 +230,7 @@ ___ ▸ **sha256FromString**(`a`: string): *Buffer* -*Defined in [hash.ts:72](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L72)* +*Defined in [hash.ts:86](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/hash.ts#L86)* Creates SHA256 hash of a string input. diff --git a/package.json b/package.json index 2ee6ec6d..da275c6e 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "lint:fix": "ethereumjs-config-lint-fix", "test": "npm run lint && npm run test:node && npm run test:browser", "test:browser": "karma start karma.conf.js", - "test:node": "nyc --reporter=lcov mocha 'test/*.spec.ts' -- --require ts-node/register", + "test:node": "nyc --reporter=lcov mocha --require ts-node/register 'test/*.spec.ts'", "tsc": "ethereumjs-config-tsc", "tslint": "ethereumjs-config-tslint", "tslint:fix": "ethereumjs-config-tslint-fix" diff --git a/src/account.ts b/src/account.ts index 125ef2d9..ed2f0aed 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,23 +1,124 @@ -import * as ethjsUtil from 'ethjs-util' +import * as assert from 'assert' +import * as BN from 'bn.js' +import * as rlp from 'rlp' +import { stripHexPrefix } from 'ethjs-util' +import { KECCAK256_RLP, KECCAK256_NULL } from './constants' +import { zeros, bufferToHex, toBuffer, unpadBuffer } from './bytes' +import { keccak, keccak256, keccakFromString, rlphash } from './hash' +import { assertIsHexString, assertIsBuffer } from './helpers' + const { privateKeyVerify, publicKeyCreate, publicKeyVerify, publicKeyConvert, } = require('ethereum-cryptography/secp256k1') -import * as assert from 'assert' -import * as BN from 'bn.js' -import { zeros, bufferToHex } from './bytes' -import { keccak, keccak256, keccakFromString, rlphash } from './hash' -import { assertIsHexString, assertIsBuffer } from './helpers' -/** - * Returns a zero address. - */ -export const zeroAddress = function(): string { - const addressLength = 20 - const addr = zeros(addressLength) - return bufferToHex(addr) +export interface AccountData { + nonce?: BNLike + balance?: BNLike + stateRoot?: BufferLike + codeHash?: BufferLike +} + +type BNLike = BN | string | number + +type BufferLike = Buffer | TransformableToBuffer | PrefixedHexString | number + +type PrefixedHexString = string + +interface TransformableToBuffer { + toBuffer(): Buffer +} + +/* Helper for Account.serialize() */ +function bnToRlp(value: BN): Buffer { + // using `bn.toArrayLike(Buffer)` instead of `bn.toBuffer()` + // for compatibility with browserify and similar tools + return unpadBuffer(value.toArrayLike(Buffer)) +} + +export class Account { + nonce: BN + balance: BN + stateRoot: Buffer + codeHash: Buffer + + static fromAccountData(accountData: AccountData) { + const { nonce, balance, stateRoot, codeHash } = accountData + + return new Account( + nonce ? new BN(toBuffer(nonce)) : undefined, + balance ? new BN(toBuffer(balance)) : undefined, + stateRoot ? toBuffer(stateRoot) : undefined, + codeHash ? toBuffer(codeHash) : undefined, + ) + } + + public static fromRlpSerializedAccount(serialized: Buffer) { + const values = rlp.decode(serialized) + + if (!Array.isArray(values)) { + throw new Error('Invalid serialized account input. Must be array') + } + + return this.fromValuesArray(values) + } + + public static fromValuesArray(values: Buffer[]) { + const [nonce, balance, stateRoot, codeHash] = values + + return new Account( + nonce ? new BN(nonce) : undefined, + balance ? new BN(balance) : undefined, + stateRoot, + codeHash, + ) + } + + /** + * This constructor takes the values, validates and assigns them. + * Use the static factory methods to assist in creating an Account from varying data types. + */ + constructor( + nonce = new BN(0), + balance = new BN(0), + stateRoot = KECCAK256_RLP, + codeHash = KECCAK256_NULL, + ) { + if (stateRoot.length !== 32) { + throw new Error('stateRoot must have a length of 32') + } + if (codeHash.length !== 32) { + throw new Error('codeHash must have a length of 32') + } + + this.nonce = nonce + this.balance = balance + this.stateRoot = stateRoot + this.codeHash = codeHash + } + + /** + * Returns the RLP serialization of the account as a `Buffer`. + */ + serialize(): Buffer { + return rlp.encode([bnToRlp(this.nonce), bnToRlp(this.balance), this.stateRoot, this.codeHash]) + } + + /** + * Returns a `Boolean` deteremining if the account is a contract. + */ + isContract(): boolean { + return !this.codeHash.equals(KECCAK256_NULL) + } + + /** + * Returns a `Boolean` determining if the account is empty. + */ + isEmpty(): boolean { + return this.balance.isZero() && this.nonce.isZero() && this.codeHash.equals(KECCAK256_NULL) + } } /** @@ -28,15 +129,6 @@ export const isValidAddress = function(hexAddress: string): boolean { return /^0x[0-9a-fA-F]{40}$/.test(hexAddress) } -/** - * Checks if a given address is a zero address. - */ -export const isZeroAddress = function(hexAddress: string): boolean { - assertIsHexString(hexAddress) - const zeroAddr = zeroAddress() - return zeroAddr === hexAddress -} - /** * Returns a checksummed address. * @@ -49,7 +141,7 @@ export const isZeroAddress = function(hexAddress: string): boolean { */ export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: number): string { assertIsHexString(hexAddress) - const address = ethjsUtil.stripHexPrefix(hexAddress).toLowerCase() + const address = stripHexPrefix(hexAddress).toLowerCase() const prefix = eip1191ChainId !== undefined ? eip1191ChainId.toString() + '0x' : '' @@ -192,3 +284,21 @@ export const importPublic = function(publicKey: Buffer): Buffer { } return publicKey } + +/** + * Returns a zero address. + */ +export const zeroAddress = function(): string { + const addressLength = 20 + const addr = zeros(addressLength) + return bufferToHex(addr) +} + +/** + * Checks if a given address is a zero address. + */ +export const isZeroAddress = function(hexAddress: string): boolean { + assertIsHexString(hexAddress) + const zeroAddr = zeroAddress() + return zeroAddr === hexAddress +} diff --git a/src/index.ts b/src/index.ts index bf462b49..ff60d581 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ export * from './constants' /** - * Public-key cryptography (secp256k1) and addresses + * Account class and helper functions */ export * from './account' diff --git a/test/account.spec.ts b/test/account.spec.ts index 44df2c17..241768ad 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -1,6 +1,8 @@ import * as assert from 'assert' import * as BN from 'bn.js' +import * as rlp from 'rlp' import { + Account, isValidPrivate, isValidPublic, importPublic, @@ -16,6 +18,187 @@ import { } from '../src' const eip1014Testdata = require('./testdata/eip1014Examples.json') +describe('Account', function() { + describe('empty constructor', function() { + const account = new Account() + it('should have zero nonce', function() { + assert.ok(account.nonce.isZero()) + }) + it('should have zero balance', function() { + assert.ok(account.balance.isZero()) + }) + it('should have stateRoot equal to KECCAK256_RLP', function() { + assert.ok( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + ) + }) + it('should have codeHash equal to KECCAK256_NULL', function() { + assert.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ) + }) + }) + + describe('from Array data', function() { + const raw = [ + '0x02', // nonce + '0x0384', // balance + '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', // stateRoot + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', // codeHash + ] + const account = Account.fromValuesArray(raw.map(toBuffer)) + it('should have correct nonce', function() { + assert.ok(account.nonce.eqn(2)) + }) + it('should have correct balance', function() { + assert.ok(account.balance.eqn(900)) + }) + it('should have correct stateRoot', function() { + assert.equal( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + ) + }) + it('should have correct codeHash', function() { + assert.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ) + }) + }) + + describe('from Object data', function() { + const raw = { + nonce: '0x02', + balance: '0x0384', + stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + } + const account = Account.fromAccountData(raw) + it('should have correct nonce', function() { + assert.ok(account.nonce.eqn(2)) + }) + it('should have correct balance', function() { + assert.ok(account.balance.eqn(900)) + }) + it('should have correct stateRoot', function() { + assert.equal( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + ) + }) + it('should have correct codeHash', function() { + assert.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ) + }) + }) + + describe('from RLP data', function() { + const accountRlp = Buffer.from( + 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'hex', + ) + const account = Account.fromRlpSerializedAccount(accountRlp) + it('should have correct nonce', function() { + assert.ok(account.nonce.eqn(2)) + }) + it('should have correct balance', function() { + assert.ok(account.balance.eqn(900)) + }) + it('should have correct stateRoot', function() { + assert.equal( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + ) + }) + it('should have correct codeHash', function() { + assert.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + ) + }) + }) + + describe('serialize', function() { + const raw = { + nonce: '0x01', + balance: '0x42', + stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + } + const account = Account.fromAccountData(raw) + const accountRlp = rlp.encode([raw.nonce, raw.balance, raw.stateRoot, raw.codeHash]) + it('should serialize correctly', function() { + assert.ok(account.serialize().equals(accountRlp)) + }) + }) + + describe('isContract', function() { + it('should return false for a non-contract account', function() { + const accountRlp = Buffer.from( + 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'hex', + ) + const account = Account.fromRlpSerializedAccount(accountRlp) + assert.equal(account.isContract(), false) + }) + + it('should return true for a contract account', function() { + const raw = { + nonce: '0x01', + balance: '0x0042', + stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + } + const account = Account.fromAccountData(raw) + assert.ok(account.isContract()) + }) + }) + + describe('isEmpty', function() { + it('should return true for an empty account', function() { + const account = new Account() + assert.ok(account.isEmpty()) + }) + + it('should return false for a non-empty account', function() { + const raw = { + nonce: '0x01', + balance: '0x0042', + stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + } + const account = Account.fromAccountData(raw) + assert.equal(account.isEmpty(), false) + }) + }) + + describe('validation', function() { + it('should only accept length 32 buffer for stateRoot', function() { + assert.throws(() => { + new Account(undefined, undefined, Buffer.from('hey'), undefined) + }) + }) + + it('should only accept length 32 buffer for codeHash', function() { + assert.throws(() => { + new Account(undefined, undefined, undefined, Buffer.from('hey')) + }) + }) + + it('should only accept an array in fromRlpSerializedAccount', function() { + const data = { balance: new BN(5) } + assert.throws(() => { + Account.fromRlpSerializedAccount(data as any) + }) + }) + }) +}) + describe('isValidPrivate', function() { const SECP256K1_N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) it('should fail on short input', function() { @@ -196,112 +379,16 @@ describe('privateToPublic', function() { it('should produce a public key given a private key', function() { const pubKey = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - 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 r: any = privateToPublic(privateKey).toString('hex') assert.equal(r.toString('hex'), pubKey) }) it("shouldn't produce a public key given an invalid private key", function() { - const privateKey1 = 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, - 42, - ]) - const privateKey2 = 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, - ]) + // prettier-ignore + const privateKey1 = 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, 42,]) + // prettier-ignore + const privateKey2 = 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,]) assert.throws(function() { privateToPublic(privateKey1) }) @@ -325,40 +412,8 @@ describe('privateToAddress', function() { it('should produce an address given a private key', function() { const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' // Our 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 r: any = privateToAddress(privateKey).toString('hex') assert.equal(r.toString('hex'), address) }) diff --git a/typedoc.js b/typedoc.js index 3bf7fa03..c8e893a9 100644 --- a/typedoc.js +++ b/typedoc.js @@ -5,8 +5,8 @@ module.exports = { plugin: 'typedoc-plugin-markdown', readme: 'none', gitRevision: 'master', - exclude: '**/*+(index|.spec|.e2e).ts', + exclude: ['src/index.ts', 'test/*.ts'], excludeNotExported: true, - // excludePrivate: true, - // excludeProtected: true, + excludePrivate: true, + excludeProtected: true, } From 3f858dbc5e58e7e86dbbe365b70b48244407b2d5 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Sun, 27 Sep 2020 20:06:32 -0700 Subject: [PATCH 02/46] export helpful types --- src/account.ts | 48 ++++++++++++++++-------------------------------- src/helpers.ts | 24 ++++++++++++++++++------ src/index.ts | 7 ++++++- src/types.ts | 11 +++++++++++ 4 files changed, 51 insertions(+), 39 deletions(-) create mode 100644 src/types.ts diff --git a/src/account.ts b/src/account.ts index ed2f0aed..cf5eca84 100644 --- a/src/account.ts +++ b/src/account.ts @@ -3,9 +3,10 @@ import * as BN from 'bn.js' import * as rlp from 'rlp' import { stripHexPrefix } from 'ethjs-util' import { KECCAK256_RLP, KECCAK256_NULL } from './constants' -import { zeros, bufferToHex, toBuffer, unpadBuffer } from './bytes' +import { zeros, bufferToHex, toBuffer } from './bytes' import { keccak, keccak256, keccakFromString, rlphash } from './hash' -import { assertIsHexString, assertIsBuffer } from './helpers' +import { assertIsHexString, assertIsBuffer, bnToRlp } from './helpers' +import { BNLike, BufferLike } from './types' const { privateKeyVerify, @@ -21,23 +22,6 @@ export interface AccountData { codeHash?: BufferLike } -type BNLike = BN | string | number - -type BufferLike = Buffer | TransformableToBuffer | PrefixedHexString | number - -type PrefixedHexString = string - -interface TransformableToBuffer { - toBuffer(): Buffer -} - -/* Helper for Account.serialize() */ -function bnToRlp(value: BN): Buffer { - // using `bn.toArrayLike(Buffer)` instead of `bn.toBuffer()` - // for compatibility with browserify and similar tools - return unpadBuffer(value.toArrayLike(Buffer)) -} - export class Account { nonce: BN balance: BN @@ -124,7 +108,7 @@ export class Account { /** * Checks if the address is a valid. Accepts checksummed addresses too. */ -export const isValidAddress = function(hexAddress: string): boolean { +export const isValidAddress = function (hexAddress: string): boolean { assertIsHexString(hexAddress) return /^0x[0-9a-fA-F]{40}$/.test(hexAddress) } @@ -139,7 +123,7 @@ export const isValidAddress = function(hexAddress: 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(hexAddress: string, eip1191ChainId?: number): string { +export const toChecksumAddress = function (hexAddress: string, eip1191ChainId?: number): string { assertIsHexString(hexAddress) const address = stripHexPrefix(hexAddress).toLowerCase() @@ -164,7 +148,7 @@ export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: n * * See toChecksumAddress' documentation for details about the eip1191ChainId parameter. */ -export const isValidChecksumAddress = function( +export const isValidChecksumAddress = function ( hexAddress: string, eip1191ChainId?: number, ): boolean { @@ -176,7 +160,7 @@ export const isValidChecksumAddress = function( * @param from The address which is creating this new address * @param nonce The nonce of the from account */ -export const generateAddress = function(from: Buffer, nonce: Buffer): Buffer { +export const generateAddress = function (from: Buffer, nonce: Buffer): Buffer { assertIsBuffer(from) assertIsBuffer(nonce) const nonceBN = new BN(nonce) @@ -197,7 +181,7 @@ export const generateAddress = function(from: Buffer, nonce: Buffer): Buffer { * @param salt A salt * @param initCode The init code of the contract being created */ -export const generateAddress2 = function(from: Buffer, salt: Buffer, initCode: Buffer): Buffer { +export const generateAddress2 = function (from: Buffer, salt: Buffer, initCode: Buffer): Buffer { assertIsBuffer(from) assertIsBuffer(salt) assertIsBuffer(initCode) @@ -215,7 +199,7 @@ export const generateAddress2 = function(from: Buffer, salt: Buffer, initCode: B /** * Checks if the private key satisfies the rules of the curve secp256k1. */ -export const isValidPrivate = function(privateKey: Buffer): boolean { +export const isValidPrivate = function (privateKey: Buffer): boolean { return privateKeyVerify(privateKey) } @@ -225,7 +209,7 @@ export const isValidPrivate = function(privateKey: Buffer): boolean { * @param publicKey The two points of an uncompressed key, unless sanitize is enabled * @param sanitize Accept public keys in other formats */ -export const isValidPublic = function(publicKey: Buffer, sanitize: boolean = false): boolean { +export const isValidPublic = function (publicKey: Buffer, sanitize: boolean = false): boolean { assertIsBuffer(publicKey) if (publicKey.length === 64) { // Convert to SEC1 for secp256k1 @@ -245,7 +229,7 @@ export const isValidPublic = function(publicKey: Buffer, sanitize: boolean = fal * @param pubKey The two points of an uncompressed key, unless sanitize is enabled * @param sanitize Accept public keys in other formats */ -export const pubToAddress = function(pubKey: Buffer, sanitize: boolean = false): Buffer { +export const pubToAddress = function (pubKey: Buffer, sanitize: boolean = false): Buffer { assertIsBuffer(pubKey) if (sanitize && pubKey.length !== 64) { pubKey = Buffer.from(publicKeyConvert(pubKey, false).slice(1)) @@ -260,7 +244,7 @@ export const publicToAddress = pubToAddress * Returns the ethereum address of a given private key. * @param privateKey A private key must be 256 bits wide */ -export const privateToAddress = function(privateKey: Buffer): Buffer { +export const privateToAddress = function (privateKey: Buffer): Buffer { return publicToAddress(privateToPublic(privateKey)) } @@ -268,7 +252,7 @@ export const privateToAddress = function(privateKey: Buffer): Buffer { * Returns the ethereum public key of a given private key. * @param privateKey A private key must be 256 bits wide */ -export const privateToPublic = function(privateKey: Buffer): Buffer { +export const privateToPublic = function (privateKey: Buffer): Buffer { assertIsBuffer(privateKey) // skip the type flag and use the X, Y points return Buffer.from(publicKeyCreate(privateKey, false)).slice(1) @@ -277,7 +261,7 @@ export const privateToPublic = function(privateKey: Buffer): Buffer { /** * Converts a public key to the Ethereum format. */ -export const importPublic = function(publicKey: Buffer): Buffer { +export const importPublic = function (publicKey: Buffer): Buffer { assertIsBuffer(publicKey) if (publicKey.length !== 64) { publicKey = Buffer.from(publicKeyConvert(publicKey, false).slice(1)) @@ -288,7 +272,7 @@ export const importPublic = function(publicKey: Buffer): Buffer { /** * Returns a zero address. */ -export const zeroAddress = function(): string { +export const zeroAddress = function (): string { const addressLength = 20 const addr = zeros(addressLength) return bufferToHex(addr) @@ -297,7 +281,7 @@ export const zeroAddress = function(): string { /** * Checks if a given address is a zero address. */ -export const isZeroAddress = function(hexAddress: string): boolean { +export const isZeroAddress = function (hexAddress: string): boolean { assertIsHexString(hexAddress) const zeroAddr = zeroAddress() return zeroAddr === hexAddress diff --git a/src/helpers.ts b/src/helpers.ts index 5f48ae39..2c027d75 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,11 +1,13 @@ -import * as ethjsUtil from 'ethjs-util' +import * as BN from 'bn.js' +import { isHexString } from 'ethjs-util' +import { unpadBuffer } from './bytes' /** * Throws if a string is not hex prefixed * @param {string} input string to check hex prefix of */ -export const assertIsHexString = function(input: string): void { - if (!ethjsUtil.isHexString(input)) { +export const assertIsHexString = function (input: string): void { + if (!isHexString(input)) { const msg = `This method only supports 0x-prefixed hex strings but input was: ${input}` throw new Error(msg) } @@ -15,7 +17,7 @@ export const assertIsHexString = function(input: string): void { * Throws if input is not a buffer * @param {Buffer} input value to check */ -export const assertIsBuffer = function(input: Buffer): void { +export const assertIsBuffer = function (input: Buffer): void { if (!Buffer.isBuffer(input)) { const msg = `This method only supports Buffer but input was: ${input}` throw new Error(msg) @@ -26,7 +28,7 @@ export const assertIsBuffer = function(input: Buffer): void { * Throws if input is not an array * @param {number[]} input value to check */ -export const assertIsArray = function(input: number[]): void { +export const assertIsArray = function (input: number[]): void { if (!Array.isArray(input)) { const msg = `This method only supports number arrays but input was: ${input}` throw new Error(msg) @@ -37,9 +39,19 @@ export const assertIsArray = function(input: number[]): void { * Throws if input is not a string * @param {string} input value to check */ -export const assertIsString = function(input: string): void { +export const assertIsString = function (input: string): void { if (typeof input !== 'string') { const msg = `This method only supports strings but input was: ${input}` throw new Error(msg) } } + +/** + * Convert value from BN to RLP (unpadded buffer) + * @param value value to convert + */ +export function bnToRlp(value: BN): Buffer { + // Using `bn.toArrayLike(Buffer)` instead of `bn.toBuffer()` + // for compatibility with browserify and similar tools + return unpadBuffer(value.toArrayLike(Buffer)) +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ff60d581..971a2876 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,7 +39,12 @@ export * from './object' */ export * from './externals' +/** + * Helpful TypeScript types + */ +export * from './types' + /** * Export ethjs-util methods */ -export * from 'ethjs-util' +export * from 'ethjs-util' \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..02618431 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,11 @@ +import * as BN from 'bn.js' + +export type BNLike = BN | string | number + +export type BufferLike = Buffer | TransformableToBuffer | PrefixedHexString | number + +export type PrefixedHexString = string + +export interface TransformableToBuffer { + toBuffer(): Buffer +} \ No newline at end of file From cfe8a9a13d94a4d2c9ac05d2e590261f7b9a3f5b Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Sun, 27 Sep 2020 20:14:55 -0700 Subject: [PATCH 03/46] check stateRoot in isEmpty --- src/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account.ts b/src/account.ts index cf5eca84..af9dc324 100644 --- a/src/account.ts +++ b/src/account.ts @@ -101,7 +101,7 @@ export class Account { * Returns a `Boolean` determining if the account is empty. */ isEmpty(): boolean { - return this.balance.isZero() && this.nonce.isZero() && this.codeHash.equals(KECCAK256_NULL) + return this.balance.isZero() && this.nonce.isZero() && this.stateRoot.equals(KECCAK256_RLP) && this.codeHash.equals(KECCAK256_NULL) } } From 6c89111e4b60b4fd3a1d98205199d6d274b61959 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Sun, 27 Sep 2020 20:15:42 -0700 Subject: [PATCH 04/46] use hex strings for private keys --- test/account.spec.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/test/account.spec.ts b/test/account.spec.ts index 241768ad..2548fbfa 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -379,16 +379,22 @@ describe('privateToPublic', function() { it('should produce a public key given a private key', function() { const pubKey = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - // 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 privateKey = Buffer.from( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', + 'hex', + ) const r: any = privateToPublic(privateKey).toString('hex') assert.equal(r.toString('hex'), pubKey) }) it("shouldn't produce a public key given an invalid private key", function() { - // prettier-ignore - const privateKey1 = 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, 42,]) - // prettier-ignore - const privateKey2 = 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,]) + const privateKey1 = Buffer.from( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f2a', + 'hex', + ) + const privateKey2 = Buffer.from( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c', + 'hex', + ) assert.throws(function() { privateToPublic(privateKey1) }) @@ -412,8 +418,10 @@ describe('privateToAddress', function() { it('should produce an address given a private key', function() { const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' // Our private key - // 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 privateKey = Buffer.from( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', + 'hex', + ) const r: any = privateToAddress(privateKey).toString('hex') assert.equal(r.toString('hex'), address) }) From 18e90bff7b301e030567a311a2796610d7b5d327 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Sun, 27 Sep 2020 20:15:47 -0700 Subject: [PATCH 05/46] lint --- src/account.ts | 33 +++++++++++++++++++-------------- src/helpers.ts | 10 +++++----- src/index.ts | 2 +- src/types.ts | 4 ++-- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/account.ts b/src/account.ts index af9dc324..77fae23f 100644 --- a/src/account.ts +++ b/src/account.ts @@ -101,14 +101,19 @@ export class Account { * Returns a `Boolean` determining if the account is empty. */ isEmpty(): boolean { - return this.balance.isZero() && this.nonce.isZero() && this.stateRoot.equals(KECCAK256_RLP) && this.codeHash.equals(KECCAK256_NULL) + return ( + this.balance.isZero() && + this.nonce.isZero() && + this.stateRoot.equals(KECCAK256_RLP) && + this.codeHash.equals(KECCAK256_NULL) + ) } } /** * Checks if the address is a valid. Accepts checksummed addresses too. */ -export const isValidAddress = function (hexAddress: string): boolean { +export const isValidAddress = function(hexAddress: string): boolean { assertIsHexString(hexAddress) return /^0x[0-9a-fA-F]{40}$/.test(hexAddress) } @@ -123,7 +128,7 @@ export const isValidAddress = function (hexAddress: 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 (hexAddress: string, eip1191ChainId?: number): string { +export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: number): string { assertIsHexString(hexAddress) const address = stripHexPrefix(hexAddress).toLowerCase() @@ -148,7 +153,7 @@ export const toChecksumAddress = function (hexAddress: string, eip1191ChainId?: * * See toChecksumAddress' documentation for details about the eip1191ChainId parameter. */ -export const isValidChecksumAddress = function ( +export const isValidChecksumAddress = function( hexAddress: string, eip1191ChainId?: number, ): boolean { @@ -160,7 +165,7 @@ export const isValidChecksumAddress = function ( * @param from The address which is creating this new address * @param nonce The nonce of the from account */ -export const generateAddress = function (from: Buffer, nonce: Buffer): Buffer { +export const generateAddress = function(from: Buffer, nonce: Buffer): Buffer { assertIsBuffer(from) assertIsBuffer(nonce) const nonceBN = new BN(nonce) @@ -181,7 +186,7 @@ export const generateAddress = function (from: Buffer, nonce: Buffer): Buffer { * @param salt A salt * @param initCode The init code of the contract being created */ -export const generateAddress2 = function (from: Buffer, salt: Buffer, initCode: Buffer): Buffer { +export const generateAddress2 = function(from: Buffer, salt: Buffer, initCode: Buffer): Buffer { assertIsBuffer(from) assertIsBuffer(salt) assertIsBuffer(initCode) @@ -199,7 +204,7 @@ export const generateAddress2 = function (from: Buffer, salt: Buffer, initCode: /** * Checks if the private key satisfies the rules of the curve secp256k1. */ -export const isValidPrivate = function (privateKey: Buffer): boolean { +export const isValidPrivate = function(privateKey: Buffer): boolean { return privateKeyVerify(privateKey) } @@ -209,7 +214,7 @@ export const isValidPrivate = function (privateKey: Buffer): boolean { * @param publicKey The two points of an uncompressed key, unless sanitize is enabled * @param sanitize Accept public keys in other formats */ -export const isValidPublic = function (publicKey: Buffer, sanitize: boolean = false): boolean { +export const isValidPublic = function(publicKey: Buffer, sanitize: boolean = false): boolean { assertIsBuffer(publicKey) if (publicKey.length === 64) { // Convert to SEC1 for secp256k1 @@ -229,7 +234,7 @@ export const isValidPublic = function (publicKey: Buffer, sanitize: boolean = fa * @param pubKey The two points of an uncompressed key, unless sanitize is enabled * @param sanitize Accept public keys in other formats */ -export const pubToAddress = function (pubKey: Buffer, sanitize: boolean = false): Buffer { +export const pubToAddress = function(pubKey: Buffer, sanitize: boolean = false): Buffer { assertIsBuffer(pubKey) if (sanitize && pubKey.length !== 64) { pubKey = Buffer.from(publicKeyConvert(pubKey, false).slice(1)) @@ -244,7 +249,7 @@ export const publicToAddress = pubToAddress * Returns the ethereum address of a given private key. * @param privateKey A private key must be 256 bits wide */ -export const privateToAddress = function (privateKey: Buffer): Buffer { +export const privateToAddress = function(privateKey: Buffer): Buffer { return publicToAddress(privateToPublic(privateKey)) } @@ -252,7 +257,7 @@ export const privateToAddress = function (privateKey: Buffer): Buffer { * Returns the ethereum public key of a given private key. * @param privateKey A private key must be 256 bits wide */ -export const privateToPublic = function (privateKey: Buffer): Buffer { +export const privateToPublic = function(privateKey: Buffer): Buffer { assertIsBuffer(privateKey) // skip the type flag and use the X, Y points return Buffer.from(publicKeyCreate(privateKey, false)).slice(1) @@ -261,7 +266,7 @@ export const privateToPublic = function (privateKey: Buffer): Buffer { /** * Converts a public key to the Ethereum format. */ -export const importPublic = function (publicKey: Buffer): Buffer { +export const importPublic = function(publicKey: Buffer): Buffer { assertIsBuffer(publicKey) if (publicKey.length !== 64) { publicKey = Buffer.from(publicKeyConvert(publicKey, false).slice(1)) @@ -272,7 +277,7 @@ export const importPublic = function (publicKey: Buffer): Buffer { /** * Returns a zero address. */ -export const zeroAddress = function (): string { +export const zeroAddress = function(): string { const addressLength = 20 const addr = zeros(addressLength) return bufferToHex(addr) @@ -281,7 +286,7 @@ export const zeroAddress = function (): string { /** * Checks if a given address is a zero address. */ -export const isZeroAddress = function (hexAddress: string): boolean { +export const isZeroAddress = function(hexAddress: string): boolean { assertIsHexString(hexAddress) const zeroAddr = zeroAddress() return zeroAddr === hexAddress diff --git a/src/helpers.ts b/src/helpers.ts index 2c027d75..bcceaa22 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -6,7 +6,7 @@ import { unpadBuffer } from './bytes' * Throws if a string is not hex prefixed * @param {string} input string to check hex prefix of */ -export const assertIsHexString = function (input: string): void { +export const assertIsHexString = function(input: string): void { if (!isHexString(input)) { const msg = `This method only supports 0x-prefixed hex strings but input was: ${input}` throw new Error(msg) @@ -17,7 +17,7 @@ export const assertIsHexString = function (input: string): void { * Throws if input is not a buffer * @param {Buffer} input value to check */ -export const assertIsBuffer = function (input: Buffer): void { +export const assertIsBuffer = function(input: Buffer): void { if (!Buffer.isBuffer(input)) { const msg = `This method only supports Buffer but input was: ${input}` throw new Error(msg) @@ -28,7 +28,7 @@ export const assertIsBuffer = function (input: Buffer): void { * Throws if input is not an array * @param {number[]} input value to check */ -export const assertIsArray = function (input: number[]): void { +export const assertIsArray = function(input: number[]): void { if (!Array.isArray(input)) { const msg = `This method only supports number arrays but input was: ${input}` throw new Error(msg) @@ -39,7 +39,7 @@ export const assertIsArray = function (input: number[]): void { * Throws if input is not a string * @param {string} input value to check */ -export const assertIsString = function (input: string): void { +export const assertIsString = function(input: string): void { if (typeof input !== 'string') { const msg = `This method only supports strings but input was: ${input}` throw new Error(msg) @@ -54,4 +54,4 @@ export function bnToRlp(value: BN): Buffer { // Using `bn.toArrayLike(Buffer)` instead of `bn.toBuffer()` // for compatibility with browserify and similar tools return unpadBuffer(value.toArrayLike(Buffer)) -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 971a2876..848d0c77 100644 --- a/src/index.ts +++ b/src/index.ts @@ -47,4 +47,4 @@ export * from './types' /** * Export ethjs-util methods */ -export * from 'ethjs-util' \ No newline at end of file +export * from 'ethjs-util' diff --git a/src/types.ts b/src/types.ts index 02618431..894c9133 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,5 +7,5 @@ export type BufferLike = Buffer | TransformableToBuffer | PrefixedHexString | nu export type PrefixedHexString = string export interface TransformableToBuffer { - toBuffer(): Buffer -} \ No newline at end of file + toBuffer(): Buffer +} From 0f5418ef1d86d0d3c54cfcb01df76352205b2888 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Sun, 27 Sep 2020 20:26:06 -0700 Subject: [PATCH 06/46] rebuild docs, add types docs --- CHANGELOG.md | 8 ++++ README.md | 2 + docs/README.md | 1 + docs/classes/_account_.account.md | 22 +++++------ docs/interfaces/_account_.accountdata.md | 16 ++++---- .../_types_.transformabletobuffer.md | 23 +++++++++++ docs/modules/_account_.md | 28 ++++++------- docs/modules/_helpers_.md | 27 +++++++++++-- docs/modules/_types_.md | 39 +++++++++++++++++++ 9 files changed, 129 insertions(+), 37 deletions(-) create mode 100644 docs/interfaces/_types_.transformabletobuffer.md create mode 100644 docs/modules/_types_.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d4474e09..14a8b6f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [7.0.6] - [UNRELEASED] +### New `Account` class + This release adds a new `Account` class intended as a modern replacement for `ethereumjs-account`. It has a shape of `Account(nonce?: BN, balance?: BN, stateRoot?: Buffer, codeHash?: Buffer)`. **Instantiation** @@ -31,6 +33,12 @@ const account = new Account( For more info see the documentation or examples of usage in `test/account.spec.ts`. +### New export: TypeScript types + +A new file with helpful TypeScript types has been added to the exports of this project. + +In this release it contains `BNLike`, `BufferLike`, and `TransformableToBuffer`. + ## [7.0.5] - 2020-09-09 This release adds a new module `address` - see [README](https://github.com/ethereumjs/ethereumjs-util#modules) - diff --git a/README.md b/README.md index 654c9561..1d22e35c 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ assert.equal(new BN('dead', 16).add(new BN('101010', 2)), 57047) - Helper function for creating a binary object (`DEPRECATED`) - [signature](docs/modules/_signature_.md) - Signing, signature validation, conversion, recovery +- [types](docs/modules/_types_.md) + - Helpful TypeScript types - [externals](docs/modules/_externals_.md) - Helper methods from `ethjs-util` - Re-exports of `BN`, `rlp` diff --git a/docs/README.md b/docs/README.md index 7dff9c06..bdfde4cb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,3 +16,4 @@ * ["helpers"](modules/_helpers_.md) * ["object"](modules/_object_.md) * ["signature"](modules/_signature_.md) +* ["types"](modules/_types_.md) diff --git a/docs/classes/_account_.account.md b/docs/classes/_account_.account.md index 4f3d33d8..42391678 100644 --- a/docs/classes/_account_.account.md +++ b/docs/classes/_account_.account.md @@ -34,7 +34,7 @@ \+ **new Account**(`nonce`: BN‹›, `balance`: BN‹›, `stateRoot`: Buffer‹›, `codeHash`: Buffer‹›): *[Account](_account_.account.md)* -*Defined in [account.ts:77](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L77)* +*Defined in [account.ts:61](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L61)* This constructor takes the values, validates and assigns them. Use the static factory methods to assist in creating an Account from varying data types. @@ -56,7 +56,7 @@ Name | Type | Default | • **balance**: *BN* -*Defined in [account.ts:43](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L43)* +*Defined in [account.ts:27](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L27)* ___ @@ -64,7 +64,7 @@ ___ • **codeHash**: *Buffer* -*Defined in [account.ts:45](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L45)* +*Defined in [account.ts:29](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L29)* ___ @@ -72,7 +72,7 @@ ___ • **nonce**: *BN* -*Defined in [account.ts:42](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L42)* +*Defined in [account.ts:26](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L26)* ___ @@ -80,7 +80,7 @@ ___ • **stateRoot**: *Buffer* -*Defined in [account.ts:44](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L44)* +*Defined in [account.ts:28](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L28)* ## Methods @@ -88,7 +88,7 @@ ___ ▸ **isContract**(): *boolean* -*Defined in [account.ts:112](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L112)* +*Defined in [account.ts:96](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L96)* Returns a `Boolean` deteremining if the account is a contract. @@ -100,7 +100,7 @@ ___ ▸ **isEmpty**(): *boolean* -*Defined in [account.ts:119](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L119)* +*Defined in [account.ts:103](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L103)* Returns a `Boolean` determining if the account is empty. @@ -112,7 +112,7 @@ ___ ▸ **serialize**(): *Buffer* -*Defined in [account.ts:105](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L105)* +*Defined in [account.ts:89](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L89)* Returns the RLP serialization of the account as a `Buffer`. @@ -124,7 +124,7 @@ ___ ▸ **fromAccountData**(`accountData`: [AccountData](../interfaces/_account_.accountdata.md)): *[Account](_account_.account.md)‹›* -*Defined in [account.ts:47](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L47)* +*Defined in [account.ts:31](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L31)* **Parameters:** @@ -140,7 +140,7 @@ ___ ▸ **fromRlpSerializedAccount**(`serialized`: Buffer): *[Account](_account_.account.md)‹›* -*Defined in [account.ts:58](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L58)* +*Defined in [account.ts:42](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L42)* **Parameters:** @@ -156,7 +156,7 @@ ___ ▸ **fromValuesArray**(`values`: Buffer[]): *[Account](_account_.account.md)‹›* -*Defined in [account.ts:68](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L68)* +*Defined in [account.ts:52](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L52)* **Parameters:** diff --git a/docs/interfaces/_account_.accountdata.md b/docs/interfaces/_account_.accountdata.md index 698b49df..126266a0 100644 --- a/docs/interfaces/_account_.accountdata.md +++ b/docs/interfaces/_account_.accountdata.md @@ -19,30 +19,30 @@ ### `Optional` balance -• **balance**? : *BNLike* +• **balance**? : *[BNLike](../modules/_types_.md#bnlike)* -*Defined in [account.ts:19](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L19)* +*Defined in [account.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L20)* ___ ### `Optional` codeHash -• **codeHash**? : *BufferLike* +• **codeHash**? : *[BufferLike](../modules/_types_.md#bufferlike)* -*Defined in [account.ts:21](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L21)* +*Defined in [account.ts:22](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L22)* ___ ### `Optional` nonce -• **nonce**? : *BNLike* +• **nonce**? : *[BNLike](../modules/_types_.md#bnlike)* -*Defined in [account.ts:18](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L18)* +*Defined in [account.ts:19](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L19)* ___ ### `Optional` stateRoot -• **stateRoot**? : *BufferLike* +• **stateRoot**? : *[BufferLike](../modules/_types_.md#bufferlike)* -*Defined in [account.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L20)* +*Defined in [account.ts:21](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L21)* diff --git a/docs/interfaces/_types_.transformabletobuffer.md b/docs/interfaces/_types_.transformabletobuffer.md new file mode 100644 index 00000000..7ab3daaf --- /dev/null +++ b/docs/interfaces/_types_.transformabletobuffer.md @@ -0,0 +1,23 @@ +[ethereumjs-util](../README.md) › ["types"](../modules/_types_.md) › [TransformableToBuffer](_types_.transformabletobuffer.md) + +# Interface: TransformableToBuffer + +## Hierarchy + +* **TransformableToBuffer** + +## Index + +### Methods + +* [toBuffer](_types_.transformabletobuffer.md#tobuffer) + +## Methods + +### toBuffer + +▸ **toBuffer**(): *Buffer* + +*Defined in [types.ts:10](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L10)* + +**Returns:** *Buffer* diff --git a/docs/modules/_account_.md b/docs/modules/_account_.md index 08125b1d..0abc65de 100644 --- a/docs/modules/_account_.md +++ b/docs/modules/_account_.md @@ -38,7 +38,7 @@ • **publicToAddress**: *pubToAddress* = pubToAddress -*Defined in [account.ts:257](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L257)* +*Defined in [account.ts:246](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L246)* ## Functions @@ -46,7 +46,7 @@ ▸ **generateAddress**(`from`: Buffer, `nonce`: Buffer): *Buffer* -*Defined in [account.ts:179](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L179)* +*Defined in [account.ts:168](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L168)* Generates an address of a newly created contract. @@ -65,7 +65,7 @@ ___ ▸ **generateAddress2**(`from`: Buffer, `salt`: Buffer, `initCode`: Buffer): *Buffer* -*Defined in [account.ts:200](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L200)* +*Defined in [account.ts:189](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L189)* Generates an address for a contract created using CREATE2. @@ -85,7 +85,7 @@ ___ ▸ **importPublic**(`publicKey`: Buffer): *Buffer* -*Defined in [account.ts:280](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L280)* +*Defined in [account.ts:269](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L269)* Converts a public key to the Ethereum format. @@ -103,7 +103,7 @@ ___ ▸ **isValidAddress**(`hexAddress`: string): *boolean* -*Defined in [account.ts:127](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L127)* +*Defined in [account.ts:116](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L116)* Checks if the address is a valid. Accepts checksummed addresses too. @@ -121,7 +121,7 @@ ___ ▸ **isValidChecksumAddress**(`hexAddress`: string, `eip1191ChainId?`: undefined | number): *boolean* -*Defined in [account.ts:167](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L167)* +*Defined in [account.ts:156](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L156)* Checks if the address is a valid checksummed address. @@ -142,7 +142,7 @@ ___ ▸ **isValidPrivate**(`privateKey`: Buffer): *boolean* -*Defined in [account.ts:218](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L218)* +*Defined in [account.ts:207](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L207)* Checks if the private key satisfies the rules of the curve secp256k1. @@ -160,7 +160,7 @@ ___ ▸ **isValidPublic**(`publicKey`: Buffer, `sanitize`: boolean): *boolean* -*Defined in [account.ts:228](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L228)* +*Defined in [account.ts:217](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L217)* Checks if the public key satisfies the rules of the curve secp256k1 and the requirements of Ethereum. @@ -180,7 +180,7 @@ ___ ▸ **isZeroAddress**(`hexAddress`: string): *boolean* -*Defined in [account.ts:300](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L300)* +*Defined in [account.ts:289](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L289)* Checks if a given address is a zero address. @@ -198,7 +198,7 @@ ___ ▸ **privateToAddress**(`privateKey`: Buffer): *Buffer* -*Defined in [account.ts:263](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L263)* +*Defined in [account.ts:252](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L252)* Returns the ethereum address of a given private key. @@ -216,7 +216,7 @@ ___ ▸ **privateToPublic**(`privateKey`: Buffer): *Buffer* -*Defined in [account.ts:271](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L271)* +*Defined in [account.ts:260](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L260)* Returns the ethereum public key of a given private key. @@ -234,7 +234,7 @@ ___ ▸ **pubToAddress**(`pubKey`: Buffer, `sanitize`: boolean): *Buffer* -*Defined in [account.ts:248](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L248)* +*Defined in [account.ts:237](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L237)* Returns the ethereum address of a given public key. Accepts "Ethereum public keys" and SEC1 encoded keys. @@ -254,7 +254,7 @@ ___ ▸ **toChecksumAddress**(`hexAddress`: string, `eip1191ChainId?`: undefined | number): *string* -*Defined in [account.ts:142](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L142)* +*Defined in [account.ts:131](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L131)* Returns a checksummed address. @@ -280,7 +280,7 @@ ___ ▸ **zeroAddress**(): *string* -*Defined in [account.ts:291](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L291)* +*Defined in [account.ts:280](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L280)* Returns a zero address. diff --git a/docs/modules/_helpers_.md b/docs/modules/_helpers_.md index 70afc005..5f6b892b 100644 --- a/docs/modules/_helpers_.md +++ b/docs/modules/_helpers_.md @@ -10,6 +10,7 @@ * [assertIsBuffer](_helpers_.md#const-assertisbuffer) * [assertIsHexString](_helpers_.md#const-assertishexstring) * [assertIsString](_helpers_.md#const-assertisstring) +* [bnToRlp](_helpers_.md#bntorlp) ## Functions @@ -17,7 +18,7 @@ ▸ **assertIsArray**(`input`: number[]): *void* -*Defined in [helpers.ts:29](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L29)* +*Defined in [helpers.ts:31](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L31)* Throws if input is not an array @@ -35,7 +36,7 @@ ___ ▸ **assertIsBuffer**(`input`: Buffer): *void* -*Defined in [helpers.ts:18](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L18)* +*Defined in [helpers.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L20)* Throws if input is not a buffer @@ -53,7 +54,7 @@ ___ ▸ **assertIsHexString**(`input`: string): *void* -*Defined in [helpers.ts:7](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L7)* +*Defined in [helpers.ts:9](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L9)* Throws if a string is not hex prefixed @@ -71,7 +72,7 @@ ___ ▸ **assertIsString**(`input`: string): *void* -*Defined in [helpers.ts:40](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L40)* +*Defined in [helpers.ts:42](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L42)* Throws if input is not a string @@ -82,3 +83,21 @@ Name | Type | Description | `input` | string | value to check | **Returns:** *void* + +___ + +### bnToRlp + +▸ **bnToRlp**(`value`: BN): *Buffer* + +*Defined in [helpers.ts:53](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L53)* + +Convert value from BN to RLP (unpadded buffer) + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`value` | BN | value to convert | + +**Returns:** *Buffer* diff --git a/docs/modules/_types_.md b/docs/modules/_types_.md new file mode 100644 index 00000000..bf0509b6 --- /dev/null +++ b/docs/modules/_types_.md @@ -0,0 +1,39 @@ +[ethereumjs-util](../README.md) › ["types"](_types_.md) + +# Module: "types" + +## Index + +### Interfaces + +* [TransformableToBuffer](../interfaces/_types_.transformabletobuffer.md) + +### Type aliases + +* [BNLike](_types_.md#bnlike) +* [BufferLike](_types_.md#bufferlike) +* [PrefixedHexString](_types_.md#prefixedhexstring) + +## Type aliases + +### BNLike + +Ƭ **BNLike**: *BN | string | number* + +*Defined in [types.ts:3](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L3)* + +___ + +### BufferLike + +Ƭ **BufferLike**: *Buffer | [TransformableToBuffer](../interfaces/_types_.transformabletobuffer.md) | [PrefixedHexString](_types_.md#prefixedhexstring) | number* + +*Defined in [types.ts:5](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L5)* + +___ + +### PrefixedHexString + +Ƭ **PrefixedHexString**: *string* + +*Defined in [types.ts:7](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L7)* From b2fce00e9b330417a3719f7a26329acede43746f Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Mon, 28 Sep 2020 12:46:29 -0700 Subject: [PATCH 07/46] fix typo --- src/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account.ts b/src/account.ts index 77fae23f..b057b93a 100644 --- a/src/account.ts +++ b/src/account.ts @@ -91,7 +91,7 @@ export class Account { } /** - * Returns a `Boolean` deteremining if the account is a contract. + * Returns a `Boolean` determining if the account is a contract. */ isContract(): boolean { return !this.codeHash.equals(KECCAK256_NULL) From 36a070008a4048461d315db487cfbe9f83e16b24 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Mon, 28 Sep 2020 12:47:22 -0700 Subject: [PATCH 08/46] make codeHash more different-looking than KECCAK256_NULL --- test/account.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/account.spec.ts b/test/account.spec.ts index 2548fbfa..2691502f 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -170,7 +170,7 @@ describe('Account', function() { nonce: '0x01', balance: '0x0042', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + codeHash: '0xd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b', } const account = Account.fromAccountData(raw) assert.equal(account.isEmpty(), false) From 69a5ef403663599b9ddadb83fb4853118adaecc1 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 29 Sep 2020 12:58:37 -0700 Subject: [PATCH 09/46] move `bnToRlp` to types file so it can be exported in the main package for use in monorepo --- src/account.ts | 4 ++-- src/helpers.ts | 12 ------------ src/types.ts | 11 +++++++++++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/account.ts b/src/account.ts index b057b93a..1cc691d7 100644 --- a/src/account.ts +++ b/src/account.ts @@ -5,8 +5,8 @@ import { stripHexPrefix } from 'ethjs-util' import { KECCAK256_RLP, KECCAK256_NULL } from './constants' import { zeros, bufferToHex, toBuffer } from './bytes' import { keccak, keccak256, keccakFromString, rlphash } from './hash' -import { assertIsHexString, assertIsBuffer, bnToRlp } from './helpers' -import { BNLike, BufferLike } from './types' +import { assertIsHexString, assertIsBuffer } from './helpers' +import { BNLike, BufferLike, bnToRlp } from './types' const { privateKeyVerify, diff --git a/src/helpers.ts b/src/helpers.ts index bcceaa22..3fd4eacd 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,6 +1,4 @@ -import * as BN from 'bn.js' import { isHexString } from 'ethjs-util' -import { unpadBuffer } from './bytes' /** * Throws if a string is not hex prefixed @@ -45,13 +43,3 @@ export const assertIsString = function(input: string): void { throw new Error(msg) } } - -/** - * Convert value from BN to RLP (unpadded buffer) - * @param value value to convert - */ -export function bnToRlp(value: BN): Buffer { - // Using `bn.toArrayLike(Buffer)` instead of `bn.toBuffer()` - // for compatibility with browserify and similar tools - return unpadBuffer(value.toArrayLike(Buffer)) -} diff --git a/src/types.ts b/src/types.ts index 894c9133..6cb819b4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,5 @@ import * as BN from 'bn.js' +import { unpadBuffer } from './bytes' export type BNLike = BN | string | number @@ -9,3 +10,13 @@ export type PrefixedHexString = string export interface TransformableToBuffer { toBuffer(): Buffer } + +/** + * Convert value from BN to RLP (unpadded buffer) + * @param value value to convert + */ +export function bnToRlp(value: BN): Buffer { + // Using `bn.toArrayLike(Buffer)` instead of `bn.toBuffer()` + // for compatibility with browserify and similar tools + return unpadBuffer(value.toArrayLike(Buffer)) +} From b0aacc0eff2ded594a12b614508a294ad9c63f4b Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Wed, 30 Sep 2020 09:37:52 -0700 Subject: [PATCH 10/46] add validation for nonce and balance --- src/account.ts | 26 ++++++++++++++++++-------- test/account.spec.ts | 12 ++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/account.ts b/src/account.ts index 1cc691d7..e7cbb349 100644 --- a/src/account.ts +++ b/src/account.ts @@ -61,7 +61,7 @@ export class Account { } /** - * This constructor takes the values, validates and assigns them. + * This constructor assigns and validates the values. * Use the static factory methods to assist in creating an Account from varying data types. */ constructor( @@ -70,17 +70,27 @@ export class Account { stateRoot = KECCAK256_RLP, codeHash = KECCAK256_NULL, ) { - if (stateRoot.length !== 32) { - throw new Error('stateRoot must have a length of 32') - } - if (codeHash.length !== 32) { - throw new Error('codeHash must have a length of 32') - } - this.nonce = nonce this.balance = balance this.stateRoot = stateRoot this.codeHash = codeHash + + this._validate() + } + + private _validate() { + if (this.nonce.lt(new BN(0))) { + throw new Error('nonce must be greater than zero') + } + if (this.balance.lt(new BN(0))) { + throw new Error('balance must be greater than zero') + } + if (this.stateRoot.length !== 32) { + throw new Error('stateRoot must have a length of 32') + } + if (this.codeHash.length !== 32) { + throw new Error('codeHash must have a length of 32') + } } /** diff --git a/test/account.spec.ts b/test/account.spec.ts index 2691502f..c999b357 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -196,6 +196,18 @@ describe('Account', function() { Account.fromRlpSerializedAccount(data as any) }) }) + + it('should not accept nonce less than 0', function() { + assert.throws(() => { + new Account(new BN(-5)) + }) + }) + + it('should not accept balance less than 0', function() { + assert.throws(() => { + new Account(undefined, new BN(-5)) + }) + }) }) }) From e48478d3449fed6142cd6f611dffab5d4ed46ee3 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Wed, 30 Sep 2020 10:22:44 -0700 Subject: [PATCH 11/46] more docs, fixups --- src/account.ts | 8 +++++--- src/types.ts | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/account.ts b/src/account.ts index e7cbb349..e3d2c6df 100644 --- a/src/account.ts +++ b/src/account.ts @@ -109,6 +109,8 @@ export class Account { /** * Returns a `Boolean` determining if the account is empty. + * For more details about account emptiness see [EIP-161](https://eips.ethereum.org/EIPS/eip-161). + * Note: The stateRoot is also checked to be empty since in Frontier it was possible to create a contract with no code where nonce remained 0 but some values were written to storage in the constructor (thus stateRoot is not KECCAK256_RLP). */ isEmpty(): boolean { return ( @@ -133,7 +135,7 @@ export const isValidAddress = function(hexAddress: string): boolean { * * 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. - * For more details, consult EIP-1191. + * For more details see [EIP-1191](https://eips.ethereum.org/EIPS/eip-1191). * * 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. @@ -285,7 +287,7 @@ export const importPublic = function(publicKey: Buffer): Buffer { } /** - * Returns a zero address. + * Returns the zero address. */ export const zeroAddress = function(): string { const addressLength = 20 @@ -294,7 +296,7 @@ export const zeroAddress = function(): string { } /** - * Checks if a given address is a zero address. + * Checks if a given address is the zero address. */ export const isZeroAddress = function(hexAddress: string): boolean { assertIsHexString(hexAddress) diff --git a/src/types.ts b/src/types.ts index 6cb819b4..64ec1a35 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,12 +1,31 @@ import * as BN from 'bn.js' import { unpadBuffer } from './bytes' +/* + * A type that represents a BNLike input that can be converted to a BN. + */ export type BNLike = BN | string | number -export type BufferLike = Buffer | TransformableToBuffer | PrefixedHexString | number +/* + * A type that represents a BufferLike input that can be converted to a Buffer. + */ +export type BufferLike = + | Buffer + | Uint8Array + | number[] + | number + | BN + | TransformableToBuffer + | PrefixedHexString +/* + * A type that represents a `0x`-prefixed hex string. + */ export type PrefixedHexString = string +/* + * A type that represents an object that has a `toBuffer()` method. + */ export interface TransformableToBuffer { toBuffer(): Buffer } From 812525a57d6125653ca994854c696394ced5e938 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 29 Sep 2020 12:35:05 -0700 Subject: [PATCH 12/46] add toBuffer() to Address --- src/address.ts | 7 +++++++ test/address.spec.ts | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/src/address.ts b/src/address.ts index d052ce0d..2e2194c6 100644 --- a/src/address.ts +++ b/src/address.ts @@ -88,4 +88,11 @@ export class Address { toString(): string { return '0x' + this.buf.toString('hex') } + + /** + * Returns Buffer representation of address. + */ + toBuffer(): Buffer { + return Buffer.from(this.buf) + } } diff --git a/test/address.spec.ts b/test/address.spec.ts index ede4bbda..8814bbd2 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -76,4 +76,12 @@ describe('Address', () => { assert.equal(addr.toString(), result) } }) + + it('should provide a buffer that does not mutate the original address', () => { + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' + const address = Address.fromString(str) + let addressBuf = address.toBuffer() + addressBuf = Buffer.from('test') + assert.equal(address.toString(), str) + }) }) From f4afe3aef43333ff99746be1e9ba651f2540f78f Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 29 Sep 2020 12:38:01 -0700 Subject: [PATCH 13/46] make toBuffer convert TransformableToBuffer --- src/bytes.ts | 2 ++ test/bytes.spec.ts | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/bytes.ts b/src/bytes.ts index 6e938f06..eb3348e3 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -129,6 +129,8 @@ export const toBuffer = function(v: any): Buffer { } else if (v.toArray) { // converts a BN to a Buffer v = Buffer.from(v.toArray()) + } else if (v.toBuffer) { + v = Buffer.from(v.toBuffer()) } else { throw new Error('invalid type') } diff --git a/test/bytes.spec.ts b/test/bytes.spec.ts index 78617b85..7057fccd 100644 --- a/test/bytes.spec.ts +++ b/test/bytes.spec.ts @@ -1,6 +1,7 @@ import * as assert from 'assert' import * as BN from 'bn.js' import { + Address, zeros, zeroAddress, isZeroAddress, @@ -233,6 +234,13 @@ describe('toBuffer', function() { assert.throws(() => toBuffer('')) assert.throws(() => toBuffer('0xR'), '0xR') }) + + it('should convert a TransformableToBuffer like the Address class (i.e. provides a toBuffer method)', function() { + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' + const address = Address.fromString(str) + const addressBuf = toBuffer(address) + assert.ok(addressBuf.equals(address.toBuffer())) + }) }) describe('baToJSON', function() { From f89ed4ad8b92b4e43b2cc3b7efa34443b8ec4415 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 29 Sep 2020 12:47:04 -0700 Subject: [PATCH 14/46] use explicit import for ethjsUtil --- src/bytes.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bytes.ts b/src/bytes.ts index eb3348e3..6199b675 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -1,5 +1,5 @@ -import * as ethjsUtil from 'ethjs-util' import * as BN from 'bn.js' +import { intToBuffer, stripHexPrefix, padToEven, isHexString, isHexPrefixed } from 'ethjs-util' import { assertIsBuffer, assertIsArray, assertIsHexString } from './helpers' /** @@ -86,7 +86,7 @@ export const unpadArray = function(a: number[]): number[] { */ export const unpadHexString = function(a: string): string { assertIsHexString(a) - a = ethjsUtil.stripHexPrefix(a) + a = stripHexPrefix(a) return stripZeros(a) as string } @@ -113,15 +113,15 @@ export const toBuffer = function(v: any): Buffer { if (Array.isArray(v) || v instanceof Uint8Array) { v = Buffer.from(v as Uint8Array) } else if (typeof v === 'string') { - if (ethjsUtil.isHexString(v)) { - v = Buffer.from(ethjsUtil.padToEven(ethjsUtil.stripHexPrefix(v)), 'hex') + if (isHexString(v)) { + v = Buffer.from(padToEven(stripHexPrefix(v)), 'hex') } else { throw new Error( `Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}`, ) } } else if (typeof v === 'number') { - v = ethjsUtil.intToBuffer(v) + v = intToBuffer(v) } else if (v === null || v === undefined) { v = Buffer.allocUnsafe(0) } else if (BN.isBN(v)) { @@ -180,7 +180,7 @@ export const addHexPrefix = function(str: string): string { return str } - return ethjsUtil.isHexPrefixed(str) ? str : '0x' + str + return isHexPrefixed(str) ? str : '0x' + str } /** From 06cdec2a72e79e5d846de1541824dd556ae21faf Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 29 Sep 2020 12:49:09 -0700 Subject: [PATCH 15/46] update changelog --- CHANGELOG.md | 7 +++++++ src/bytes.ts | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a8b6f0..6e7638b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,13 @@ For more info see the documentation or examples of usage in `test/account.spec.t A new file with helpful TypeScript types has been added to the exports of this project. In this release it contains `BNLike`, `BufferLike`, and `TransformableToBuffer`. +### Address.toBuffer() + +The Address class has as a new method `address.toBuffer()` that will give you a copy of the underlying `address.buf`. + +### `toBuffer()` now converts TransformableToBuffer + +The `toBuffer()` exported function now additionally converts any object with a `toBuffer()` method. ## [7.0.5] - 2020-09-09 diff --git a/src/bytes.ts b/src/bytes.ts index 6199b675..c043da90 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -105,7 +105,8 @@ const stripZeros = function(a: any): Buffer | number[] | string { } /** - * Attempts to turn a value into a `Buffer`. As input it supports `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` method. + * Attempts to turn a value into a `Buffer`. + * Inputs supported: `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` or `toBuffer()` method. * @param v the value */ export const toBuffer = function(v: any): Buffer { From 4f37fbf57f17a579b9eef24045ddef6b56703666 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 29 Sep 2020 17:12:26 -0700 Subject: [PATCH 16/46] update example to overwrite existing buffer --- test/address.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/address.spec.ts b/test/address.spec.ts index 8814bbd2..00ece37d 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -80,8 +80,8 @@ describe('Address', () => { it('should provide a buffer that does not mutate the original address', () => { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const address = Address.fromString(str) - let addressBuf = address.toBuffer() - addressBuf = Buffer.from('test') + const addressBuf = address.toBuffer() + addressBuf.fill(0) assert.equal(address.toString(), str) }) }) From e280340238b36551598fe67c7b1a3fc1f7dccc24 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 29 Sep 2020 17:43:45 -0700 Subject: [PATCH 17/46] set types for toBuffer param --- src/bytes.ts | 86 ++++++++++++++++++++++++++++++++-------------- test/bytes.spec.ts | 3 +- 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/bytes.ts b/src/bytes.ts index c043da90..b9db5461 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -2,6 +2,16 @@ import * as BN from 'bn.js' import { intToBuffer, stripHexPrefix, padToEven, isHexString, isHexPrefixed } from 'ethjs-util' import { assertIsBuffer, assertIsArray, assertIsHexString } from './helpers' +// These can be moved to './types' when PR#275 is merged. +export interface TransformableToArray { + toArray(): Uint8Array + toBuffer?(): Buffer +} +export interface TransformableToBuffer { + toBuffer(): Buffer + toArray?(): Uint8Array +} + /** * Returns a buffer filled with 0s. * @param bytes the number of bytes the buffer should be @@ -109,34 +119,58 @@ const stripZeros = function(a: any): Buffer | number[] | string { * Inputs supported: `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` or `toBuffer()` method. * @param v the value */ -export const toBuffer = function(v: any): Buffer { - if (!Buffer.isBuffer(v)) { - if (Array.isArray(v) || v instanceof Uint8Array) { - v = Buffer.from(v as Uint8Array) - } else if (typeof v === 'string') { - if (isHexString(v)) { - v = Buffer.from(padToEven(stripHexPrefix(v)), 'hex') - } else { - throw new Error( - `Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}`, - ) - } - } else if (typeof v === 'number') { - v = intToBuffer(v) - } else if (v === null || v === undefined) { - v = Buffer.allocUnsafe(0) - } else if (BN.isBN(v)) { - v = v.toArrayLike(Buffer) - } else if (v.toArray) { - // converts a BN to a Buffer - v = Buffer.from(v.toArray()) - } else if (v.toBuffer) { - v = Buffer.from(v.toBuffer()) - } else { - throw new Error('invalid type') +export const toBuffer = function( + v: + | string + | number + | BN + | Buffer + | Uint8Array + | number[] + | TransformableToArray + | TransformableToBuffer + | null + | undefined, +): Buffer { + if (v === null || v === undefined) { + return Buffer.allocUnsafe(0) + } + + if (Buffer.isBuffer(v)) { + return Buffer.from(v) + } + + if (Array.isArray(v) || v instanceof Uint8Array) { + return Buffer.from(v as Uint8Array) + } + + if (typeof v === 'string') { + if (!isHexString(v)) { + throw new Error( + `Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}`, + ) } + return Buffer.from(padToEven(stripHexPrefix(v)), 'hex') } - return v + + if (typeof v === 'number') { + return intToBuffer(v) + } + + if (BN.isBN(v)) { + return v.toArrayLike(Buffer) + } + + if (v.toArray) { + // converts a BN to a Buffer + return Buffer.from(v.toArray()) + } + + if (v.toBuffer) { + return Buffer.from(v.toBuffer()) + } + + throw new Error('invalid type') } /** diff --git a/test/bytes.spec.ts b/test/bytes.spec.ts index 7057fccd..41323458 100644 --- a/test/bytes.spec.ts +++ b/test/bytes.spec.ts @@ -216,7 +216,7 @@ describe('toBuffer', function() { // 'toArray' assert.deepEqual( toBuffer({ - toArray: function() { + toArray: function(): any { return [1] }, }), @@ -225,6 +225,7 @@ describe('toBuffer', function() { }) it('should fail', function() { assert.throws(function() { + // @ts-ignore toBuffer({ test: 1 }) }) }) From 45512f8c884dea7d5972003b677969901a1f99da Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Wed, 7 Oct 2020 16:33:36 +0200 Subject: [PATCH 18/46] Moved TransformableToArray, TransformableToBuffer interfaces to types --- CHANGELOG.md | 1 + src/bytes.ts | 11 +---------- src/types.ts | 9 +++++++++ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e7638b1..bb2351df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ For more info see the documentation or examples of usage in `test/account.spec.t A new file with helpful TypeScript types has been added to the exports of this project. In this release it contains `BNLike`, `BufferLike`, and `TransformableToBuffer`. + ### Address.toBuffer() The Address class has as a new method `address.toBuffer()` that will give you a copy of the underlying `address.buf`. diff --git a/src/bytes.ts b/src/bytes.ts index b9db5461..d2659d5c 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -1,17 +1,8 @@ import * as BN from 'bn.js' import { intToBuffer, stripHexPrefix, padToEven, isHexString, isHexPrefixed } from 'ethjs-util' +import { TransformableToArray, TransformableToBuffer } from './types' import { assertIsBuffer, assertIsArray, assertIsHexString } from './helpers' -// These can be moved to './types' when PR#275 is merged. -export interface TransformableToArray { - toArray(): Uint8Array - toBuffer?(): Buffer -} -export interface TransformableToBuffer { - toBuffer(): Buffer - toArray?(): Uint8Array -} - /** * Returns a buffer filled with 0s. * @param bytes the number of bytes the buffer should be diff --git a/src/types.ts b/src/types.ts index 64ec1a35..0f3acd2e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,11 +23,20 @@ export type BufferLike = */ export type PrefixedHexString = string +/* + * A type that represents an object that has a `toArray()` method. + */ +export interface TransformableToArray { + toArray(): Uint8Array + toBuffer?(): Buffer +} + /* * A type that represents an object that has a `toBuffer()` method. */ export interface TransformableToBuffer { toBuffer(): Buffer + toArray?(): Uint8Array } /** From 8136c8f7fe4794bb7f8726a5824da86a6c7486a2 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Wed, 7 Oct 2020 16:52:08 +0200 Subject: [PATCH 19/46] Bumped version to v7.0.6, updated CHANGELOG --- CHANGELOG.md | 16 +++++++++++----- package.json | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb2351df..228ce080 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) (modification: no type change headlines) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [7.0.6] - [UNRELEASED] +## [7.0.6] - 2020-10-07 ### New `Account` class @@ -31,21 +31,27 @@ const account = new Account( ) ``` -For more info see the documentation or examples of usage in `test/account.spec.ts`. +For more info see the documentation, examples of usage in `test/account.spec.ts` or +PR [#275](https://github.com/ethereumjs/ethereumjs-util/pull/275). ### New export: TypeScript types -A new file with helpful TypeScript types has been added to the exports of this project. +A new file with helpful TypeScript types has been added to the exports of this project, +see PR [#275](https://github.com/ethereumjs/ethereumjs-util/pull/275). In this release it contains `BNLike`, `BufferLike`, and `TransformableToBuffer`. ### Address.toBuffer() -The Address class has as a new method `address.toBuffer()` that will give you a copy of the underlying `address.buf`. +The Address class has as a new method `address.toBuffer()` that will give you a copy of the underlying `address.buf` +(PR [#277](https://github.com/ethereumjs/ethereumjs-util/pull/277)). ### `toBuffer()` now converts TransformableToBuffer -The `toBuffer()` exported function now additionally converts any object with a `toBuffer()` method. +The `toBuffer()` exported function now additionally converts any object with a `toBuffer()` method +(PR [#277](https://github.com/ethereumjs/ethereumjs-util/pull/277)). + +[7.0.6]: https://github.com/ethereumjs/ethereumjs-util/compare/v7.0.5...v7.0.6 ## [7.0.5] - 2020-09-09 diff --git a/package.json b/package.json index da275c6e..d265a3d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethereumjs-util", - "version": "7.0.5", + "version": "7.0.6", "description": "a collection of utility functions for Ethereum", "main": "dist/index.js", "types": "./dist/index.d.ts", From 54efacbaa2effa5b06ed28a628e4cac0a36458d2 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Wed, 7 Oct 2020 16:52:17 +0200 Subject: [PATCH 20/46] Rebuild documentation --- docs/classes/_account_.account.md | 12 ++++--- docs/classes/_address_.address.md | 13 +++++++ .../_types_.transformabletoarray.md | 34 +++++++++++++++++++ .../_types_.transformabletobuffer.md | 13 ++++++- docs/modules/_account_.md | 34 +++++++++---------- docs/modules/_bytes_.md | 33 +++++++++--------- docs/modules/_helpers_.md | 27 +++------------ docs/modules/_types_.md | 31 ++++++++++++++--- 8 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 docs/interfaces/_types_.transformabletoarray.md diff --git a/docs/classes/_account_.account.md b/docs/classes/_account_.account.md index 42391678..8b83bd60 100644 --- a/docs/classes/_account_.account.md +++ b/docs/classes/_account_.account.md @@ -36,7 +36,7 @@ *Defined in [account.ts:61](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L61)* -This constructor takes the values, validates and assigns them. +This constructor assigns and validates the values. Use the static factory methods to assist in creating an Account from varying data types. **Parameters:** @@ -88,9 +88,9 @@ ___ ▸ **isContract**(): *boolean* -*Defined in [account.ts:96](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L96)* +*Defined in [account.ts:106](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L106)* -Returns a `Boolean` deteremining if the account is a contract. +Returns a `Boolean` determining if the account is a contract. **Returns:** *boolean* @@ -100,9 +100,11 @@ ___ ▸ **isEmpty**(): *boolean* -*Defined in [account.ts:103](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L103)* +*Defined in [account.ts:115](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L115)* Returns a `Boolean` determining if the account is empty. +For more details about account emptiness see [EIP-161](https://eips.ethereum.org/EIPS/eip-161). +Note: The stateRoot is also checked to be empty since in Frontier it was possible to create a contract with no code where nonce remained 0 but some values were written to storage in the constructor (thus stateRoot is not KECCAK256_RLP). **Returns:** *boolean* @@ -112,7 +114,7 @@ ___ ▸ **serialize**(): *Buffer* -*Defined in [account.ts:89](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L89)* +*Defined in [account.ts:99](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L99)* Returns the RLP serialization of the account as a `Buffer`. diff --git a/docs/classes/_address_.address.md b/docs/classes/_address_.address.md index 9e367281..9059f8c7 100644 --- a/docs/classes/_address_.address.md +++ b/docs/classes/_address_.address.md @@ -19,6 +19,7 @@ ### Methods * [isZero](_address_.address.md#iszero) +* [toBuffer](_address_.address.md#tobuffer) * [toString](_address_.address.md#tostring) * [fromPrivateKey](_address_.address.md#static-fromprivatekey) * [fromPublicKey](_address_.address.md#static-frompublickey) @@ -65,6 +66,18 @@ Is address zero. ___ +### toBuffer + +▸ **toBuffer**(): *Buffer* + +*Defined in [address.ts:95](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/address.ts#L95)* + +Returns Buffer representation of address. + +**Returns:** *Buffer* + +___ + ### toString ▸ **toString**(): *string* diff --git a/docs/interfaces/_types_.transformabletoarray.md b/docs/interfaces/_types_.transformabletoarray.md new file mode 100644 index 00000000..b89bdcba --- /dev/null +++ b/docs/interfaces/_types_.transformabletoarray.md @@ -0,0 +1,34 @@ +[ethereumjs-util](../README.md) › ["types"](../modules/_types_.md) › [TransformableToArray](_types_.transformabletoarray.md) + +# Interface: TransformableToArray + +## Hierarchy + +* **TransformableToArray** + +## Index + +### Methods + +* [toArray](_types_.transformabletoarray.md#toarray) +* [toBuffer](_types_.transformabletoarray.md#optional-tobuffer) + +## Methods + +### toArray + +▸ **toArray**(): *Uint8Array* + +*Defined in [types.ts:30](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L30)* + +**Returns:** *Uint8Array* + +___ + +### `Optional` toBuffer + +▸ **toBuffer**(): *Buffer* + +*Defined in [types.ts:31](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L31)* + +**Returns:** *Buffer* diff --git a/docs/interfaces/_types_.transformabletobuffer.md b/docs/interfaces/_types_.transformabletobuffer.md index 7ab3daaf..3a5d1ac2 100644 --- a/docs/interfaces/_types_.transformabletobuffer.md +++ b/docs/interfaces/_types_.transformabletobuffer.md @@ -10,14 +10,25 @@ ### Methods +* [toArray](_types_.transformabletobuffer.md#optional-toarray) * [toBuffer](_types_.transformabletobuffer.md#tobuffer) ## Methods +### `Optional` toArray + +▸ **toArray**(): *Uint8Array* + +*Defined in [types.ts:39](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L39)* + +**Returns:** *Uint8Array* + +___ + ### toBuffer ▸ **toBuffer**(): *Buffer* -*Defined in [types.ts:10](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L10)* +*Defined in [types.ts:38](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L38)* **Returns:** *Buffer* diff --git a/docs/modules/_account_.md b/docs/modules/_account_.md index 0abc65de..cc0c3998 100644 --- a/docs/modules/_account_.md +++ b/docs/modules/_account_.md @@ -38,7 +38,7 @@ • **publicToAddress**: *pubToAddress* = pubToAddress -*Defined in [account.ts:246](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L246)* +*Defined in [account.ts:258](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L258)* ## Functions @@ -46,7 +46,7 @@ ▸ **generateAddress**(`from`: Buffer, `nonce`: Buffer): *Buffer* -*Defined in [account.ts:168](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L168)* +*Defined in [account.ts:180](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L180)* Generates an address of a newly created contract. @@ -65,7 +65,7 @@ ___ ▸ **generateAddress2**(`from`: Buffer, `salt`: Buffer, `initCode`: Buffer): *Buffer* -*Defined in [account.ts:189](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L189)* +*Defined in [account.ts:201](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L201)* Generates an address for a contract created using CREATE2. @@ -85,7 +85,7 @@ ___ ▸ **importPublic**(`publicKey`: Buffer): *Buffer* -*Defined in [account.ts:269](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L269)* +*Defined in [account.ts:281](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L281)* Converts a public key to the Ethereum format. @@ -103,7 +103,7 @@ ___ ▸ **isValidAddress**(`hexAddress`: string): *boolean* -*Defined in [account.ts:116](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L116)* +*Defined in [account.ts:128](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L128)* Checks if the address is a valid. Accepts checksummed addresses too. @@ -121,7 +121,7 @@ ___ ▸ **isValidChecksumAddress**(`hexAddress`: string, `eip1191ChainId?`: undefined | number): *boolean* -*Defined in [account.ts:156](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L156)* +*Defined in [account.ts:168](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L168)* Checks if the address is a valid checksummed address. @@ -142,7 +142,7 @@ ___ ▸ **isValidPrivate**(`privateKey`: Buffer): *boolean* -*Defined in [account.ts:207](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L207)* +*Defined in [account.ts:219](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L219)* Checks if the private key satisfies the rules of the curve secp256k1. @@ -160,7 +160,7 @@ ___ ▸ **isValidPublic**(`publicKey`: Buffer, `sanitize`: boolean): *boolean* -*Defined in [account.ts:217](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L217)* +*Defined in [account.ts:229](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L229)* Checks if the public key satisfies the rules of the curve secp256k1 and the requirements of Ethereum. @@ -180,9 +180,9 @@ ___ ▸ **isZeroAddress**(`hexAddress`: string): *boolean* -*Defined in [account.ts:289](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L289)* +*Defined in [account.ts:301](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L301)* -Checks if a given address is a zero address. +Checks if a given address is the zero address. **Parameters:** @@ -198,7 +198,7 @@ ___ ▸ **privateToAddress**(`privateKey`: Buffer): *Buffer* -*Defined in [account.ts:252](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L252)* +*Defined in [account.ts:264](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L264)* Returns the ethereum address of a given private key. @@ -216,7 +216,7 @@ ___ ▸ **privateToPublic**(`privateKey`: Buffer): *Buffer* -*Defined in [account.ts:260](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L260)* +*Defined in [account.ts:272](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L272)* Returns the ethereum public key of a given private key. @@ -234,7 +234,7 @@ ___ ▸ **pubToAddress**(`pubKey`: Buffer, `sanitize`: boolean): *Buffer* -*Defined in [account.ts:237](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L237)* +*Defined in [account.ts:249](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L249)* Returns the ethereum address of a given public key. Accepts "Ethereum public keys" and SEC1 encoded keys. @@ -254,13 +254,13 @@ ___ ▸ **toChecksumAddress**(`hexAddress`: string, `eip1191ChainId?`: undefined | number): *string* -*Defined in [account.ts:131](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L131)* +*Defined in [account.ts:143](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L143)* Returns a checksummed address. 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. -For more details, consult EIP-1191. +For more details see [EIP-1191](https://eips.ethereum.org/EIPS/eip-1191). 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. @@ -280,8 +280,8 @@ ___ ▸ **zeroAddress**(): *string* -*Defined in [account.ts:280](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L280)* +*Defined in [account.ts:292](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L292)* -Returns a zero address. +Returns the zero address. **Returns:** *string* diff --git a/docs/modules/_bytes_.md b/docs/modules/_bytes_.md index 9fb775c1..15cbd175 100644 --- a/docs/modules/_bytes_.md +++ b/docs/modules/_bytes_.md @@ -26,7 +26,7 @@ ▸ **addHexPrefix**(`str`: string): *string* -*Defined in [bytes.ts:176](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L176)* +*Defined in [bytes.ts:204](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L204)* Adds "0x" to a given `String` if it does not already start with "0x". @@ -44,7 +44,7 @@ ___ ▸ **baToJSON**(`ba`: any): *any* -*Defined in [bytes.ts:189](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L189)* +*Defined in [bytes.ts:217](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L217)* Converts a `Buffer` or `Array` to JSON. @@ -64,7 +64,7 @@ ___ ▸ **bufferToHex**(`buf`: Buffer): *string* -*Defined in [bytes.ts:152](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L152)* +*Defined in [bytes.ts:180](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L180)* Converts a `Buffer` into a `0x`-prefixed hex `String`. @@ -82,7 +82,7 @@ ___ ▸ **bufferToInt**(`buf`: Buffer): *number* -*Defined in [bytes.ts:144](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L144)* +*Defined in [bytes.ts:172](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L172)* Converts a `Buffer` to a `Number`. @@ -100,7 +100,7 @@ ___ ▸ **fromSigned**(`num`: Buffer): *BN* -*Defined in [bytes.ts:161](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L161)* +*Defined in [bytes.ts:189](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L189)* Interprets a `Buffer` as a signed integer and returns a `BN`. Assumes 256-bit numbers. @@ -118,7 +118,7 @@ ___ ▸ **setLengthLeft**(`msg`: Buffer, `length`: number): *Buffer‹›* -*Defined in [bytes.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L20)* +*Defined in [bytes.ts:21](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L21)* Left Pads a `Buffer` with leading zeros till it has `length` bytes. Or it truncates the beginning if it exceeds. @@ -140,7 +140,7 @@ ___ ▸ **setLengthRight**(`msg`: Buffer, `length`: number): *Buffer‹›* -*Defined in [bytes.ts:32](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L32)* +*Defined in [bytes.ts:33](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L33)* Right Pads a `Buffer` with trailing zeros till it has `length` bytes. it truncates the end if it exceeds. @@ -160,17 +160,18 @@ ___ ### `Const` toBuffer -▸ **toBuffer**(`v`: any): *Buffer* +▸ **toBuffer**(`v`: string | number | BN | Buffer | Uint8Array | number[] | [TransformableToArray](../interfaces/_types_.transformabletoarray.md) | [TransformableToBuffer](../interfaces/_types_.transformabletobuffer.md) | null | undefined): *Buffer* -*Defined in [bytes.ts:111](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L111)* +*Defined in [bytes.ts:113](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L113)* -Attempts to turn a value into a `Buffer`. As input it supports `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` method. +Attempts to turn a value into a `Buffer`. +Inputs supported: `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` or `toBuffer()` method. **Parameters:** Name | Type | Description | ------ | ------ | ------ | -`v` | any | the value | +`v` | string | number | BN | Buffer | Uint8Array | number[] | [TransformableToArray](../interfaces/_types_.transformabletoarray.md) | [TransformableToBuffer](../interfaces/_types_.transformabletobuffer.md) | null | undefined | the value | **Returns:** *Buffer* @@ -180,7 +181,7 @@ ___ ▸ **toUnsigned**(`num`: BN): *Buffer* -*Defined in [bytes.ts:169](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L169)* +*Defined in [bytes.ts:197](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L197)* Converts a `BN` to an unsigned integer and returns it as a `Buffer`. Assumes 256-bit numbers. @@ -198,7 +199,7 @@ ___ ▸ **unpadArray**(`a`: number[]): *number[]* -*Defined in [bytes.ts:77](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L77)* +*Defined in [bytes.ts:78](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L78)* Trims leading zeros from an `Array` (of numbers). @@ -218,7 +219,7 @@ ___ ▸ **unpadBuffer**(`a`: Buffer): *Buffer* -*Defined in [bytes.ts:67](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L67)* +*Defined in [bytes.ts:68](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L68)* Trims leading zeros from a `Buffer`. @@ -238,7 +239,7 @@ ___ ▸ **unpadHexString**(`a`: string): *string* -*Defined in [bytes.ts:87](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L87)* +*Defined in [bytes.ts:88](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L88)* Trims leading zeros from a hex-prefixed `String`. @@ -258,7 +259,7 @@ ___ ▸ **zeros**(`bytes`: number): *Buffer* -*Defined in [bytes.ts:9](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L9)* +*Defined in [bytes.ts:10](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/bytes.ts#L10)* Returns a buffer filled with 0s. diff --git a/docs/modules/_helpers_.md b/docs/modules/_helpers_.md index 5f6b892b..70afc005 100644 --- a/docs/modules/_helpers_.md +++ b/docs/modules/_helpers_.md @@ -10,7 +10,6 @@ * [assertIsBuffer](_helpers_.md#const-assertisbuffer) * [assertIsHexString](_helpers_.md#const-assertishexstring) * [assertIsString](_helpers_.md#const-assertisstring) -* [bnToRlp](_helpers_.md#bntorlp) ## Functions @@ -18,7 +17,7 @@ ▸ **assertIsArray**(`input`: number[]): *void* -*Defined in [helpers.ts:31](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L31)* +*Defined in [helpers.ts:29](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L29)* Throws if input is not an array @@ -36,7 +35,7 @@ ___ ▸ **assertIsBuffer**(`input`: Buffer): *void* -*Defined in [helpers.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L20)* +*Defined in [helpers.ts:18](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L18)* Throws if input is not a buffer @@ -54,7 +53,7 @@ ___ ▸ **assertIsHexString**(`input`: string): *void* -*Defined in [helpers.ts:9](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L9)* +*Defined in [helpers.ts:7](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L7)* Throws if a string is not hex prefixed @@ -72,7 +71,7 @@ ___ ▸ **assertIsString**(`input`: string): *void* -*Defined in [helpers.ts:42](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L42)* +*Defined in [helpers.ts:40](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L40)* Throws if input is not a string @@ -83,21 +82,3 @@ Name | Type | Description | `input` | string | value to check | **Returns:** *void* - -___ - -### bnToRlp - -▸ **bnToRlp**(`value`: BN): *Buffer* - -*Defined in [helpers.ts:53](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/helpers.ts#L53)* - -Convert value from BN to RLP (unpadded buffer) - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`value` | BN | value to convert | - -**Returns:** *Buffer* diff --git a/docs/modules/_types_.md b/docs/modules/_types_.md index bf0509b6..8167f51f 100644 --- a/docs/modules/_types_.md +++ b/docs/modules/_types_.md @@ -6,6 +6,7 @@ ### Interfaces +* [TransformableToArray](../interfaces/_types_.transformabletoarray.md) * [TransformableToBuffer](../interfaces/_types_.transformabletobuffer.md) ### Type aliases @@ -14,21 +15,25 @@ * [BufferLike](_types_.md#bufferlike) * [PrefixedHexString](_types_.md#prefixedhexstring) +### Functions + +* [bnToRlp](_types_.md#bntorlp) + ## Type aliases ### BNLike Ƭ **BNLike**: *BN | string | number* -*Defined in [types.ts:3](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L3)* +*Defined in [types.ts:7](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L7)* ___ ### BufferLike -Ƭ **BufferLike**: *Buffer | [TransformableToBuffer](../interfaces/_types_.transformabletobuffer.md) | [PrefixedHexString](_types_.md#prefixedhexstring) | number* +Ƭ **BufferLike**: *Buffer | Uint8Array | number[] | number | BN | [TransformableToBuffer](../interfaces/_types_.transformabletobuffer.md) | [PrefixedHexString](_types_.md#prefixedhexstring)* -*Defined in [types.ts:5](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L5)* +*Defined in [types.ts:12](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L12)* ___ @@ -36,4 +41,22 @@ ___ Ƭ **PrefixedHexString**: *string* -*Defined in [types.ts:7](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L7)* +*Defined in [types.ts:24](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L24)* + +## Functions + +### bnToRlp + +▸ **bnToRlp**(`value`: BN): *Buffer* + +*Defined in [types.ts:46](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/types.ts#L46)* + +Convert value from BN to RLP (unpadded buffer) + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`value` | BN | value to convert | + +**Returns:** *Buffer* From ac22b6ecf015bb7783b94d778089f4c6303a004a Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 15 Oct 2020 10:55:01 +0200 Subject: [PATCH 21/46] Remove stateRoot check for Account.isEmpty() to make emptiness check EIP-161 compliant --- src/account.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/account.ts b/src/account.ts index e3d2c6df..ac94fa5d 100644 --- a/src/account.ts +++ b/src/account.ts @@ -108,17 +108,12 @@ export class Account { } /** - * Returns a `Boolean` determining if the account is empty. - * For more details about account emptiness see [EIP-161](https://eips.ethereum.org/EIPS/eip-161). - * Note: The stateRoot is also checked to be empty since in Frontier it was possible to create a contract with no code where nonce remained 0 but some values were written to storage in the constructor (thus stateRoot is not KECCAK256_RLP). + * Returns a `Boolean` determining if the account is empty complying to the definition of + * account emptiness in [EIP-161](https://eips.ethereum.org/EIPS/eip-161): + * "An account is considered empty when it has no code and zero nonce and zero balance." */ isEmpty(): boolean { - return ( - this.balance.isZero() && - this.nonce.isZero() && - this.stateRoot.equals(KECCAK256_RLP) && - this.codeHash.equals(KECCAK256_NULL) - ) + return this.balance.isZero() && this.nonce.isZero() && this.codeHash.equals(KECCAK256_NULL) } } From d5b886f535b874142651fe02fd50e0b3689efb77 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 15 Oct 2020 10:58:01 +0200 Subject: [PATCH 22/46] Bumped version to v7.0.7, added CHANGELOG entry --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 228ce080..b0f18ebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) (modification: no type change headlines) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [7.0.7] - 2020-10-15 + +- Removed `stateRoot` check for `Account.isEmpty()` to make emptiness check `EIP-161` compliant, + PR [#279](https://github.com/ethereumjs/ethereumjs-util/pull/279) + +[7.0.7]: https://github.com/ethereumjs/ethereumjs-util/compare/v7.0.6...v7.0.7 + ## [7.0.6] - 2020-10-07 ### New `Account` class diff --git a/package.json b/package.json index d265a3d2..d894d479 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethereumjs-util", - "version": "7.0.6", + "version": "7.0.7", "description": "a collection of utility functions for Ethereum", "main": "dist/index.js", "types": "./dist/index.d.ts", From ce3511ba8084646b422a5958e8c0dfd64558c619 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 15 Oct 2020 09:37:21 -0700 Subject: [PATCH 23/46] add account.raw() --- src/account.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/account.ts b/src/account.ts index ac94fa5d..948c1175 100644 --- a/src/account.ts +++ b/src/account.ts @@ -93,11 +93,18 @@ export class Account { } } + /** + * Returns a Buffer Array of the raw Buffers for the account, in order. + */ + raw(): Buffer[] { + return [bnToRlp(this.nonce), bnToRlp(this.balance), this.stateRoot, this.codeHash] + } + /** * Returns the RLP serialization of the account as a `Buffer`. */ serialize(): Buffer { - return rlp.encode([bnToRlp(this.nonce), bnToRlp(this.balance), this.stateRoot, this.codeHash]) + return rlp.encode(this.raw()) } /** From e8bc2c65cf1114307da9510953cf77b768d90261 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 15 Oct 2020 09:39:29 -0700 Subject: [PATCH 24/46] add AddressLike, bnToHex --- src/types.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/types.ts b/src/types.ts index 0f3acd2e..307a0c1e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,5 @@ import * as BN from 'bn.js' +import { Address } from './address' import { unpadBuffer } from './bytes' /* @@ -23,6 +24,12 @@ export type BufferLike = */ export type PrefixedHexString = string +/** + * A type that represents an Address-like value. + * To convert to address, use `new Address(toBuffer(value))` + */ +export type AddressLike = Address | Buffer | string + /* * A type that represents an object that has a `toArray()` method. */ @@ -39,6 +46,13 @@ export interface TransformableToBuffer { toArray?(): Uint8Array } +/** + * Convert BN to 0x-prefixed hex string. + */ +export function bnToHex(value: BN): PrefixedHexString { + return `0x${value.toString(16)}` +} + /** * Convert value from BN to RLP (unpadded buffer) * @param value value to convert From a7025148eaa493179b5c06348d85bf2b5c3abd82 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 15 Oct 2020 09:41:45 -0700 Subject: [PATCH 25/46] update changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0f18ebb..700f09cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [7.0.7] - 2020-10-15 -- Removed `stateRoot` check for `Account.isEmpty()` to make emptiness check `EIP-161` compliant, - PR [#279](https://github.com/ethereumjs/ethereumjs-util/pull/279) +- Removed `stateRoot` check for `Account.isEmpty()` to make emptiness check `EIP-161` compliant, PR [#279](https://github.com/ethereumjs/ethereumjs-util/pull/279) +- Added type `AddressLike` and helper `bnToHex()`, PR [#279](https://github.com/ethereumjs/ethereumjs-util/pull/279) +- Added `account.raw()` which returns a Buffer Array of the raw Buffers for the account in order, PR [#279](https://github.com/ethereumjs/ethereumjs-util/pull/279) [7.0.7]: https://github.com/ethereumjs/ethereumjs-util/compare/v7.0.6...v7.0.7 From 93c43f76a0ea4db5e2d412bcb6e9872a7a92b878 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 21 Jan 2021 09:40:34 -0800 Subject: [PATCH 26/46] add `Address.equals(address: Address)` --- src/address.ts | 9 ++++++++- test/address.spec.ts | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/address.ts b/src/address.ts index 2e2194c6..fac7ecd2 100644 --- a/src/address.ts +++ b/src/address.ts @@ -75,11 +75,18 @@ export class Address { return new Address(generateAddress2(from.buf, salt, initCode)) } + /** + * Is address equal to another. + */ + equals(address: Address): boolean { + return this.buf.equals(address.buf) + } + /** * Is address zero. */ isZero(): boolean { - return this.buf.equals(Address.zero().buf) + return this.equals(Address.zero()) } /** diff --git a/test/address.spec.ts b/test/address.spec.ts index 00ece37d..e94fa2dd 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -84,4 +84,22 @@ describe('Address', () => { addressBuf.fill(0) assert.equal(address.toString(), str) }) + + it('should compare equality properly', () => { + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' + const address1 = Address.fromString(str) + const address2 = new Address(Buffer.from(str.slice(2), 'hex')) + assert.ok(address1.equals(address2)) + assert.ok(address1.buf.equals(address2.buf)) + + const str2 = '0xcd4EC7b66fbc029C116BA9Ffb3e59351c20B5B06' + const address3 = Address.fromString(str2) + assert.ok(!address1.equals(address3)) + + const address3LowerCase = Address.fromString(str2.toLowerCase()) + assert.ok(address3.equals(address3LowerCase)) + + const address4 = Address.zero() + assert.ok(!address1.equals(address4)) + }) }) From cc7850d50f93b112c73d1c49d69d33c6743daeb4 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Sun, 31 Jan 2021 16:27:16 -0300 Subject: [PATCH 27/46] Handle signatures for chainIds greater than 110 The signature module was assuming that the `v` recovery value fit in a single byte. With chainIds larger than 110, since `v(110) = 110 * 2 + 35 = 255`, this assumption breaks (see chainid.network for a list of chainIds with large values). This commit changes `fromRpcSig` so the recovery `v` value is not just the 65th byte of the signaure, but all bytes after the 64th. --- src/signature.ts | 7 +++--- test/signature.spec.ts | 52 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/signature.ts b/src/signature.ts index 2a85d50b..446e01f1 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,6 +1,6 @@ const { ecdsaSign, ecdsaRecover, publicKeyConvert } = require('ethereum-cryptography/secp256k1') import * as BN from 'bn.js' -import { toBuffer, setLengthLeft, bufferToHex } from './bytes' +import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes' import { keccak } from './hash' import { assertIsBuffer } from './helpers' @@ -71,12 +71,11 @@ export const toRpcSig = function(v: number, r: Buffer, s: Buffer, chainId?: numb export const fromRpcSig = function(sig: string): ECDSASignature { const buf: Buffer = toBuffer(sig) - // NOTE: with potential introduction of chainId this might need to be updated - if (buf.length !== 65) { + if (buf.length < 65) { throw new Error('Invalid signature length') } - let v = buf[64] + let v = bufferToInt(buf.slice(64)) // support both versions of `eth_sign` responses if (v < 27) { v += 27 diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 2969ce7b..041580b7 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -46,6 +46,20 @@ describe('ecsign', function() { ) assert.equal(sig.v, 41) }) + + it('should produce a signature for chainId=150', function() { + const chainId = 150 + const sig = ecsign(echash, ecprivkey, chainId) + assert.deepEqual( + sig.r, + Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex'), + ) + assert.deepEqual( + sig.s, + Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex'), + ) + assert.equal(sig.v, chainId * 2 + 35) + }) }) describe('ecrecover', function() { @@ -63,6 +77,14 @@ describe('ecrecover', function() { const pubkey = ecrecover(echash, v, r, s, chainId) assert.deepEqual(pubkey, privateToPublic(ecprivkey)) }) + it('should recover a public key (chainId = 150)', function() { + const chainId = 150 + const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') + const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const v = chainId * 2 + 35 + const pubkey = ecrecover(echash, v, r, s, chainId) + assert.deepEqual(pubkey, privateToPublic(ecprivkey)) + }) it('should fail on an invalid signature (v = 21)', function() { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') @@ -160,6 +182,13 @@ describe('isValidSignature', function() { const v = 41 assert.equal(isValidSignature(v, r, s, false, chainId), true) }) + it('should work otherwise(chainId=150)', function() { + const chainId = 150 + const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') + const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const v = chainId * 2 + 35 + assert.equal(isValidSignature(v, r, s, false, chainId), true) + }) // FIXME: add homestead test }) @@ -194,13 +223,32 @@ describe('message sig', function() { ) }) - it('should throw on invalid length', function() { + it('should return hex strings that the RPC can use (chainId=150)', function() { + const chainId = 150 + const v = chainId * 2 + 35 + assert.equal( + toRpcSig(v, r, s, chainId), + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f', + ) + assert.deepEqual( + fromRpcSig( + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f', + ), + { + v, + r: r, + s: s, + }, + ) + }) + + it('should throw on shorter length', function() { assert.throws(function() { fromRpcSig('') }) assert.throws(function() { fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca660042', + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca', ) }) }) From fcb65c459e3b48801b2e94d92e17bf58d04bd9b8 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Mon, 1 Feb 2021 11:36:45 +0100 Subject: [PATCH 28/46] Bumped version to v7.0.8, added CHANGELOG entry --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 700f09cc..f493ffda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) (modification: no type change headlines) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [7.0.8] - 2021-02-01 + +- New `Address.equals(address: Address)` function for easier address equality comparions, PR [#285](https://github.com/ethereumjs/ethereumjs-util/pull/285) +- Fixed a bug in `fromRpcSig()` in the `signature` module not working correctly for chain IDs greater than 110, PR [#287](https://github.com/ethereumjs/ethereumjs-util/pull/287) + +[7.0.8]: https://github.com/ethereumjs/ethereumjs-util/compare/v7.0.7...v7.0.8 + ## [7.0.7] - 2020-10-15 - Removed `stateRoot` check for `Account.isEmpty()` to make emptiness check `EIP-161` compliant, PR [#279](https://github.com/ethereumjs/ethereumjs-util/pull/279) diff --git a/package.json b/package.json index d894d479..1d3c861c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethereumjs-util", - "version": "7.0.7", + "version": "7.0.8", "description": "a collection of utility functions for Ethereum", "main": "dist/index.js", "types": "./dist/index.d.ts", From a2c8296876f3de3af8d368f194f16d2ea6762e41 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 4 Feb 2021 14:39:32 -0800 Subject: [PATCH 29/46] * upgrade to ethereumjs-config 2.0 * switch from coveralls to codecov * fix lint errors --- .eslintrc.js | 6 +++ .github/workflows/build.yml | 6 +-- README.md | 14 +++-- codecov.yml | 7 +++ package.json | 25 ++++----- prettier.config.js | 2 +- src/account.ts | 37 ++++++------- src/address.ts | 2 +- src/bytes.ts | 74 +++++++++++++------------- src/constants.ts | 6 +-- src/hash.ts | 43 ++++++++------- src/object.ts | 10 ++-- src/signature.ts | 32 +++++------ src/types.ts | 2 +- test/account.spec.ts | 102 ++++++++++++++++++------------------ test/address.spec.ts | 6 +-- test/bytes.spec.ts | 10 ++-- test/constants.spec.ts | 20 +++---- test/externals.spec.ts | 24 +++++---- test/hash.spec.ts | 4 +- test/object.spec.ts | 54 +++++++++---------- test/signature.spec.ts | 54 +++++++++---------- tsconfig.json | 2 +- tsconfig.prod.json | 2 +- tslint.json | 3 -- 25 files changed, 278 insertions(+), 269 deletions(-) create mode 100644 .eslintrc.js create mode 100644 codecov.yml delete mode 100644 tslint.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..aa0843b6 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: '@ethereumjs/eslint-config-defaults', + env: { + mocha: true + } +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a3a549b..a14057a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: - run: npm install - run: npm run test - - name: Upload coverage to Coveralls - uses: coverallsapp/github-action@master + - uses: codecov/codecov-action@v1 + if: ${{ matrix.node-version == 12 }} with: - github-token: ${{ secrets.GITHUB_TOKEN }} + file: ./coverage/lcov.info diff --git a/README.md b/README.md index 1d22e35c..67e8679d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # SYNOPSIS -[![NPM Package](https://img.shields.io/npm/v/ethereumjs-util.svg)](https://www.npmjs.org/package/ethereumjs-util) -[![Actions Status](https://github.com/ethereumjs/ethereumjs-util/workflows/Build/badge.svg)](https://github.com/ethereumjs/ethereumjs-util/actions) -[![Coverage Status](https://img.shields.io/coveralls/ethereumjs/ethereumjs-util.svg)](https://coveralls.io/r/ethereumjs/ethereumjs-util) +[![NPM Status][npm-badge]][npm-link] +[![Actions Status][actions-badge]][actions-link] +[![Coverage Status][coverage-badge]][coverage-link] [![Discord][discord-badge]][discord-link] A collection of utility functions for Ethereum. It can be used in Node.js and in the browser with [browserify](http://browserify.org/). @@ -71,7 +71,7 @@ The following methods are available provided by [ethjs-util](https://github.com/ - toAscii - getKeys -Import can be done directly by function name analogous to the build-in function import: +They can be imported by name: ```js import { intToHex, stripHexPrefix } from 'ethereumjs-util' @@ -94,5 +94,11 @@ If you want to join for work or do improvements on the libraries have a look at MPL-2.0 +[npm-badge]: https://img.shields.io/npm/v/ethereumjs-util.svg +[npm-link]: https://www.npmjs.org/package/ethereumjs-util +[actions-badge]: https://github.com/ethereumjs/ethereumjs-util/workflows/Build/badge.svg +[actions-link]: https://github.com/ethereumjs/ethereumjs-util/actions +[coverage-badge]: https://codecov.io/gh/ethereumjs/ethereumjs-util/branch/master/graph/badge.svg +[coverage-link]: https://codecov.io/gh/ethereumjs/ethereumjs-util [discord-badge]: https://img.shields.io/static/v1?logo=discord&label=discord&message=Join&color=blue [discord-link]: https://discord.gg/TNwARpR diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..3bc0db94 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,7 @@ +coverage: + status: + project: + default: + target: auto + threshold: 2% + base: auto diff --git a/package.json b/package.json index 1d3c861c..6b8f355a 100644 --- a/package.json +++ b/package.json @@ -11,19 +11,15 @@ "dist" ], "scripts": { - "build": "ethereumjs-config-build", + "build": "ethereumjs-config-ts-build", "prepublishOnly": "npm run test && npm run build", "docs:build": "npx typedoc --options typedoc.js", - "format": "ethereumjs-config-format", - "format:fix": "ethereumjs-config-format-fix", "lint": "ethereumjs-config-lint", "lint:fix": "ethereumjs-config-lint-fix", "test": "npm run lint && npm run test:node && npm run test:browser", "test:browser": "karma start karma.conf.js", "test:node": "nyc --reporter=lcov mocha --require ts-node/register 'test/*.spec.ts'", - "tsc": "ethereumjs-config-tsc", - "tslint": "ethereumjs-config-tslint", - "tslint:fix": "ethereumjs-config-tslint-fix" + "tsc": "ethereumjs-config-tsc" }, "husky": { "hooks": { @@ -89,7 +85,7 @@ }, "homepage": "https://github.com/ethereumjs/ethereumjs-util", "dependencies": { - "@types/bn.js": "^4.11.3", + "@types/bn.js": "^5.1.0", "bn.js": "^5.1.2", "create-hash": "^1.1.2", "ethereum-cryptography": "^0.1.3", @@ -97,10 +93,11 @@ "rlp": "^2.2.4" }, "devDependencies": { - "@ethereumjs/config-prettier": "^1.1.0", - "@ethereumjs/config-tsc": "^1.1.0", - "@ethereumjs/config-tslint": "^1.1.0", - "@types/mocha": "^5.2.7", + "@ethereumjs/config-coverage": "^2.0.0", + "@ethereumjs/config-typescript": "^2.0.0", + "@ethereumjs/eslint-config-defaults": "^2.0.0", + "@types/assert": "^1.5.4", + "@types/mocha": "^8.2.0", "@types/node": "^11.9.0", "@types/secp256k1": "^4.0.1", "husky": "^2.1.0", @@ -109,14 +106,12 @@ "karma-firefox-launcher": "^1.0.0", "karma-mocha": "^2.0.0", "karma-typescript": "^4.1.1", - "mocha": "^6.0.0", + "mocha": "^8.2.1", "nyc": "^15.0.0", "prettier": "^1.15.3", "ts-node": "^8.6.2", - "tslint": "^5.12.0", "typedoc": "next", "typedoc-plugin-markdown": "^2.2.16", - "typescript": "^3.8.3", - "typestrict": "^1.0.2" + "typescript": "^3.8.3" } } diff --git a/prettier.config.js b/prettier.config.js index 0f2e5b7a..71472721 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1 +1 @@ -module.exports = require('@ethereumjs/config-prettier') +module.exports = require('@ethereumjs/eslint-config-defaults/prettier.config.js') diff --git a/src/account.ts b/src/account.ts index 948c1175..e6895064 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,5 +1,5 @@ -import * as assert from 'assert' -import * as BN from 'bn.js' +import assert from 'assert' +import BN from 'bn.js' import * as rlp from 'rlp' import { stripHexPrefix } from 'ethjs-util' import { KECCAK256_RLP, KECCAK256_NULL } from './constants' @@ -12,7 +12,7 @@ const { privateKeyVerify, publicKeyCreate, publicKeyVerify, - publicKeyConvert, + publicKeyConvert } = require('ethereum-cryptography/secp256k1') export interface AccountData { @@ -35,7 +35,7 @@ export class Account { nonce ? new BN(toBuffer(nonce)) : undefined, balance ? new BN(toBuffer(balance)) : undefined, stateRoot ? toBuffer(stateRoot) : undefined, - codeHash ? toBuffer(codeHash) : undefined, + codeHash ? toBuffer(codeHash) : undefined ) } @@ -52,12 +52,7 @@ export class Account { public static fromValuesArray(values: Buffer[]) { const [nonce, balance, stateRoot, codeHash] = values - return new Account( - nonce ? new BN(nonce) : undefined, - balance ? new BN(balance) : undefined, - stateRoot, - codeHash, - ) + return new Account(new BN(nonce), new BN(balance), stateRoot, codeHash) } /** @@ -68,7 +63,7 @@ export class Account { nonce = new BN(0), balance = new BN(0), stateRoot = KECCAK256_RLP, - codeHash = KECCAK256_NULL, + codeHash = KECCAK256_NULL ) { this.nonce = nonce this.balance = balance @@ -169,7 +164,7 @@ export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: n */ export const isValidChecksumAddress = function( hexAddress: string, - eip1191ChainId?: number, + eip1191ChainId?: number ): boolean { return isValidAddress(hexAddress) && toChecksumAddress(hexAddress, eip1191ChainId) === hexAddress } @@ -209,7 +204,7 @@ export const generateAddress2 = function(from: Buffer, salt: Buffer, initCode: B assert(salt.length === 32) const address = keccak256( - Buffer.concat([Buffer.from('ff', 'hex'), from, salt, keccak256(initCode)]), + Buffer.concat([Buffer.from('ff', 'hex'), from, salt, keccak256(initCode)]) ) return address.slice(-20) @@ -259,14 +254,6 @@ export const pubToAddress = function(pubKey: Buffer, sanitize: boolean = false): } export const publicToAddress = pubToAddress -/** - * Returns the ethereum address of a given private key. - * @param privateKey A private key must be 256 bits wide - */ -export const privateToAddress = function(privateKey: Buffer): Buffer { - return publicToAddress(privateToPublic(privateKey)) -} - /** * Returns the ethereum public key of a given private key. * @param privateKey A private key must be 256 bits wide @@ -277,6 +264,14 @@ export const privateToPublic = function(privateKey: Buffer): Buffer { return Buffer.from(publicKeyCreate(privateKey, false)).slice(1) } +/** + * Returns the ethereum address of a given private key. + * @param privateKey A private key must be 256 bits wide + */ +export const privateToAddress = function(privateKey: Buffer): Buffer { + return publicToAddress(privateToPublic(privateKey)) +} + /** * Converts a public key to the Ethereum format. */ diff --git a/src/address.ts b/src/address.ts index fac7ecd2..c80c3d34 100644 --- a/src/address.ts +++ b/src/address.ts @@ -6,7 +6,7 @@ import { pubToAddress, privateToAddress, generateAddress, - generateAddress2, + generateAddress2 } from './account' export class Address { diff --git a/src/bytes.ts b/src/bytes.ts index d2659d5c..7594771f 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -1,4 +1,4 @@ -import * as BN from 'bn.js' +import BN from 'bn.js' import { intToBuffer, stripHexPrefix, padToEven, isHexString, isHexPrefixed } from 'ethjs-util' import { TransformableToArray, TransformableToBuffer } from './types' import { assertIsBuffer, assertIsArray, assertIsHexString } from './helpers' @@ -11,6 +11,31 @@ export const zeros = function(bytes: number): Buffer { return Buffer.allocUnsafe(bytes).fill(0) } +/** + * Pads a `Buffer` with zeros till it has `length` bytes. + * Truncates the beginning or end of input if its length exceeds `length`. + * @param msg the value to pad (Buffer) + * @param length the number of bytes the output should be + * @param right whether to start padding form the left or right + * @return (Buffer) + */ +const setLength = function(msg: Buffer, length: number, right: boolean) { + const buf = zeros(length) + if (right) { + if (msg.length < length) { + msg.copy(buf) + return buf + } + return msg.slice(0, length) + } else { + if (msg.length < length) { + msg.copy(buf, length - msg.length) + return buf + } + return msg.slice(-length) + } +} + /** * Left Pads a `Buffer` with leading zeros till it has `length` bytes. * Or it truncates the beginning if it exceeds. @@ -36,28 +61,17 @@ export const setLengthRight = function(msg: Buffer, length: number) { } /** - * Pads a `Buffer` with zeros till it has `length` bytes. - * Truncates the beginning or end of input if its length exceeds `length`. - * @param msg the value to pad (Buffer) - * @param length the number of bytes the output should be - * @param right whether to start padding form the left or right - * @return (Buffer) + * Trims leading zeros from a `Buffer`, `String` or `Number[]`. + * @param a (Buffer|Array|String) + * @return (Buffer|Array|String) */ -const setLength = function(msg: Buffer, length: number, right: boolean) { - const buf = zeros(length) - if (right) { - if (msg.length < length) { - msg.copy(buf) - return buf - } - return msg.slice(0, length) - } else { - if (msg.length < length) { - msg.copy(buf, length - msg.length) - return buf - } - return msg.slice(-length) +const stripZeros = function(a: any): Buffer | number[] | string { + let first = a[0] + while (a.length > 0 && first.toString() === '0') { + a = a.slice(1) + first = a[0] } + return a } /** @@ -91,20 +105,6 @@ export const unpadHexString = function(a: string): string { return stripZeros(a) as string } -/** - * Trims leading zeros from a `Buffer`, `String` or `Number[]`. - * @param a (Buffer|Array|String) - * @return (Buffer|Array|String) - */ -const stripZeros = function(a: any): Buffer | number[] | string { - let first = a[0] - while (a.length > 0 && first.toString() === '0') { - a = a.slice(1) - first = a[0] - } - return a -} - /** * Attempts to turn a value into a `Buffer`. * Inputs supported: `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` or `toBuffer()` method. @@ -121,7 +121,7 @@ export const toBuffer = function( | TransformableToArray | TransformableToBuffer | null - | undefined, + | undefined ): Buffer { if (v === null || v === undefined) { return Buffer.allocUnsafe(0) @@ -138,7 +138,7 @@ export const toBuffer = function( if (typeof v === 'string') { if (!isHexString(v)) { throw new Error( - `Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}`, + `Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}` ) } return Buffer.from(padToEven(stripHexPrefix(v)), 'hex') diff --git a/src/constants.ts b/src/constants.ts index 160284f3..dc0742b6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,12 +1,12 @@ const Buffer = require('buffer').Buffer -import * as BN from 'bn.js' +import BN from 'bn.js' /** * The max integer that this VM can handle */ export const MAX_INTEGER: BN = new BN( 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - 16, + 16 ) /** @@ -14,7 +14,7 @@ export const MAX_INTEGER: BN = new BN( */ export const TWO_POW256: BN = new BN( '10000000000000000000000000000000000000000000000000000000000000000', - 16, + 16 ) /** diff --git a/src/hash.ts b/src/hash.ts index 51a30ddc..79472a96 100644 --- a/src/hash.ts +++ b/src/hash.ts @@ -1,6 +1,5 @@ const { keccak224, keccak384, keccak256: k256, keccak512 } = require('ethereum-cryptography/keccak') const createHash = require('create-hash') -import * as ethjsUtil from 'ethjs-util' import * as rlp from 'rlp' import { toBuffer, setLengthLeft } from './bytes' import { assertIsString, assertIsBuffer, assertIsArray, assertIsHexString } from './helpers' @@ -70,6 +69,17 @@ export const keccakFromArray = function(a: number[], bits: number = 256) { return keccak(toBuffer(a), bits) } +/** + * Creates SHA256 hash of an input. + * @param a The input data (Buffer|Array|String) + */ +const _sha256 = function(a: any): Buffer { + a = toBuffer(a) + return createHash('sha256') + .update(a) + .digest() +} + /** * Creates SHA256 hash of a Buffer input. * @param a The input data (Buffer) @@ -98,14 +108,20 @@ export const sha256FromArray = function(a: number[]): Buffer { } /** - * Creates SHA256 hash of an input. - * @param a The input data (Buffer|Array|String) + * Creates RIPEMD160 hash of the input. + * @param a The input data (Buffer|Array|String|Number) + * @param padded Whether it should be padded to 256 bits or not */ -const _sha256 = function(a: any): Buffer { +const _ripemd160 = function(a: any, padded: boolean): Buffer { a = toBuffer(a) - return createHash('sha256') + const hash = createHash('rmd160') .update(a) .digest() + if (padded === true) { + return setLengthLeft(hash, 32) + } else { + return hash + } } /** @@ -138,23 +154,6 @@ export const ripemd160FromArray = function(a: number[], padded: boolean): Buffer return _ripemd160(a, padded) } -/** - * Creates RIPEMD160 hash of the input. - * @param a The input data (Buffer|Array|String|Number) - * @param padded Whether it should be padded to 256 bits or not - */ -const _ripemd160 = function(a: any, padded: boolean): Buffer { - a = toBuffer(a) - const hash = createHash('rmd160') - .update(a) - .digest() - if (padded === true) { - return setLengthLeft(hash, 32) - } else { - return hash - } -} - /** * Creates SHA-3 hash of the RLP encoded version of the input. * @param a The input data diff --git a/src/object.ts b/src/object.ts index e24991eb..c5c55c6e 100644 --- a/src/object.ts +++ b/src/object.ts @@ -1,5 +1,5 @@ +import assert from 'assert' import * as ethjsUtil from 'ethjs-util' -import * as assert from 'assert' import * as rlp from 'rlp' import { toBuffer, baToJSON, unpadBuffer } from './bytes' @@ -51,12 +51,12 @@ export const defineProperties = function(self: any, fields: any, data?: any) { v = unpadBuffer(v) assert( field.length >= v.length, - `The field ${field.name} must not have more ${field.length} bytes`, + `The field ${field.name} must not have more ${field.length} bytes` ) } else if (!(field.allowZero && v.length === 0) && field.length) { assert( field.length === v.length, - `The field ${field.name} must have byte length of ${field.length}`, + `The field ${field.name} must have byte length of ${field.length}` ) } @@ -67,7 +67,7 @@ export const defineProperties = function(self: any, fields: any, data?: any) { enumerable: true, configurable: true, get: getter, - set: setter, + set: setter }) if (field.default) { @@ -80,7 +80,7 @@ export const defineProperties = function(self: any, fields: any, data?: any) { enumerable: false, configurable: true, set: setter, - get: getter, + get: getter }) } }) diff --git a/src/signature.ts b/src/signature.ts index 446e01f1..c0235af3 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,5 +1,5 @@ const { ecdsaSign, ecdsaRecover, publicKeyConvert } = require('ethereum-cryptography/secp256k1') -import * as BN from 'bn.js' +import BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes' import { keccak } from './hash' import { assertIsBuffer } from './helpers' @@ -16,7 +16,7 @@ export interface ECDSASignature { export const ecsign = function( msgHash: Buffer, privateKey: Buffer, - chainId?: number, + chainId?: number ): ECDSASignature { const sig = ecdsaSign(msgHash, privateKey) const recovery: number = sig.recid @@ -24,12 +24,20 @@ export const ecsign = function( const ret = { r: Buffer.from(sig.signature.slice(0, 32)), s: Buffer.from(sig.signature.slice(32, 64)), - v: chainId ? recovery + (chainId * 2 + 35) : recovery + 27, + v: chainId ? recovery + (chainId * 2 + 35) : recovery + 27 } return ret } +function calculateSigRecovery(v: number, chainId?: number): number { + return chainId ? v - (2 * chainId + 35) : v - 27 +} + +function isValidSigRecovery(recovery: number): boolean { + return recovery === 0 || recovery === 1 +} + /** * ECDSA public key recovery from signature. * @returns Recovered public key @@ -39,7 +47,7 @@ export const ecrecover = function( v: number, r: Buffer, s: Buffer, - chainId?: number, + chainId?: number ): Buffer { const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64) const recovery = calculateSigRecovery(v, chainId) @@ -84,7 +92,7 @@ export const fromRpcSig = function(sig: string): ECDSASignature { return { v: v, r: buf.slice(0, 32), - s: buf.slice(32, 64), + s: buf.slice(32, 64) } } @@ -97,11 +105,11 @@ export const isValidSignature = function( r: Buffer, s: Buffer, homesteadOrLater: boolean = true, - chainId?: number, + chainId?: number ): boolean { const SECP256K1_N_DIV_2 = new BN( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', - 16, + 16 ) const SECP256K1_N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) @@ -137,15 +145,7 @@ export const hashPersonalMessage = function(message: Buffer): Buffer { assertIsBuffer(message) const prefix = Buffer.from( `\u0019Ethereum Signed Message:\n${message.length.toString()}`, - 'utf-8', + 'utf-8' ) return keccak(Buffer.concat([prefix, message])) } - -function calculateSigRecovery(v: number, chainId?: number): number { - return chainId ? v - (2 * chainId + 35) : v - 27 -} - -function isValidSigRecovery(recovery: number): boolean { - return recovery === 0 || recovery === 1 -} diff --git a/src/types.ts b/src/types.ts index 307a0c1e..7b3d7725 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import * as BN from 'bn.js' +import BN from 'bn.js' import { Address } from './address' import { unpadBuffer } from './bytes' diff --git a/test/account.spec.ts b/test/account.spec.ts index c999b357..ce9e22a0 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -1,5 +1,5 @@ -import * as assert from 'assert' -import * as BN from 'bn.js' +import assert from 'assert' +import BN from 'bn.js' import * as rlp from 'rlp' import { Account, @@ -14,7 +14,7 @@ import { toBuffer, isValidChecksumAddress, isValidAddress, - toChecksumAddress, + toChecksumAddress } from '../src' const eip1014Testdata = require('./testdata/eip1014Examples.json') @@ -30,13 +30,13 @@ describe('Account', function() { it('should have stateRoot equal to KECCAK256_RLP', function() { assert.ok( account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) }) it('should have codeHash equal to KECCAK256_NULL', function() { assert.equal( account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) }) }) @@ -46,7 +46,7 @@ describe('Account', function() { '0x02', // nonce '0x0384', // balance '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', // stateRoot - '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', // codeHash + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' // codeHash ] const account = Account.fromValuesArray(raw.map(toBuffer)) it('should have correct nonce', function() { @@ -58,13 +58,13 @@ describe('Account', function() { it('should have correct stateRoot', function() { assert.equal( account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) }) it('should have correct codeHash', function() { assert.equal( account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) }) }) @@ -74,7 +74,7 @@ describe('Account', function() { nonce: '0x02', balance: '0x0384', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' } const account = Account.fromAccountData(raw) it('should have correct nonce', function() { @@ -86,13 +86,13 @@ describe('Account', function() { it('should have correct stateRoot', function() { assert.equal( account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) }) it('should have correct codeHash', function() { assert.equal( account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) }) }) @@ -100,7 +100,7 @@ describe('Account', function() { describe('from RLP data', function() { const accountRlp = Buffer.from( 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - 'hex', + 'hex' ) const account = Account.fromRlpSerializedAccount(accountRlp) it('should have correct nonce', function() { @@ -112,13 +112,13 @@ describe('Account', function() { it('should have correct stateRoot', function() { assert.equal( account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) }) it('should have correct codeHash', function() { assert.equal( account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) }) }) @@ -128,7 +128,7 @@ describe('Account', function() { nonce: '0x01', balance: '0x42', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' } const account = Account.fromAccountData(raw) const accountRlp = rlp.encode([raw.nonce, raw.balance, raw.stateRoot, raw.codeHash]) @@ -141,7 +141,7 @@ describe('Account', function() { it('should return false for a non-contract account', function() { const accountRlp = Buffer.from( 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - 'hex', + 'hex' ) const account = Account.fromRlpSerializedAccount(accountRlp) assert.equal(account.isContract(), false) @@ -152,7 +152,7 @@ describe('Account', function() { nonce: '0x01', balance: '0x0042', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' } const account = Account.fromAccountData(raw) assert.ok(account.isContract()) @@ -170,7 +170,7 @@ describe('Account', function() { nonce: '0x01', balance: '0x0042', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - codeHash: '0xd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b', + codeHash: '0xd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b' } const account = Account.fromAccountData(raw) assert.equal(account.isEmpty(), false) @@ -253,56 +253,56 @@ describe('isValidPublic', function() { it('should fail on too short input', function() { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey), false) }) it('should fail on too big input', function() { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d00', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey), false) }) it('should fail on SEC1 key', function() { const pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey), false) }) it("shouldn't fail on SEC1 key with sanitize enabled", function() { const pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey, true), true) }) it('should fail with an invalid SEC1 public key', function() { const pubKey = Buffer.from( '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey, true), false) }) it('should work with compressed keys with sanitize enabled', function() { const pubKey = Buffer.from( '033a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey, true), true) }) it('should work with sanitize enabled', function() { const pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey, true), true) }) it('should work otherwise', function() { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) assert.equal(isValidPublic(pubKey), true) }) @@ -345,7 +345,7 @@ describe('publicToAddress', function() { it('should produce an address given a public key', function() { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' const r = publicToAddress(pubKey) @@ -354,7 +354,7 @@ describe('publicToAddress', function() { it('should produce an address given a SEC1 public key', function() { const pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' const r = publicToAddress(pubKey, true) @@ -363,7 +363,7 @@ describe('publicToAddress', function() { it("shouldn't produce an address given an invalid SEC1 public key", function() { const pubKey = Buffer.from( '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) assert.throws(function() { publicToAddress(pubKey, true) @@ -372,7 +372,7 @@ describe('publicToAddress', function() { it("shouldn't produce an address given an invalid public key", function() { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', - 'hex', + 'hex' ) assert.throws(function() { publicToAddress(pubKey) @@ -393,7 +393,7 @@ describe('privateToPublic', function() { '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' const privateKey = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', - 'hex', + 'hex' ) const r: any = privateToPublic(privateKey).toString('hex') assert.equal(r.toString('hex'), pubKey) @@ -401,11 +401,11 @@ describe('privateToPublic', function() { it("shouldn't produce a public key given an invalid private key", function() { const privateKey1 = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f2a', - 'hex', + 'hex' ) const privateKey2 = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c', - 'hex', + 'hex' ) assert.throws(function() { privateToPublic(privateKey1) @@ -432,7 +432,7 @@ describe('privateToAddress', function() { // Our private key const privateKey = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', - 'hex', + 'hex' ) const r: any = privateToAddress(privateKey).toString('hex') assert.equal(r.toString('hex'), address) @@ -443,7 +443,7 @@ describe('generateAddress', function() { it('should produce an address given a public key', function() { const add: any = generateAddress( Buffer.from('990ccf8a0de58091c028d6ff76bb235ee67c1c39', 'utf8'), - toBuffer(14), + toBuffer(14) ).toString('hex') assert.equal(add.toString('hex'), '936a4295d8d74e310c0c95f0a63e53737b998d12') }) @@ -453,7 +453,7 @@ describe('generateAddress with hex prefix', function() { it('should produce an address given a public key', function() { const add: any = generateAddress( toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), - toBuffer(14), + toBuffer(14) ).toString('hex') assert.equal(add.toString('hex'), 'd658a4b8247c14868f3c512fa5cbb6e458e4a989') }) @@ -463,7 +463,7 @@ describe('generateAddress with nonce 0 (special case)', function() { it('should produce an address given a public key', function() { const add: any = generateAddress( toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), - toBuffer(0), + toBuffer(0) ).toString('hex') assert.equal(add.toString('hex'), 'bfa69ba91385206bfdd2d8b9c1a5d6c10097a85b') }) @@ -474,7 +474,7 @@ describe('generateAddress with non-buffer inputs', function() { assert.throws(function() { generateAddress( ('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') as Buffer, - toBuffer(0), + toBuffer(0) ) }) }) @@ -482,7 +482,7 @@ describe('generateAddress with non-buffer inputs', function() { assert.throws(function() { generateAddress( toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), - (0) as Buffer, + (0) as Buffer ) }) }) @@ -490,12 +490,12 @@ describe('generateAddress with non-buffer inputs', function() { describe('generateAddress2: EIP-1014 testdata examples', function() { for (let i = 0; i <= 6; i++) { - let e = eip1014Testdata[i] + const e = eip1014Testdata[i] it(`${e['comment']}: should generate the addresses provided`, function() { - let result = generateAddress2( + const result = generateAddress2( toBuffer(e['address']), toBuffer(e['salt']), - toBuffer(e['initCode']), + toBuffer(e['initCode']) ) assert.equal('0x' + result.toString('hex'), e['result']) }) @@ -510,7 +510,7 @@ describe('generateAddress2: non-buffer inputs', function() { generateAddress2( (e['address']) as Buffer, toBuffer(e['salt']), - toBuffer(e['initCode']), + toBuffer(e['initCode']) ) }) }) @@ -519,7 +519,7 @@ describe('generateAddress2: non-buffer inputs', function() { generateAddress2( toBuffer(e['address']), (e['salt']) as Buffer, - toBuffer(e['initCode']), + toBuffer(e['initCode']) ) }) }) @@ -528,7 +528,7 @@ describe('generateAddress2: non-buffer inputs', function() { generateAddress2( toBuffer(e['address']), toBuffer(e['salt']), - (e['initCode']) as Buffer, + (e['initCode']) as Buffer ) }) }) @@ -545,7 +545,7 @@ const eip55ChecksumAddresses = [ '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359', '0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB', - '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', + '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb' ] const eip1191ChecksummAddresses = { @@ -558,7 +558,7 @@ const eip1191ChecksummAddresses = { '0xd1220a0CF47C7B9Be7A2E6ba89F429762E7B9Adb', '0xdBf03b407c01e7cD3CBea99509d93f8dDDC8C6fB', '0xDe709F2102306220921060314715629080E2fb77', - '0xfb6916095Ca1dF60bB79cE92ce3ea74C37c5D359', + '0xfb6916095Ca1dF60bB79cE92ce3ea74C37c5D359' ], 30: [ '0x6549F4939460DE12611948B3F82B88C3C8975323', @@ -570,7 +570,7 @@ const eip1191ChecksummAddresses = { '0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB', '0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB', '0xDe709F2102306220921060314715629080e2FB77', - '0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359', + '0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359' ], 31: [ '0x42712D45473476B98452F434E72461577D686318', @@ -583,15 +583,15 @@ const eip1191ChecksummAddresses = { '0xDE709F2102306220921060314715629080e2Fb77', '0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359', '0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB', - '0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB', - ], + '0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB' + ] } describe('.toChecksumAddress()', function() { describe('EIP55', function() { it('should work', function() { for (let i = 0; i < eip55ChecksumAddresses.length; i++) { - let tmp = eip55ChecksumAddresses[i] + const tmp = eip55ChecksumAddresses[i] assert.equal(toChecksumAddress(tmp.toLowerCase()), tmp) } }) diff --git a/test/address.spec.ts b/test/address.spec.ts index e94fa2dd..ae92f17e 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -1,4 +1,4 @@ -import * as assert from 'assert' +import assert from 'assert' import { BN, toBuffer } from '../src' import { Address } from '../src' const eip1014Testdata = require('./testdata/eip1014Examples.json') @@ -36,7 +36,7 @@ describe('Address', () => { it('should instantiate from public key', () => { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex', + 'hex' ) const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const addr = Address.fromPublicKey(pubKey) @@ -46,7 +46,7 @@ describe('Address', () => { it('should fail to instantiate from invalid public key', () => { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', - 'hex', + 'hex' ) assert.throws(() => Address.fromPublicKey(pubKey)) }) diff --git a/test/bytes.spec.ts b/test/bytes.spec.ts index 41323458..b95c3f90 100644 --- a/test/bytes.spec.ts +++ b/test/bytes.spec.ts @@ -1,5 +1,5 @@ -import * as assert from 'assert' -import * as BN from 'bn.js' +import assert from 'assert' +import BN from 'bn.js' import { Address, zeros, @@ -16,7 +16,7 @@ import { toUnsigned, addHexPrefix, toBuffer, - baToJSON, + baToJSON } from '../src' describe('zeros function', function() { @@ -218,9 +218,9 @@ describe('toBuffer', function() { toBuffer({ toArray: function(): any { return [1] - }, + } }), - Buffer.from([1]), + Buffer.from([1]) ) }) it('should fail', function() { diff --git a/test/constants.spec.ts b/test/constants.spec.ts index 79cdf188..c985c2b9 100644 --- a/test/constants.spec.ts +++ b/test/constants.spec.ts @@ -1,4 +1,4 @@ -import * as assert from 'assert' +import assert from 'assert' import { MAX_INTEGER, TWO_POW256, @@ -7,49 +7,49 @@ import { KECCAK256_RLP_ARRAY_S, KECCAK256_RLP_ARRAY, KECCAK256_RLP_S, - KECCAK256_RLP, + KECCAK256_RLP } from '../src' describe('constants', function() { it('should match constants', function() { assert.equal( MAX_INTEGER.toString('hex'), - 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' ) assert.equal( TWO_POW256.toString('hex'), - '10000000000000000000000000000000000000000000000000000000000000000', + '10000000000000000000000000000000000000000000000000000000000000000' ) assert.equal( KECCAK256_NULL_S, - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) assert.equal( KECCAK256_NULL.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) assert.equal( KECCAK256_RLP_ARRAY_S, - '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' ) assert.equal( KECCAK256_RLP_ARRAY.toString('hex'), - '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' ) assert.equal( KECCAK256_RLP_S, - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) assert.equal( KECCAK256_RLP.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) }) }) diff --git a/test/externals.spec.ts b/test/externals.spec.ts index 4292d4fa..b6b25130 100644 --- a/test/externals.spec.ts +++ b/test/externals.spec.ts @@ -1,6 +1,6 @@ -import * as assert from 'assert' +import assert from 'assert' -import * as BN_export from 'bn.js' +import BN_export from 'bn.js' import * as rlp_export from 'rlp' import * as src from '../src' @@ -43,8 +43,8 @@ describe('External rlp export', () => { it('should use a rlp function correctly', () => { const nestedList = [[], [[]], [[], [[]]]] - var encoded = src.rlp.encode(nestedList) - var decoded = src.rlp.decode(encoded) + const encoded = src.rlp.encode(nestedList) + const decoded = src.rlp.decode(encoded) assert.deepEqual(nestedList, decoded) }) @@ -52,24 +52,28 @@ describe('External rlp export', () => { // bad values: wrong encoded a zero const val = Buffer.from( 'f9005f030182520894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3', - 'hex', + 'hex' ) let result try { result = src.rlp.decode(val) - } catch (e) {} + } catch (e) { + // pass + } assert.equal(result, undefined) // bad values: invalid length const a = Buffer.from( 'f86081000182520894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3', - 'hex', + 'hex' ) let res try { result = src.rlp.decode(a) - } catch (e) {} + } catch (e) { + // pass + } assert.equal(res, undefined) }) }) @@ -90,11 +94,11 @@ describe('External ethjsUtil export', () => { 'toAscii', 'getKeys', 'isHexString', - 'toUtf8', + 'toUtf8' ] expected.forEach(prop => { - assert.ok(src.hasOwnProperty(prop)) + assert.ok(prop in src) }) }) diff --git a/test/hash.spec.ts b/test/hash.spec.ts index bb01089d..e9a3fadf 100644 --- a/test/hash.spec.ts +++ b/test/hash.spec.ts @@ -1,4 +1,4 @@ -import * as assert from 'assert' +import assert from 'assert' import { keccak, keccak256, @@ -12,7 +12,7 @@ import { ripemd160FromString, ripemd160FromArray, rlphash, - toBuffer, + toBuffer } from '../src' describe('keccak', function() { diff --git a/test/object.spec.ts b/test/object.spec.ts index e0b5004e..5e700910 100644 --- a/test/object.spec.ts +++ b/test/object.spec.ts @@ -1,4 +1,4 @@ -import * as assert from 'assert' +import assert from 'assert' import { zeros, defineProperties } from '../src' describe('define', function() { @@ -7,33 +7,33 @@ describe('define', function() { name: 'aword', alias: 'blah', word: true, - default: Buffer.allocUnsafe(0), + default: Buffer.allocUnsafe(0) }, { name: 'empty', allowZero: true, length: 20, - default: Buffer.allocUnsafe(0), + default: Buffer.allocUnsafe(0) }, { name: 'cannotBeZero', allowZero: false, - default: Buffer.from([0]), + default: Buffer.from([0]) }, { name: 'value', - default: Buffer.allocUnsafe(0), + default: Buffer.allocUnsafe(0) }, { name: 'r', length: 32, allowLess: true, - default: zeros(32), - }, + default: zeros(32) + } ] it('should trim zeros', function() { - var someOb: any = {} + const someOb: any = {} defineProperties(someOb, fields) // Define Properties someOb.r = '0x00004' @@ -44,7 +44,7 @@ describe('define', function() { }) it("shouldn't allow wrong size for exact size requirements", function() { - var someOb = {} + const someOb = {} defineProperties(someOb, fields) assert.throws(function() { @@ -53,51 +53,51 @@ describe('define', function() { name: 'mustBeExactSize', allowZero: false, length: 20, - default: Buffer.from([1, 2, 3, 4]), - }, + default: Buffer.from([1, 2, 3, 4]) + } ] defineProperties(someOb, tmp) }) }) it('it should accept rlp encoded intial data', function() { - var someOb: any = {} - var data = { + const someOb: any = {} + const data = { aword: '0x01', cannotBeZero: '0x02', value: '0x03', - r: '0x04', + r: '0x04' } - var expected = { + const expected = { aword: '0x01', empty: '0x', cannotBeZero: '0x02', value: '0x03', - r: '0x04', + r: '0x04' } - var expectedArray = ['0x01', '0x', '0x02', '0x03', '0x04'] + const expectedArray = ['0x01', '0x', '0x02', '0x03', '0x04'] defineProperties(someOb, fields, data) assert.deepEqual(someOb.toJSON(true), expected, 'should produce the correctly labeled object') - var someOb2: any = {} - var rlpEncoded = someOb.serialize().toString('hex') + const someOb2: any = {} + const rlpEncoded = someOb.serialize().toString('hex') defineProperties(someOb2, fields, rlpEncoded) assert.equal( someOb2.serialize().toString('hex'), rlpEncoded, - 'the constuctor should accept rlp encoded buffers', + 'the constuctor should accept rlp encoded buffers' ) - var someOb3 = {} + const someOb3 = {} defineProperties(someOb3, fields, expectedArray) assert.deepEqual(someOb.toJSON(), expectedArray, 'should produce the correctly object') }) it('it should not accept invalid values in the constuctor', function() { - var someOb = {} + const someOb = {} assert.throws(function() { defineProperties(someOb, fields, 5) }, 'should throw on nonsensical data') @@ -108,12 +108,12 @@ describe('define', function() { }) it('alias should work ', function() { - var someOb: any = {} - var data = { + const someOb: any = {} + const data = { aword: '0x01', cannotBeZero: '0x02', value: '0x03', - r: '0x04', + r: '0x04' } defineProperties(someOb, fields, data) @@ -124,8 +124,8 @@ describe('define', function() { }) it('alias should work #2', function() { - var someOb: any = {} - var data = { blah: '0x1' } + const someOb: any = {} + const data = { blah: '0x1' } defineProperties(someOb, fields, data) assert.equal(someOb.blah.toString('hex'), '01') diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 041580b7..52b8a0f3 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -1,5 +1,5 @@ -import * as assert from 'assert' -import * as BN from 'bn.js' +import assert from 'assert' +import BN from 'bn.js' import { ecsign, ecrecover, @@ -7,16 +7,16 @@ import { hashPersonalMessage, isValidSignature, fromRpcSig, - toRpcSig, + toRpcSig } from '../src' const echash = Buffer.from( '82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28', - 'hex', + 'hex' ) const ecprivkey = Buffer.from( '3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1', - 'hex', + 'hex' ) const chainId = 3 // ropsten @@ -25,11 +25,11 @@ describe('ecsign', function() { const sig = ecsign(echash, ecprivkey) assert.deepEqual( sig.r, - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex'), + Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') ) assert.deepEqual( sig.s, - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex'), + Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') ) assert.equal(sig.v, 27) }) @@ -38,11 +38,11 @@ describe('ecsign', function() { const sig = ecsign(echash, ecprivkey, chainId) assert.deepEqual( sig.r, - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex'), + Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') ) assert.deepEqual( sig.s, - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex'), + Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') ) assert.equal(sig.v, 41) }) @@ -52,11 +52,11 @@ describe('ecsign', function() { const sig = ecsign(echash, ecprivkey, chainId) assert.deepEqual( sig.r, - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex'), + Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') ) assert.deepEqual( sig.s, - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex'), + Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') ) assert.equal(sig.v, chainId * 2 + 35) }) @@ -113,7 +113,7 @@ describe('hashPersonalMessage', function() { const h = hashPersonalMessage(Buffer.from('Hello world')) assert.deepEqual( h, - Buffer.from('8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede', 'hex'), + Buffer.from('8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede', 'hex') ) }) it('should throw if input is not a buffer', function() { @@ -149,7 +149,7 @@ describe('isValidSignature', function() { it('should fail when on homestead and s > secp256k1n/2', function() { const SECP256K1_N_DIV_2 = new BN( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', - 16, + 16 ) const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') @@ -161,7 +161,7 @@ describe('isValidSignature', function() { it('should not fail when not on homestead but s > secp256k1n/2', function() { const SECP256K1_N_DIV_2 = new BN( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', - 16, + 16 ) const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') @@ -199,27 +199,27 @@ describe('message sig', function() { it('should return hex strings that the RPC can use', function() { assert.equal( toRpcSig(27, r, s), - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca661b', + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca661b' ) assert.deepEqual( fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca661b', + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca661b' ), { v: 27, r: r, - s: s, - }, + s: s + } ) assert.deepEqual( fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca6600', + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca6600' ), { v: 27, r: r, - s: s, - }, + s: s + } ) }) @@ -228,17 +228,17 @@ describe('message sig', function() { const v = chainId * 2 + 35 assert.equal( toRpcSig(v, r, s, chainId), - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f', + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f' ) assert.deepEqual( fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f', + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f' ), { v, r: r, - s: s, - }, + s: s + } ) }) @@ -248,7 +248,7 @@ describe('message sig', function() { }) assert.throws(function() { fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca', + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca' ) }) }) @@ -256,7 +256,7 @@ describe('message sig', function() { it('pad short r and s values', function() { assert.equal( toRpcSig(27, r.slice(20), s.slice(20)), - '0x00000000000000000000000000000000000000004a1579cf389ef88b20a1abe90000000000000000000000000000000000000000326fa689f228040429e3ca661b', + '0x00000000000000000000000000000000000000004a1579cf389ef88b20a1abe90000000000000000000000000000000000000000326fa689f228040429e3ca661b' ) }) diff --git a/tsconfig.json b/tsconfig.json index ddbd8a97..ed3b4809 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,4 @@ { - "extends": "@ethereumjs/config-tsc", + "extends": "@ethereumjs/config-typescript", "include": ["src/**/*.ts", "test/**/*.ts"] } diff --git a/tsconfig.prod.json b/tsconfig.prod.json index 37770820..31187a41 100644 --- a/tsconfig.prod.json +++ b/tsconfig.prod.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "outDir": "./dist" + "outDir": "./dist", }, "include": ["src/**/*.ts"] } diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 2ba21c41..00000000 --- a/tslint.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@ethereumjs/config-tslint" -} From 9cc8050e25f7b0d984e7b32c0fe62e033735d3f0 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 4 Feb 2021 14:50:27 -0800 Subject: [PATCH 30/46] fix karma: * add tsconfig to silence errors * remove custom types exclude (seems to be working without now) * lint --- karma.conf.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index f166a9e5..a64ca12c 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,16 +1,16 @@ module.exports = function(config) { config.set({ frameworks: ['mocha', 'karma-typescript'], - exclude: ["src/@types/**"], // ref: https://github.com/monounity/karma-typescript/issues/254 files: ['src/**/*.ts', 'test/**/*.ts'], preprocessors: { - '**/*.ts': ['karma-typescript'], + '**/*.ts': ['karma-typescript'] }, plugins: ['karma-mocha', 'karma-typescript', 'karma-chrome-launcher', 'karma-firefox-launcher'], karmaTypescriptConfig: { bundlerOptions: { - entrypoints: /\.spec\.ts$/, + entrypoints: /\.spec\.ts$/ }, + tsconfig: './tsconfig.json' }, colors: true, reporters: ['progress', 'karma-typescript'], @@ -19,6 +19,6 @@ module.exports = function(config) { concurrency: Infinity, // Fail after timeout browserDisconnectTimeout: 100000, - browserNoActivityTimeout: 100000, + browserNoActivityTimeout: 100000 }) } From 919ff364d928407bfaf27ab5b69b88e8888fef30 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 4 Feb 2021 15:03:58 -0800 Subject: [PATCH 31/46] fix codecov if clause --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a14057a8..1f5ce9e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,6 @@ jobs: - run: npm run test - uses: codecov/codecov-action@v1 - if: ${{ matrix.node-version == 12 }} + if: matrix.node-version == '12.x' with: file: ./coverage/lcov.info From 0f8741660ac99cbc61b9d0d4fe42d0b715338bdb Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 27 Feb 2021 14:47:16 +0100 Subject: [PATCH 32/46] signature: add support for very high recovery IDs --- src/signature.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/signature.ts b/src/signature.ts index c0235af3..b6e67e6f 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -30,12 +30,15 @@ export const ecsign = function( return ret } -function calculateSigRecovery(v: number, chainId?: number): number { - return chainId ? v - (2 * chainId + 35) : v - 27 +function calculateSigRecovery(v: number | BN | Buffer, chainId?: number | BN | Buffer): BN { + const vBN = new BN(toBuffer(v)) + const chainIdBN = chainId ? new BN(toBuffer(chainId)) : undefined + return chainIdBN ? vBN.sub(chainIdBN.muln(2).addn(35)) : vBN.subn(27) } -function isValidSigRecovery(recovery: number): boolean { - return recovery === 0 || recovery === 1 +function isValidSigRecovery(recovery: number | BN): boolean { + const rec = new BN(recovery) + return rec.eqn(0) || rec.eqn(1) } /** @@ -44,17 +47,17 @@ function isValidSigRecovery(recovery: number): boolean { */ export const ecrecover = function( msgHash: Buffer, - v: number, + v: number | BN | Buffer, r: Buffer, s: Buffer, - chainId?: number + chainId?: number | BN | Buffer ): Buffer { const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64) const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } - const senderPubKey = ecdsaRecover(signature, recovery, msgHash) + const senderPubKey = ecdsaRecover(signature, recovery.toNumber(), msgHash) return Buffer.from(publicKeyConvert(senderPubKey, false).slice(1)) } From db8d73673a3365ba13a55c672bb55b2202281a9a Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 27 Feb 2021 17:05:29 +0100 Subject: [PATCH 33/46] signature: add test of failing tx on YoloV3 --- test/signature.spec.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 52b8a0f3..0fba18db 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -106,6 +106,43 @@ describe('ecrecover', function() { ecrecover(echash, 27, s, r) }) }) + it('should return the right sender when using very high chain id / v values', function() { + // This data is from a transaction of the YoloV3 network, block 77, txhash c6121a23ca17b8ff70d4706c7d134920c1da43c8329444c96b4c63a55af1c760 + /* + { + nonce: '0x8', + gasPrice: '0x3b9aca00', + gasLimit: '0x1a965', + to: undefined, + value: '0x0', + data: '0x608060405234801561001057600080fd5b50610101806100206000396000f3fe608060405260043610601f5760003560e01c8063776d1a0114603b576020565b5b6000543660008037600080366000845af43d6000803e3d6000f35b348015604657600080fd5b50608660048036036020811015605b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506088565b005b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea26469706673582212206d3160e3f009c6ebac579877e529c0a1ca8313678f08fe311659d440067d26ea64736f6c63430007040033', + v: '0xf2ded8deec6714', + r: '0xec212841e0b7aaffc3b3e33a08adf32fa07159e856ef23db85175a4f6d71dc0f', + s: '0x4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5' + } + */ + const senderPubKey = Buffer.from( + '78988201fbceed086cfca7b64e382d08d0bd776898731443d2907c097745b7324c54f522087f5964412cddba019f192de0fd57a0ffa63f098c2b200e53594b15', + 'hex' + ) + const msgHash = Buffer.from( + '8ae8cb685a7a9f29494b07b287c3f6a103b73fa178419d10d1184861a40f6afe', + 'hex' + ) + const v = Buffer.from('f2ded8deec6714', 'hex') + const r = Buffer.from('ec212841e0b7aaffc3b3e33a08adf32fa07159e856ef23db85175a4f6d71dc0f', 'hex') + const s = Buffer.from('4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5', 'hex') + const chainID = Buffer.from('796f6c6f763378', 'hex') + const sender = ecrecover(msgHash, v, r, s, chainID) + assert.ok(sender.equals(senderPubKey), 'sender pubkey correct') + const chainIDNumber = parseInt(chainID.toString('hex'), 16) + const vNumber = parseInt(v.toString('hex'), 16) + assert.throws(() => { + // If we would use numbers for the `v` and `chainId` parameters, then it should throw. + // (The numbers are too high to perform arithmetic on) + ecrecover(msgHash, vNumber, r, s, chainIDNumber) + }) + }) }) describe('hashPersonalMessage', function() { From 1c9d742fd75ab6b0e73d418fe9b4e2270ac26f80 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Tue, 2 Mar 2021 09:55:23 +0100 Subject: [PATCH 34/46] Added Buffer to BNLike type, PrefixedHexString in types, use BNLike for ecrecover v and chainId input, added type tests --- src/bytes.ts | 4 ++-- src/signature.ts | 7 ++++--- src/types.ts | 4 ++-- test/signature.spec.ts | 25 +++++++++++++++++++------ 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/bytes.ts b/src/bytes.ts index 7594771f..a9d13a42 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -1,6 +1,6 @@ import BN from 'bn.js' import { intToBuffer, stripHexPrefix, padToEven, isHexString, isHexPrefixed } from 'ethjs-util' -import { TransformableToArray, TransformableToBuffer } from './types' +import { PrefixedHexString, TransformableToArray, TransformableToBuffer } from './types' import { assertIsBuffer, assertIsArray, assertIsHexString } from './helpers' /** @@ -112,7 +112,7 @@ export const unpadHexString = function(a: string): string { */ export const toBuffer = function( v: - | string + | PrefixedHexString | number | BN | Buffer diff --git a/src/signature.ts b/src/signature.ts index b6e67e6f..14743912 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -3,6 +3,7 @@ import BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes' import { keccak } from './hash' import { assertIsBuffer } from './helpers' +import { BNLike } from './types' export interface ECDSASignature { v: number @@ -30,7 +31,7 @@ export const ecsign = function( return ret } -function calculateSigRecovery(v: number | BN | Buffer, chainId?: number | BN | Buffer): BN { +function calculateSigRecovery(v: BNLike, chainId?: BNLike): BN { const vBN = new BN(toBuffer(v)) const chainIdBN = chainId ? new BN(toBuffer(chainId)) : undefined return chainIdBN ? vBN.sub(chainIdBN.muln(2).addn(35)) : vBN.subn(27) @@ -47,10 +48,10 @@ function isValidSigRecovery(recovery: number | BN): boolean { */ export const ecrecover = function( msgHash: Buffer, - v: number | BN | Buffer, + v: BNLike, r: Buffer, s: Buffer, - chainId?: number | BN | Buffer + chainId?: BNLike ): Buffer { const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64) const recovery = calculateSigRecovery(v, chainId) diff --git a/src/types.ts b/src/types.ts index 7b3d7725..3afa856a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,7 +5,7 @@ import { unpadBuffer } from './bytes' /* * A type that represents a BNLike input that can be converted to a BN. */ -export type BNLike = BN | string | number +export type BNLike = BN | PrefixedHexString | number | Buffer /* * A type that represents a BufferLike input that can be converted to a Buffer. @@ -28,7 +28,7 @@ export type PrefixedHexString = string * A type that represents an Address-like value. * To convert to address, use `new Address(toBuffer(value))` */ -export type AddressLike = Address | Buffer | string +export type AddressLike = Address | Buffer | PrefixedHexString /* * A type that represents an object that has a `toArray()` method. diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 0fba18db..30c7542c 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -129,14 +129,27 @@ describe('ecrecover', function() { '8ae8cb685a7a9f29494b07b287c3f6a103b73fa178419d10d1184861a40f6afe', 'hex' ) - const v = Buffer.from('f2ded8deec6714', 'hex') + const r = Buffer.from('ec212841e0b7aaffc3b3e33a08adf32fa07159e856ef23db85175a4f6d71dc0f', 'hex') const s = Buffer.from('4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5', 'hex') - const chainID = Buffer.from('796f6c6f763378', 'hex') - const sender = ecrecover(msgHash, v, r, s, chainID) - assert.ok(sender.equals(senderPubKey), 'sender pubkey correct') - const chainIDNumber = parseInt(chainID.toString('hex'), 16) - const vNumber = parseInt(v.toString('hex'), 16) + + const vBuffer = Buffer.from('f2ded8deec6714', 'hex') + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + let sender = ecrecover(msgHash, vBuffer, r, s, chainIDBuffer) + assert.ok(sender.equals(senderPubKey), 'sender pubkey correct (Buffer)') + + const vBN = new BN(vBuffer) + const chainIDBN = new BN(chainIDBuffer) + sender = ecrecover(msgHash, vBN, r, s, chainIDBN) + assert.ok(sender.equals(senderPubKey), 'sender pubkey correct (BN)') + + const vHexString = '0xf2ded8deec6714' + const chainIDHexString = '0x796f6c6f763378' + sender = ecrecover(msgHash, vHexString, r, s, chainIDHexString) + assert.ok(sender.equals(senderPubKey), 'sender pubkey correct (HexString)') + + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + const vNumber = parseInt(vBuffer.toString('hex'), 16) assert.throws(() => { // If we would use numbers for the `v` and `chainId` parameters, then it should throw. // (The numbers are too high to perform arithmetic on) From 5c80ce87fd21fba61b68e4ab3b80a80ecaf5e53c Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Tue, 2 Mar 2021 12:04:23 +0100 Subject: [PATCH 35/46] Added BNLike chainId input and return types for ecsign, added tests --- src/signature.ts | 85 +++++++++++++++++++++++++++++++++++++----- test/signature.spec.ts | 39 ++++++++++++++----- 2 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/signature.ts b/src/signature.ts index 14743912..50dfea21 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,9 +1,10 @@ -const { ecdsaSign, ecdsaRecover, publicKeyConvert } = require('ethereum-cryptography/secp256k1') +import { ecdsaSign, ecdsaRecover, publicKeyConvert } from 'ethereum-cryptography/secp256k1' import BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes' import { keccak } from './hash' import { assertIsBuffer } from './helpers' -import { BNLike } from './types' +import { BNLike, PrefixedHexString } from './types' +import { isHexString } from '.' export interface ECDSASignature { v: number @@ -11,21 +12,87 @@ export interface ECDSASignature { s: Buffer } +export interface ECDSASignatureBN { + v: BN + r: Buffer + s: Buffer +} + +export interface ECDSASignatureBuffer { + v: Buffer + r: Buffer + s: Buffer +} + +export interface ECDSASignatureHexString { + v: PrefixedHexString + r: Buffer + s: Buffer +} + /** * Returns the ECDSA signature of a message hash. */ -export const ecsign = function( +export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId?: number): ECDSASignature +export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: BN): ECDSASignatureBN +export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: Buffer): ECDSASignatureBuffer +export function ecsign( msgHash: Buffer, privateKey: Buffer, - chainId?: number -): ECDSASignature { + chainId: PrefixedHexString +): ECDSASignatureHexString +export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: any): any { const sig = ecdsaSign(msgHash, privateKey) const recovery: number = sig.recid - const ret = { - r: Buffer.from(sig.signature.slice(0, 32)), - s: Buffer.from(sig.signature.slice(32, 64)), - v: chainId ? recovery + (chainId * 2 + 35) : recovery + 27 + let ret + if (typeof chainId === 'number') { + return { + r: Buffer.from(sig.signature.slice(0, 32)), + s: Buffer.from(sig.signature.slice(32, 64)), + v: recovery + (chainId * 2 + 35) + } + } else if (BN.isBN(chainId)) { + ret = { + r: Buffer.from(sig.signature.slice(0, 32)), + s: Buffer.from(sig.signature.slice(32, 64)), + v: (chainId as BN) + .muln(2) + .addn(35) + .addn(recovery) + } + } else if (Buffer.isBuffer(chainId)) { + ret = { + r: Buffer.from(sig.signature.slice(0, 32)), + s: Buffer.from(sig.signature.slice(32, 64)), + v: toBuffer( + new BN(chainId) + .muln(2) + .addn(35) + .addn(recovery) + ) + } + } else if (typeof chainId === 'string') { + if (!isHexString(chainId)) { + throw new Error(`A chainId string must be provided with a 0x-prefix, given: ${chainId}`) + } + ret = { + r: Buffer.from(sig.signature.slice(0, 32)), + s: Buffer.from(sig.signature.slice(32, 64)), + v: + '0x' + + new BN(toBuffer(chainId)) + .muln(2) + .addn(35) + .addn(recovery) + .toString('hex') + } + } else { + ret = { + r: Buffer.from(sig.signature.slice(0, 32)), + s: Buffer.from(sig.signature.slice(32, 64)), + v: recovery + 27 + } } return ret diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 30c7542c..6033880b 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -48,17 +48,38 @@ describe('ecsign', function() { }) it('should produce a signature for chainId=150', function() { - const chainId = 150 - const sig = ecsign(echash, ecprivkey, chainId) - assert.deepEqual( - sig.r, - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') + const expectedSigR = Buffer.from( + '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', + 'hex' ) - assert.deepEqual( - sig.s, - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const expectedSigS = Buffer.from( + '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', + 'hex' ) - assert.equal(sig.v, chainId * 2 + 35) + + const sig = ecsign(echash, ecprivkey, 150) + assert.deepEqual(sig.r, expectedSigR) + assert.deepEqual(sig.s, expectedSigS) + assert.equal(sig.v, 150 * 2 + 35) + + const sigBN = ecsign(echash, ecprivkey, new BN(150)) + assert.deepEqual(sigBN.r, expectedSigR) + assert.deepEqual(sigBN.s, expectedSigS) + assert.deepEqual(sigBN.v, new BN(150).muln(2).addn(35)) + + const sigBuffer = ecsign(echash, ecprivkey, Buffer.from([150])) + assert.deepEqual(sigBuffer.r, expectedSigR) + assert.deepEqual(sigBuffer.s, expectedSigS) + assert.deepEqual(sigBuffer.v, Buffer.from('014f', 'hex')) + + const sigHexString = ecsign(echash, ecprivkey, '0x96') + assert.deepEqual(sigHexString.r, expectedSigR) + assert.deepEqual(sigHexString.s, expectedSigS) + assert.equal(sigHexString.v, '0x14f') + + assert.throws(function() { + ecsign(echash, ecprivkey, '96') + }) }) }) From 24279bf839eb697a8f8e2b643662f6d2986c266b Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Tue, 2 Mar 2021 13:22:40 +0100 Subject: [PATCH 36/46] Limited ecsign v return value to Buffer on extended input types, additional input value checks --- src/signature.ts | 92 +++++++++++++++++------------------------- test/signature.spec.ts | 57 +++++++++++++++++++++----- 2 files changed, 84 insertions(+), 65 deletions(-) diff --git a/src/signature.ts b/src/signature.ts index 50dfea21..2c7b14f4 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -3,7 +3,7 @@ import BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes' import { keccak } from './hash' import { assertIsBuffer } from './helpers' -import { BNLike, PrefixedHexString } from './types' +import { BNLike } from './types' import { isHexString } from '.' export interface ECDSASignature { @@ -12,86 +12,53 @@ export interface ECDSASignature { s: Buffer } -export interface ECDSASignatureBN { - v: BN - r: Buffer - s: Buffer -} - export interface ECDSASignatureBuffer { v: Buffer r: Buffer s: Buffer } -export interface ECDSASignatureHexString { - v: PrefixedHexString - r: Buffer - s: Buffer -} - /** * Returns the ECDSA signature of a message hash. */ export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId?: number): ECDSASignature -export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: BN): ECDSASignatureBN -export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: Buffer): ECDSASignatureBuffer export function ecsign( msgHash: Buffer, privateKey: Buffer, - chainId: PrefixedHexString -): ECDSASignatureHexString + chainId: BN | string | Buffer +): ECDSASignatureBuffer export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: any): any { const sig = ecdsaSign(msgHash, privateKey) const recovery: number = sig.recid let ret - if (typeof chainId === 'number') { - return { - r: Buffer.from(sig.signature.slice(0, 32)), - s: Buffer.from(sig.signature.slice(32, 64)), - v: recovery + (chainId * 2 + 35) - } - } else if (BN.isBN(chainId)) { - ret = { - r: Buffer.from(sig.signature.slice(0, 32)), - s: Buffer.from(sig.signature.slice(32, 64)), - v: (chainId as BN) - .muln(2) - .addn(35) - .addn(recovery) - } - } else if (Buffer.isBuffer(chainId)) { - ret = { - r: Buffer.from(sig.signature.slice(0, 32)), - s: Buffer.from(sig.signature.slice(32, 64)), - v: toBuffer( - new BN(chainId) - .muln(2) - .addn(35) - .addn(recovery) + const r = Buffer.from(sig.signature.slice(0, 32)) + const s = Buffer.from(sig.signature.slice(32, 64)) + if (!chainId || typeof chainId === 'number') { + if (chainId && !Number.isSafeInteger(chainId)) { + throw new Error( + 'The provided chainId is greater than MAX_SAFE_INTEGER (please use an alternative input type)' ) } - } else if (typeof chainId === 'string') { - if (!isHexString(chainId)) { + return { + r, + s, + v: chainId ? recovery + (chainId * 2 + 35) : recovery + 27 + } + } else { + // BN, string, Buffer + if (typeof chainId === 'string' && !isHexString(chainId)) { throw new Error(`A chainId string must be provided with a 0x-prefix, given: ${chainId}`) } ret = { - r: Buffer.from(sig.signature.slice(0, 32)), - s: Buffer.from(sig.signature.slice(32, 64)), - v: - '0x' + + r, + s, + v: toBuffer( new BN(toBuffer(chainId)) .muln(2) .addn(35) .addn(recovery) - .toString('hex') - } - } else { - ret = { - r: Buffer.from(sig.signature.slice(0, 32)), - s: Buffer.from(sig.signature.slice(32, 64)), - v: recovery + 27 + ) } } @@ -120,6 +87,23 @@ export const ecrecover = function( s: Buffer, chainId?: BNLike ): Buffer { + if (typeof v === 'string' && !isHexString(v)) { + throw new Error(`A v value string must be provided with a 0x-prefix, given: ${v}`) + } + if (typeof chainId === 'string' && !isHexString(chainId)) { + throw new Error(`A chainId string must be provided with a 0x-prefix, given: ${chainId}`) + } + if (typeof v === 'number' && !Number.isSafeInteger(v)) { + throw new Error( + 'The provided v is greater than MAX_SAFE_INTEGER (please use an alternative input type)' + ) + } + if (typeof chainId === 'number' && !Number.isSafeInteger(chainId)) { + throw new Error( + 'The provided chainId is greater than MAX_SAFE_INTEGER (please use an alternative input type)' + ) + } + const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64) const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 6033880b..6a897fed 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -56,26 +56,23 @@ describe('ecsign', function() { '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex' ) + const expectedSigV = Buffer.from('014f', 'hex') const sig = ecsign(echash, ecprivkey, 150) assert.deepEqual(sig.r, expectedSigR) assert.deepEqual(sig.s, expectedSigS) assert.equal(sig.v, 150 * 2 + 35) - const sigBN = ecsign(echash, ecprivkey, new BN(150)) - assert.deepEqual(sigBN.r, expectedSigR) - assert.deepEqual(sigBN.s, expectedSigS) - assert.deepEqual(sigBN.v, new BN(150).muln(2).addn(35)) - - const sigBuffer = ecsign(echash, ecprivkey, Buffer.from([150])) + let sigBuffer = ecsign(echash, ecprivkey, new BN(150)) assert.deepEqual(sigBuffer.r, expectedSigR) assert.deepEqual(sigBuffer.s, expectedSigS) - assert.deepEqual(sigBuffer.v, Buffer.from('014f', 'hex')) + assert.deepEqual(sigBuffer.v, expectedSigV) + + sigBuffer = ecsign(echash, ecprivkey, Buffer.from([150])) + assert.deepEqual(sigBuffer.v, expectedSigV) - const sigHexString = ecsign(echash, ecprivkey, '0x96') - assert.deepEqual(sigHexString.r, expectedSigR) - assert.deepEqual(sigHexString.s, expectedSigS) - assert.equal(sigHexString.v, '0x14f') + sigBuffer = ecsign(echash, ecprivkey, '0x96') + assert.deepEqual(sigBuffer.v, expectedSigV) assert.throws(function() { ecsign(echash, ecprivkey, '96') @@ -83,6 +80,37 @@ describe('ecsign', function() { }) }) +it('should produce a signature for a high number chainId greater than MAX_SAFE_INTEGER', function() { + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + const expectedSigR = Buffer.from( + '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', + 'hex' + ) + const expectedSigS = Buffer.from( + '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', + 'hex' + ) + const expectedSigV = Buffer.from('f2ded8deec6713', 'hex') + + let sigBuffer = ecsign(echash, ecprivkey, new BN(chainIDBuffer)) + assert.deepEqual(sigBuffer.r, expectedSigR) + assert.deepEqual(sigBuffer.s, expectedSigS) + assert.deepEqual(sigBuffer.v, expectedSigV) + + sigBuffer = ecsign(echash, ecprivkey, chainIDBuffer) + assert.deepEqual(sigBuffer.v, expectedSigV) + + sigBuffer = ecsign(echash, ecprivkey, '0x' + chainIDBuffer.toString('hex')) + assert.deepEqual(sigBuffer.v, expectedSigV) + + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + assert.throws(() => { + // If we would use a number for the `chainId` parameter then it should throw. + // (The numbers are too high to perform arithmetic on) + ecsign(echash, ecprivkey, chainIDNumber) + }) +}) + describe('ecrecover', function() { it('should recover a public key', function() { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') @@ -169,6 +197,13 @@ describe('ecrecover', function() { sender = ecrecover(msgHash, vHexString, r, s, chainIDHexString) assert.ok(sender.equals(senderPubKey), 'sender pubkey correct (HexString)') + assert.throws(function() { + ecrecover(msgHash, 'f2ded8deec6714', r, s, chainIDHexString) + }) + assert.throws(function() { + ecrecover(msgHash, vHexString, r, s, '796f6c6f763378') + }) + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) const vNumber = parseInt(vBuffer.toString('hex'), 16) assert.throws(() => { From abe0aff8230f80aef73d9fec8c1617cee7f228b1 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Tue, 2 Mar 2021 18:36:56 +0100 Subject: [PATCH 37/46] Added BNLike input type for chain IDs in toChecksumAddress() and isValidChecksumAddress(), expanded tests --- src/account.ts | 20 ++++++++++++++++---- test/account.spec.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/account.ts b/src/account.ts index e6895064..90236ca0 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,7 +1,7 @@ import assert from 'assert' import BN from 'bn.js' import * as rlp from 'rlp' -import { stripHexPrefix } from 'ethjs-util' +import { isHexString, stripHexPrefix } from 'ethjs-util' import { KECCAK256_RLP, KECCAK256_NULL } from './constants' import { zeros, bufferToHex, toBuffer } from './bytes' import { keccak, keccak256, keccakFromString, rlphash } from './hash' @@ -137,11 +137,23 @@ export const isValidAddress = function(hexAddress: 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(hexAddress: string, eip1191ChainId?: number): string { +export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: BNLike): string { assertIsHexString(hexAddress) + if (typeof eip1191ChainId === 'string' && !isHexString(eip1191ChainId)) { + throw new Error(`A chainId string must be provided with a 0x-prefix, given: ${eip1191ChainId}`) + } const address = stripHexPrefix(hexAddress).toLowerCase() - const prefix = eip1191ChainId !== undefined ? eip1191ChainId.toString() + '0x' : '' + let prefix = '' + if (eip1191ChainId) { + // Performance optimization + if (typeof eip1191ChainId === 'number' && Number.isSafeInteger(eip1191ChainId)) { + prefix = eip1191ChainId.toString() + } else { + prefix = new BN(toBuffer(eip1191ChainId)).toString() + } + prefix += '0x' + } const hash = keccakFromString(prefix + address).toString('hex') let ret = '0x' @@ -164,7 +176,7 @@ export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: n */ export const isValidChecksumAddress = function( hexAddress: string, - eip1191ChainId?: number + eip1191ChainId?: BNLike ): boolean { return isValidAddress(hexAddress) && toChecksumAddress(hexAddress, eip1191ChainId) === hexAddress } diff --git a/test/account.spec.ts b/test/account.spec.ts index ce9e22a0..a5e81aa0 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -602,9 +602,27 @@ describe('.toChecksumAddress()', function() { for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { for (const addr of addresses) { assert.equal(toChecksumAddress(addr.toLowerCase(), Number(chainId)), addr) + assert.equal(toChecksumAddress(addr.toLowerCase(), Buffer.from([chainId])), addr) + assert.equal(toChecksumAddress(addr.toLowerCase(), new BN(chainId)), addr) + assert.equal( + toChecksumAddress(addr.toLowerCase(), '0x' + Buffer.from([chainId]).toString('hex')), + addr + ) } } }) + it('Should encode large chain ids greater than MAX_INTEGER correctly', function() { + const addr = '0x88021160C5C792225E4E5452585947470010289D' + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + assert.equal(toChecksumAddress(addr.toLowerCase(), chainIDBuffer), addr) + assert.equal(toChecksumAddress(addr.toLowerCase(), new BN(chainIDBuffer)), addr) + assert.equal( + toChecksumAddress(addr.toLowerCase(), '0x' + chainIDBuffer.toString('hex')), + addr + ) + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + assert.equal(toChecksumAddress(addr.toLowerCase(), chainIDNumber), addr) + }) }) describe('input format', function() { @@ -613,6 +631,11 @@ describe('.toChecksumAddress()', function() { toChecksumAddress('52908400098527886E0F7030069857D2E4169EE7'.toLowerCase()) }) }) + it('Should throw when the chainId is not hex-prefixed', function() { + assert.throws(function() { + toChecksumAddress('0xde709f2102306220921060314715629080e2fb77', '1234') + }) + }) }) }) @@ -633,6 +656,12 @@ describe('.isValidChecksumAddress()', function() { for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { for (const addr of addresses) { assert.equal(isValidChecksumAddress(addr, Number(chainId)), true) + assert.equal(isValidChecksumAddress(addr, Buffer.from([chainId])), true) + assert.equal(isValidChecksumAddress(addr, new BN(chainId)), true) + assert.equal( + isValidChecksumAddress(addr, '0x' + Buffer.from([chainId]).toString('hex')), + true + ) } } }) From b3ba3eec78c30cee2c8ea81e257b19d39d30739c Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Tue, 2 Mar 2021 19:14:56 +0100 Subject: [PATCH 38/46] Added BNLike input type for v and chain IDs in toRpcSig() and isValidSignature(), expanded tests --- src/signature.ts | 34 +++++++----- test/signature.spec.ts | 118 +++++++++++++++++++++++++++++------------ 2 files changed, 103 insertions(+), 49 deletions(-) diff --git a/src/signature.ts b/src/signature.ts index 2c7b14f4..96dfa3d6 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -76,17 +76,7 @@ function isValidSigRecovery(recovery: number | BN): boolean { return rec.eqn(0) || rec.eqn(1) } -/** - * ECDSA public key recovery from signature. - * @returns Recovered public key - */ -export const ecrecover = function( - msgHash: Buffer, - v: BNLike, - r: Buffer, - s: Buffer, - chainId?: BNLike -): Buffer { +function vAndChainIdTypeChecks(v: BNLike, chainId?: BNLike) { if (typeof v === 'string' && !isHexString(v)) { throw new Error(`A v value string must be provided with a 0x-prefix, given: ${v}`) } @@ -103,6 +93,20 @@ export const ecrecover = function( 'The provided chainId is greater than MAX_SAFE_INTEGER (please use an alternative input type)' ) } +} + +/** + * ECDSA public key recovery from signature. + * @returns Recovered public key + */ +export const ecrecover = function( + msgHash: Buffer, + v: BNLike, + r: Buffer, + s: Buffer, + chainId?: BNLike +): Buffer { + vAndChainIdTypeChecks(v, chainId) const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64) const recovery = calculateSigRecovery(v, chainId) @@ -117,7 +121,8 @@ export const ecrecover = function( * Convert signature parameters into the format of `eth_sign` RPC method. * @returns Signature */ -export const toRpcSig = function(v: number, r: Buffer, s: Buffer, chainId?: number): string { +export const toRpcSig = function(v: BNLike, r: Buffer, s: Buffer, chainId?: BNLike): string { + vAndChainIdTypeChecks(v, chainId) const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') @@ -156,12 +161,13 @@ export const fromRpcSig = function(sig: string): ECDSASignature { * @param homesteadOrLater Indicates whether this is being used on either the homestead hardfork or a later one */ export const isValidSignature = function( - v: number, + v: BNLike, r: Buffer, s: Buffer, homesteadOrLater: boolean = true, - chainId?: number + chainId?: BNLike ): boolean { + vAndChainIdTypeChecks(v, chainId) const SECP256K1_N_DIV_2 = new BN( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16 diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 6a897fed..972856f5 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -7,7 +7,8 @@ import { hashPersonalMessage, isValidSignature, fromRpcSig, - toRpcSig + toRpcSig, + intToBuffer } from '../src' const echash = Buffer.from( @@ -294,6 +295,45 @@ describe('isValidSignature', function() { const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') const v = chainId * 2 + 35 assert.equal(isValidSignature(v, r, s, false, chainId), true) + assert.equal(isValidSignature(intToBuffer(v), r, s, false, intToBuffer(chainId)), true) + assert.equal(isValidSignature(new BN(v), r, s, false, new BN(chainId)), true) + assert.equal( + isValidSignature( + '0x' + intToBuffer(v).toString('hex'), + r, + s, + false, + '0x' + intToBuffer(chainId).toString('hex') + ), + true + ) + }) + it('should work otherwise(chainId larger than MAX_INTEGER)', function() { + const r = Buffer.from('ec212841e0b7aaffc3b3e33a08adf32fa07159e856ef23db85175a4f6d71dc0f', 'hex') + const s = Buffer.from('4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5', 'hex') + + const vBuffer = Buffer.from('f2ded8deec6714', 'hex') + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + assert.equal(isValidSignature(vBuffer, r, s, false, chainIDBuffer), true) + assert.equal(isValidSignature(new BN(vBuffer), r, s, false, new BN(chainIDBuffer)), true) + assert.equal( + isValidSignature( + '0x' + vBuffer.toString('hex'), + r, + s, + false, + '0x' + chainIDBuffer.toString('hex') + ), + true + ) + + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + const vNumber = parseInt(vBuffer.toString('hex'), 16) + assert.throws(() => { + // If we would use numbers for the `v` and `chainId` parameters, then it should throw. + // (The numbers are too high to perform arithmetic on) + isValidSignature(vNumber, r, s, false, chainIDNumber) + }) }) // FIXME: add homestead test }) @@ -303,49 +343,57 @@ describe('message sig', function() { const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') it('should return hex strings that the RPC can use', function() { - assert.equal( - toRpcSig(27, r, s), + const sig = '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca661b' - ) - assert.deepEqual( - fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca661b' - ), - { - v: 27, - r: r, - s: s - } - ) - assert.deepEqual( - fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca6600' - ), - { - v: 27, - r: r, - s: s - } - ) + assert.equal(toRpcSig(27, r, s), sig) + assert.deepEqual(fromRpcSig(sig), { + v: 27, + r: r, + s: s + }) }) it('should return hex strings that the RPC can use (chainId=150)', function() { + const sig = + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f' const chainId = 150 const v = chainId * 2 + 35 + assert.equal(toRpcSig(v, r, s, chainId), sig) + assert.equal(toRpcSig(intToBuffer(v), r, s, intToBuffer(chainId)), sig) + assert.equal(toRpcSig(new BN(v), r, s, new BN(chainId)), sig) assert.equal( - toRpcSig(v, r, s, chainId), - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f' - ) - assert.deepEqual( - fromRpcSig( - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f' + toRpcSig( + '0x' + intToBuffer(v).toString('hex'), + r, + s, + '0x' + intToBuffer(chainId).toString('hex') ), - { - v, - r: r, - s: s - } + sig ) + assert.deepEqual(fromRpcSig(sig), { + v, + r: r, + s: s + }) + }) + + it('should return hex strings that the RPC can use (chainId larger than MAX_SAFE_INTEGER)', function() { + const sig = + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66f2ded8deec6714' + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + const vBuffer = Buffer.from('f2ded8deec6714', 'hex') + assert.equal(toRpcSig(vBuffer, r, s, chainIDBuffer), sig) + assert.equal(toRpcSig(new BN(vBuffer), r, s, new BN(chainIDBuffer)), sig) + assert.equal( + toRpcSig('0x' + vBuffer.toString('hex'), r, s, '0x' + chainIDBuffer.toString('hex')), + sig + ) + + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + const vNumber = parseInt(vBuffer.toString('hex'), 16) + assert.throws(function() { + toRpcSig(vNumber, r, s, chainIDNumber) + }) }) it('should throw on shorter length', function() { From 243fd4776d789c7b8c37dc22d0249465d1a0766c Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 2 Mar 2021 22:35:08 -0800 Subject: [PATCH 39/46] add `toType` helper with 0x-prefix and MAX_SAFE_INTEGER checks --- src/account.ts | 16 ++------ src/bytes.ts | 26 ++++++------ src/signature.ts | 92 +++++++++++------------------------------- src/types.ts | 51 ++++++++++++++++++++++- test/account.spec.ts | 10 ++++- test/signature.spec.ts | 8 ++-- 6 files changed, 104 insertions(+), 99 deletions(-) diff --git a/src/account.ts b/src/account.ts index 90236ca0..fb8d46ba 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,12 +1,12 @@ import assert from 'assert' import BN from 'bn.js' import * as rlp from 'rlp' -import { isHexString, stripHexPrefix } from 'ethjs-util' +import { stripHexPrefix } from 'ethjs-util' import { KECCAK256_RLP, KECCAK256_NULL } from './constants' import { zeros, bufferToHex, toBuffer } from './bytes' import { keccak, keccak256, keccakFromString, rlphash } from './hash' import { assertIsHexString, assertIsBuffer } from './helpers' -import { BNLike, BufferLike, bnToRlp } from './types' +import { BNLike, BufferLike, bnToRlp, toType, TypeOutput } from './types' const { privateKeyVerify, @@ -139,20 +139,12 @@ export const isValidAddress = function(hexAddress: string): boolean { */ export const toChecksumAddress = function(hexAddress: string, eip1191ChainId?: BNLike): string { assertIsHexString(hexAddress) - if (typeof eip1191ChainId === 'string' && !isHexString(eip1191ChainId)) { - throw new Error(`A chainId string must be provided with a 0x-prefix, given: ${eip1191ChainId}`) - } const address = stripHexPrefix(hexAddress).toLowerCase() let prefix = '' if (eip1191ChainId) { - // Performance optimization - if (typeof eip1191ChainId === 'number' && Number.isSafeInteger(eip1191ChainId)) { - prefix = eip1191ChainId.toString() - } else { - prefix = new BN(toBuffer(eip1191ChainId)).toString() - } - prefix += '0x' + const chainId = toType(eip1191ChainId, TypeOutput.BN) + prefix = chainId.toString() + '0x' } const hash = keccakFromString(prefix + address).toString('hex') diff --git a/src/bytes.ts b/src/bytes.ts index a9d13a42..f2229e3b 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -105,24 +105,24 @@ export const unpadHexString = function(a: string): string { return stripZeros(a) as string } +export type ToBufferInputTypes = + | PrefixedHexString + | number + | BN + | Buffer + | Uint8Array + | number[] + | TransformableToArray + | TransformableToBuffer + | null + | undefined + /** * Attempts to turn a value into a `Buffer`. * Inputs supported: `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` or `toBuffer()` method. * @param v the value */ -export const toBuffer = function( - v: - | PrefixedHexString - | number - | BN - | Buffer - | Uint8Array - | number[] - | TransformableToArray - | TransformableToBuffer - | null - | undefined -): Buffer { +export const toBuffer = function(v: ToBufferInputTypes): Buffer { if (v === null || v === undefined) { return Buffer.allocUnsafe(0) } diff --git a/src/signature.ts b/src/signature.ts index 96dfa3d6..9083f104 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -3,8 +3,7 @@ import BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes' import { keccak } from './hash' import { assertIsBuffer } from './helpers' -import { BNLike } from './types' -import { isHexString } from '.' +import { BNLike, toType, TypeOutput } from './types' export interface ECDSASignature { v: number @@ -22,53 +21,33 @@ export interface ECDSASignatureBuffer { * Returns the ECDSA signature of a message hash. */ export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId?: number): ECDSASignature -export function ecsign( - msgHash: Buffer, - privateKey: Buffer, - chainId: BN | string | Buffer -): ECDSASignatureBuffer +export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: BNLike): ECDSASignatureBuffer export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: any): any { - const sig = ecdsaSign(msgHash, privateKey) - const recovery: number = sig.recid - - let ret - const r = Buffer.from(sig.signature.slice(0, 32)) - const s = Buffer.from(sig.signature.slice(32, 64)) - if (!chainId || typeof chainId === 'number') { - if (chainId && !Number.isSafeInteger(chainId)) { - throw new Error( - 'The provided chainId is greater than MAX_SAFE_INTEGER (please use an alternative input type)' - ) - } - return { - r, - s, - v: chainId ? recovery + (chainId * 2 + 35) : recovery + 27 - } + const { signature, recid: recovery } = ecdsaSign(msgHash, privateKey) + + const r = Buffer.from(signature.slice(0, 32)) + const s = Buffer.from(signature.slice(32, 64)) + + if (!chainId) { + return { r, s, v: recovery + 27 } } else { - // BN, string, Buffer - if (typeof chainId === 'string' && !isHexString(chainId)) { - throw new Error(`A chainId string must be provided with a 0x-prefix, given: ${chainId}`) - } - ret = { - r, - s, - v: toBuffer( - new BN(toBuffer(chainId)) - .muln(2) - .addn(35) - .addn(recovery) - ) - } + const chainIdBN = toType(chainId, TypeOutput.BN) + const vValue = chainIdBN + .muln(2) + .addn(35) + .addn(recovery) + const v = typeof chainId === 'number' ? vValue.toNumber() : vValue.toArrayLike(Buffer) + return { r, s, v } } - - return ret } function calculateSigRecovery(v: BNLike, chainId?: BNLike): BN { - const vBN = new BN(toBuffer(v)) - const chainIdBN = chainId ? new BN(toBuffer(chainId)) : undefined - return chainIdBN ? vBN.sub(chainIdBN.muln(2).addn(35)) : vBN.subn(27) + const vBN = toType(v, TypeOutput.BN) + if (!chainId) { + return vBN.subn(27) + } + const chainIdBN = toType(chainId, TypeOutput.BN) + return vBN.sub(chainIdBN.muln(2).addn(35)) } function isValidSigRecovery(recovery: number | BN): boolean { @@ -76,25 +55,6 @@ function isValidSigRecovery(recovery: number | BN): boolean { return rec.eqn(0) || rec.eqn(1) } -function vAndChainIdTypeChecks(v: BNLike, chainId?: BNLike) { - if (typeof v === 'string' && !isHexString(v)) { - throw new Error(`A v value string must be provided with a 0x-prefix, given: ${v}`) - } - if (typeof chainId === 'string' && !isHexString(chainId)) { - throw new Error(`A chainId string must be provided with a 0x-prefix, given: ${chainId}`) - } - if (typeof v === 'number' && !Number.isSafeInteger(v)) { - throw new Error( - 'The provided v is greater than MAX_SAFE_INTEGER (please use an alternative input type)' - ) - } - if (typeof chainId === 'number' && !Number.isSafeInteger(chainId)) { - throw new Error( - 'The provided chainId is greater than MAX_SAFE_INTEGER (please use an alternative input type)' - ) - } -} - /** * ECDSA public key recovery from signature. * @returns Recovered public key @@ -106,8 +66,6 @@ export const ecrecover = function( s: Buffer, chainId?: BNLike ): Buffer { - vAndChainIdTypeChecks(v, chainId) - const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64) const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { @@ -122,7 +80,6 @@ export const ecrecover = function( * @returns Signature */ export const toRpcSig = function(v: BNLike, r: Buffer, s: Buffer, chainId?: BNLike): string { - vAndChainIdTypeChecks(v, chainId) const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') @@ -167,7 +124,6 @@ export const isValidSignature = function( homesteadOrLater: boolean = true, chainId?: BNLike ): boolean { - vAndChainIdTypeChecks(v, chainId) const SECP256K1_N_DIV_2 = new BN( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16 @@ -182,8 +138,8 @@ export const isValidSignature = function( return false } - const rBN: BN = new BN(r) - const sBN: BN = new BN(s) + const rBN = new BN(r) + const sBN = new BN(s) if (rBN.isZero() || rBN.gt(SECP256K1_N) || sBN.isZero() || sBN.gt(SECP256K1_N)) { return false diff --git a/src/types.ts b/src/types.ts index 3afa856a..511b7e48 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,7 @@ import BN from 'bn.js' +import { isHexString } from 'ethjs-util' import { Address } from './address' -import { unpadBuffer } from './bytes' +import { unpadBuffer, toBuffer, ToBufferInputTypes } from './bytes' /* * A type that represents a BNLike input that can be converted to a BN. @@ -62,3 +63,51 @@ export function bnToRlp(value: BN): Buffer { // for compatibility with browserify and similar tools return unpadBuffer(value.toArrayLike(Buffer)) } + +/** + * Type output options + */ +export enum TypeOutput { + Number, + BN, + Buffer, + PrefixedHexString +} + +export type TypeOutputReturnType = { + [TypeOutput.Number]: Number + [TypeOutput.BN]: BN + [TypeOutput.Buffer]: Buffer + [TypeOutput.PrefixedHexString]: PrefixedHexString +} + +/** + * Convert an input to a specified type + * @param input value to convert + * @param outputType type to output + */ +export function toType( + input: ToBufferInputTypes, + outputType: T +): TypeOutputReturnType[T] { + if (typeof input === 'string' && !isHexString(input)) { + throw new Error(`A string must be provided with a 0x-prefix, given: ${input}`) + } else if (typeof input === 'number' && !Number.isSafeInteger(input)) { + throw new Error( + 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)' + ) + } + + input = toBuffer(input) + + if (outputType === TypeOutput.Buffer) { + return input as any + } else if (outputType === TypeOutput.BN) { + return new BN(input) as any + } else if (outputType === TypeOutput.Number) { + return new BN(input).toNumber() as any + } else { + // outputType === TypeOutput.PrefixedHexString + return `0x${input.toString('hex')}` as any + } +} diff --git a/test/account.spec.ts b/test/account.spec.ts index a5e81aa0..469ef129 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -621,7 +621,15 @@ describe('.toChecksumAddress()', function() { addr ) const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) - assert.equal(toChecksumAddress(addr.toLowerCase(), chainIDNumber), addr) + assert.throws( + () => { + toChecksumAddress(addr.toLowerCase(), chainIDNumber) + }, + { + message: + 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)' + } + ) }) }) diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 972856f5..09dd25d8 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -348,8 +348,8 @@ describe('message sig', function() { assert.equal(toRpcSig(27, r, s), sig) assert.deepEqual(fromRpcSig(sig), { v: 27, - r: r, - s: s + r, + s }) }) @@ -372,8 +372,8 @@ describe('message sig', function() { ) assert.deepEqual(fromRpcSig(sig), { v, - r: r, - s: s + r, + s }) }) From 059ce3b12d4130a790b198e507f681298e209e49 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Wed, 3 Mar 2021 13:08:31 -0800 Subject: [PATCH 40/46] add tests for `toType` add error if output number is greater than MAX_SAFE_INTEGER --- src/types.ts | 11 ++++- test/types.spec.ts | 112 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 test/types.spec.ts diff --git a/src/types.ts b/src/types.ts index 511b7e48..bdca4baf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -75,7 +75,7 @@ export enum TypeOutput { } export type TypeOutputReturnType = { - [TypeOutput.Number]: Number + [TypeOutput.Number]: number [TypeOutput.BN]: BN [TypeOutput.Buffer]: Buffer [TypeOutput.PrefixedHexString]: PrefixedHexString @@ -105,7 +105,14 @@ export function toType( } else if (outputType === TypeOutput.BN) { return new BN(input) as any } else if (outputType === TypeOutput.Number) { - return new BN(input).toNumber() as any + const bn = new BN(input) + const max = new BN(Number.MAX_SAFE_INTEGER.toString()) + if (bn.gt(max)) { + throw new Error( + 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative output type)' + ) + } + return bn.toNumber() as any } else { // outputType === TypeOutput.PrefixedHexString return `0x${input.toString('hex')}` as any diff --git a/test/types.spec.ts b/test/types.spec.ts new file mode 100644 index 00000000..16ae5f31 --- /dev/null +++ b/test/types.spec.ts @@ -0,0 +1,112 @@ +import assert from 'assert' +import BN from 'bn.js' +import { toType, TypeOutput, intToBuffer, bufferToHex, intToHex, bnToHex, toBuffer } from '../src' + +describe('toType', function() { + describe('from Number', function() { + const num = 1000 + it('should convert to Number', function() { + const result = toType(num, TypeOutput.Number) + assert.strictEqual(result, num) + }) + it('should convert to BN', function() { + const result = toType(num, TypeOutput.BN) + assert.ok(result.eq(new BN(num))) + }) + it('should convert to Buffer', function() { + const result = toType(num, TypeOutput.Buffer) + assert.ok(result.equals(intToBuffer(num))) + }) + it('should convert to PrefixedHexString', function() { + const result = toType(num, TypeOutput.PrefixedHexString) + assert.strictEqual(result, bufferToHex(new BN(num).toArrayLike(Buffer))) + }) + it('should throw an error if greater than MAX_SAFE_INTEGER', function() { + assert.throws( + () => { + const num = Number.MAX_SAFE_INTEGER + 1 + toType(num, TypeOutput.BN) + }, + { + message: + 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)' + } + ) + }) + }) + describe('from BN', function() { + const num = new BN(1000) + it('should convert to Number', function() { + const result = toType(num, TypeOutput.Number) + assert.strictEqual(result, num.toNumber()) + }) + it('should convert to BN', function() { + const result = toType(num, TypeOutput.BN) + assert.ok(result.eq(num)) + }) + it('should convert to Buffer', function() { + const result = toType(num, TypeOutput.Buffer) + assert.ok(result.equals(num.toArrayLike(Buffer))) + }) + it('should convert to PrefixedHexString', function() { + const result = toType(num, TypeOutput.PrefixedHexString) + assert.strictEqual(result, bufferToHex(num.toArrayLike(Buffer))) + }) + it('should throw an error if converting to Number and greater than MAX_SAFE_INTEGER', function() { + const num = new BN(Number.MAX_SAFE_INTEGER).addn(1) + assert.throws( + () => { + toType(num, TypeOutput.Number) + }, + { + message: + 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative output type)' + } + ) + }) + }) + describe('from Buffer', function() { + const num = intToBuffer(1000) + it('should convert to Number', function() { + const result = toType(num, TypeOutput.Number) + assert.ok(intToBuffer(result).equals(num)) + }) + it('should convert to BN', function() { + const result = toType(num, TypeOutput.BN) + assert.ok(result.eq(new BN(num))) + }) + it('should convert to Buffer', function() { + const result = toType(num, TypeOutput.Buffer) + assert.ok(result.equals(num)) + }) + it('should convert to PrefixedHexString', function() { + const result = toType(num, TypeOutput.PrefixedHexString) + assert.strictEqual(result, bufferToHex(num)) + }) + }) + describe('from HexPrefixedString', function() { + const num = intToHex(1000) + it('should convert to Number', function() { + const result = toType(num, TypeOutput.Number) + assert.strictEqual(intToHex(result), num) + }) + it('should convert to BN', function() { + const result = toType(num, TypeOutput.BN) + assert.strictEqual(bnToHex(result), num) + }) + it('should convert to Buffer', function() { + const result = toType(num, TypeOutput.Buffer) + assert.ok(result.equals(toBuffer(num))) + }) + it('should throw an error if is not 0x-prefixed', function() { + assert.throws( + () => { + toType('1', TypeOutput.Number) + }, + { + message: 'A string must be provided with a 0x-prefix, given: 1' + } + ) + }) + }) +}) From db4ee779a50adc8173fb202708c22f278bad1206 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Wed, 3 Mar 2021 13:08:39 -0800 Subject: [PATCH 41/46] improve ecsign logic --- src/signature.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/signature.ts b/src/signature.ts index 9083f104..31424a36 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -28,17 +28,24 @@ export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: any): any { const r = Buffer.from(signature.slice(0, 32)) const s = Buffer.from(signature.slice(32, 64)) - if (!chainId) { - return { r, s, v: recovery + 27 } - } else { - const chainIdBN = toType(chainId, TypeOutput.BN) - const vValue = chainIdBN - .muln(2) - .addn(35) - .addn(recovery) - const v = typeof chainId === 'number' ? vValue.toNumber() : vValue.toArrayLike(Buffer) + if (!chainId || typeof chainId === 'number') { + // return legacy type ECDSASignature (deprecated in favor of ECDSASignatureBuffer to handle large chainIds) + if (chainId && !Number.isSafeInteger(chainId)) { + throw new Error( + 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)' + ) + } + const v = chainId ? recovery + (chainId * 2 + 35) : recovery + 27 return { r, s, v } } + + const chainIdBN = toType(chainId, TypeOutput.BN) + const v = chainIdBN + .muln(2) + .addn(35) + .addn(recovery) + .toArrayLike(Buffer) + return { r, s, v } } function calculateSigRecovery(v: BNLike, chainId?: BNLike): BN { From 46415c1e43d3f51989412055292a32915e7e2173 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Wed, 3 Mar 2021 13:08:57 -0800 Subject: [PATCH 42/46] dev ex: remove lint from `npm run test` and add as a separate step to ci --- .github/workflows/build.yml | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f5ce9e0..98993c3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,8 @@ jobs: - uses: actions/checkout@v2 - run: npm install + + - run: npm run lint - run: npm run test - uses: codecov/codecov-action@v1 diff --git a/package.json b/package.json index 6b8f355a..1692b692 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "docs:build": "npx typedoc --options typedoc.js", "lint": "ethereumjs-config-lint", "lint:fix": "ethereumjs-config-lint-fix", - "test": "npm run lint && npm run test:node && npm run test:browser", + "test": "npm run test:node && npm run test:browser", "test:browser": "karma start karma.conf.js", "test:node": "nyc --reporter=lcov mocha --require ts-node/register 'test/*.spec.ts'", "tsc": "ethereumjs-config-tsc" From 9a3298e3dd508e5f8bdf253baa6530d63c52c2a0 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 4 Mar 2021 11:15:35 +0100 Subject: [PATCH 43/46] Bumped version to v7.0.9, added CHANGELOG entry --- CHANGELOG.md | 24 ++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f493ffda..4951260e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,30 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) (modification: no type change headlines) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [7.0.9] - 2021-03-04 + +This release adds support for very high `chainId` numbers exceeding `MAX_SAFE_INTEGER` (an example is the chain ID `34180983699157880` used for the ephemeral Yolov3 testnet preparing for the `berlin` hardfork, but high chain IDs might be used for things like private test networks and the like as well). + +Function signatures for methods in `address` and `signature` are therefore expanded to allow for a `BNLike` input type (`BN | PrefixedHexString | number | Buffer`) for chain ID related parameters. + +All function signatures are still taking in a `number` input for backwards-compatibility reasons. If you use one of the following functions to implement generic use cases in your library where the chain ID is not yet known it is recommended to updated to one of the other input types (with plain `Buffer` likely be the most future-proof). Note that on some functions this changes the return value as well. + +- `account`: `toChecksumAddresss(hexAddress: string, eip1191ChainId?: number): string` + - -> `toChecksumAddress = function(hexAddress: string, eip1191ChainId?: BNLike): string` +- `account`: `isValidChecksumAddress(hexAddress: string, eip1191ChainId?: number)` + - -> `isValidChecksumAddress(hexAddress: string, eip1191ChainId?: BNLike)` +- `signature`: `ecsign(msgHash: Buffer, privateKey: Buffer, chainId?: number): ECDSASignature` + - -> `ecsign(msgHash: Buffer, privateKey: Buffer, chainId?: number): ECDSASignature` (return value stays the same on `number` input) + - -> `ecsign(msgHash: Buffer, privateKey: Buffer, chainId: BNLike): ECDSASignatureBuffer` (changed return value for other type inputs) +- `signature`: `ecrecover(msgHash: Buffer, v: number, r: Buffer, s: Buffer, chainId?: number): Buffer` + - -> `ecrecover(msgHash: Buffer, v: BNLike, r: Buffer, s: Buffer, chainId?: BNLike): Buffer` +- `signature`: `toRpcSig(v: number, r: Buffer, s: Buffer, chainId?: number): string` + - -> `toRpcSig(v: BNLike, r: Buffer, s: Buffer, chainId?: BNLike): string` +- `signature`: `isValidSignature(v: number, r: Buffer, s: Buffer, homesteadOrLater: boolean = true, chainId?: number)` + - -> `isValidSignature(v: BNLike, r: Buffer, s: Buffer, homesteadOrLater: boolean = true, chainId?: BNLike)` + +[7.0.9]: https://github.com/ethereumjs/ethereumjs-util/compare/v7.0.8...v7.0.9 + ## [7.0.8] - 2021-02-01 - New `Address.equals(address: Address)` function for easier address equality comparions, PR [#285](https://github.com/ethereumjs/ethereumjs-util/pull/285) diff --git a/package.json b/package.json index 1692b692..752310cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethereumjs-util", - "version": "7.0.8", + "version": "7.0.9", "description": "a collection of utility functions for Ethereum", "main": "dist/index.js", "types": "./dist/index.d.ts", From 35f1747f5782ebc6c62bbec1c339db377877087a Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 4 Mar 2021 13:47:28 -0800 Subject: [PATCH 44/46] convert mocha to tape --- .eslintrc.js | 5 +- .github/workflows/build.yml | 2 +- karma.conf.js | 5 +- package.json | 9 +- test/account.spec.ts | 1008 +++++++++++++++++------------------ test/address.spec.ts | 80 +-- test/bytes.spec.ts | 240 +++++---- test/constants.spec.ts | 30 +- test/externals.spec.ts | 85 +-- test/hash.spec.ts | 183 ++++--- test/object.spec.ts | 48 +- test/signature.spec.ts | 338 ++++++------ test/types.spec.ts | 156 +++--- 13 files changed, 1136 insertions(+), 1053 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index aa0843b6..fd92c294 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,3 @@ module.exports = { - extends: '@ethereumjs/eslint-config-defaults', - env: { - mocha: true - } + extends: '@ethereumjs/eslint-config-defaults' } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98993c3c..b33cc6ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: - run: npm install - run: npm run lint - - run: npm run test + - run: npm run coverage - uses: codecov/codecov-action@v1 if: matrix.node-version == '12.x' diff --git a/karma.conf.js b/karma.conf.js index a64ca12c..f9617f27 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,11 +1,10 @@ module.exports = function(config) { config.set({ - frameworks: ['mocha', 'karma-typescript'], + frameworks: ['tap', 'karma-typescript'], files: ['src/**/*.ts', 'test/**/*.ts'], preprocessors: { '**/*.ts': ['karma-typescript'] }, - plugins: ['karma-mocha', 'karma-typescript', 'karma-chrome-launcher', 'karma-firefox-launcher'], karmaTypescriptConfig: { bundlerOptions: { entrypoints: /\.spec\.ts$/ @@ -16,7 +15,7 @@ module.exports = function(config) { reporters: ['progress', 'karma-typescript'], browsers: ['FirefoxHeadless', 'ChromeHeadless'], singleRun: true, - concurrency: Infinity, + concurrency: 1, // Fail after timeout browserDisconnectTimeout: 100000, browserNoActivityTimeout: 100000 diff --git a/package.json b/package.json index 752310cc..93430636 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,13 @@ "scripts": { "build": "ethereumjs-config-ts-build", "prepublishOnly": "npm run test && npm run build", + "coverage": "ethereumjs-config-coverage", "docs:build": "npx typedoc --options typedoc.js", "lint": "ethereumjs-config-lint", "lint:fix": "ethereumjs-config-lint-fix", "test": "npm run test:node && npm run test:browser", "test:browser": "karma start karma.conf.js", - "test:node": "nyc --reporter=lcov mocha --require ts-node/register 'test/*.spec.ts'", + "test:node": "tape -r ts-node/register test/*.spec.ts", "tsc": "ethereumjs-config-tsc" }, "husky": { @@ -97,18 +98,18 @@ "@ethereumjs/config-typescript": "^2.0.0", "@ethereumjs/eslint-config-defaults": "^2.0.0", "@types/assert": "^1.5.4", - "@types/mocha": "^8.2.0", "@types/node": "^11.9.0", "@types/secp256k1": "^4.0.1", + "@types/tape": "^4.13.0", "husky": "^2.1.0", "karma": "^5.0.2", "karma-chrome-launcher": "^2.0.0", "karma-firefox-launcher": "^1.0.0", - "karma-mocha": "^2.0.0", + "karma-tap": "^4.2.0", "karma-typescript": "^4.1.1", - "mocha": "^8.2.1", "nyc": "^15.0.0", "prettier": "^1.15.3", + "tape": "^5.2.2", "ts-node": "^8.6.2", "typedoc": "next", "typedoc-plugin-markdown": "^2.2.16", diff --git a/test/account.spec.ts b/test/account.spec.ts index 469ef129..483f88de 100644 --- a/test/account.spec.ts +++ b/test/account.spec.ts @@ -1,8 +1,8 @@ -import assert from 'assert' -import BN from 'bn.js' -import * as rlp from 'rlp' +import tape from 'tape' +import { encode } from 'rlp' import { Account, + BN, isValidPrivate, isValidPublic, importPublic, @@ -18,30 +18,25 @@ import { } from '../src' const eip1014Testdata = require('./testdata/eip1014Examples.json') -describe('Account', function() { - describe('empty constructor', function() { +tape('Account', function(t) { + t.test('empty constructor', function(st) { const account = new Account() - it('should have zero nonce', function() { - assert.ok(account.nonce.isZero()) - }) - it('should have zero balance', function() { - assert.ok(account.balance.isZero()) - }) - it('should have stateRoot equal to KECCAK256_RLP', function() { - assert.ok( - account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' - ) - }) - it('should have codeHash equal to KECCAK256_NULL', function() { - assert.equal( - account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' - ) - }) + st.ok(account.nonce.isZero(), 'should have zero nonce') + st.ok(account.balance.isZero(), 'should have zero balance') + st.equal( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + 'should have stateRoot equal to KECCAK256_RLP' + ) + st.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'should have codeHash equal to KECCAK256_NULL' + ) + st.end() }) - describe('from Array data', function() { + t.test('from Array data', function(st) { const raw = [ '0x02', // nonce '0x0384', // balance @@ -49,27 +44,22 @@ describe('Account', function() { '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' // codeHash ] const account = Account.fromValuesArray(raw.map(toBuffer)) - it('should have correct nonce', function() { - assert.ok(account.nonce.eqn(2)) - }) - it('should have correct balance', function() { - assert.ok(account.balance.eqn(900)) - }) - it('should have correct stateRoot', function() { - assert.equal( - account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' - ) - }) - it('should have correct codeHash', function() { - assert.equal( - account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' - ) - }) + st.ok(account.nonce.eqn(2), 'should have correct nonce') + st.ok(account.balance.eqn(900), 'should have correct balance') + st.equal( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + 'should have correct stateRoot' + ) + st.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'should have correct codeHash' + ) + st.end() }) - describe('from Object data', function() { + t.test('from Object data', function(st) { const raw = { nonce: '0x02', balance: '0x0384', @@ -77,53 +67,43 @@ describe('Account', function() { codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' } const account = Account.fromAccountData(raw) - it('should have correct nonce', function() { - assert.ok(account.nonce.eqn(2)) - }) - it('should have correct balance', function() { - assert.ok(account.balance.eqn(900)) - }) - it('should have correct stateRoot', function() { - assert.equal( - account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' - ) - }) - it('should have correct codeHash', function() { - assert.equal( - account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' - ) - }) + st.ok(account.nonce.eqn(2), 'should have correct nonce') + st.ok(account.balance.eqn(900), 'should have correct balance') + st.equal( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + 'should have correct stateRoot' + ) + st.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'should have correct codeHash' + ) + st.end() }) - describe('from RLP data', function() { + t.test('from RLP data', function(st) { const accountRlp = Buffer.from( 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', 'hex' ) const account = Account.fromRlpSerializedAccount(accountRlp) - it('should have correct nonce', function() { - assert.ok(account.nonce.eqn(2)) - }) - it('should have correct balance', function() { - assert.ok(account.balance.eqn(900)) - }) - it('should have correct stateRoot', function() { - assert.equal( - account.stateRoot.toString('hex'), - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' - ) - }) - it('should have correct codeHash', function() { - assert.equal( - account.codeHash.toString('hex'), - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' - ) - }) + st.ok(account.nonce.eqn(2), 'should have correct nonce') + st.ok(account.balance.eqn(900), 'should have correct balance') + st.equal( + account.stateRoot.toString('hex'), + '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + 'should have correct stateRoot' + ) + st.equal( + account.codeHash.toString('hex'), + 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'should have correct codeHash' + ) + st.end() }) - describe('serialize', function() { + t.test('serialize', function(st) { const raw = { nonce: '0x01', balance: '0x42', @@ -131,603 +111,571 @@ describe('Account', function() { codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' } const account = Account.fromAccountData(raw) - const accountRlp = rlp.encode([raw.nonce, raw.balance, raw.stateRoot, raw.codeHash]) - it('should serialize correctly', function() { - assert.ok(account.serialize().equals(accountRlp)) - }) + const accountRlp = encode([raw.nonce, raw.balance, raw.stateRoot, raw.codeHash]) + st.ok(account.serialize().equals(accountRlp), 'should serialize correctly') + st.end() }) - describe('isContract', function() { - it('should return false for a non-contract account', function() { - const accountRlp = Buffer.from( - 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - 'hex' - ) - const account = Account.fromRlpSerializedAccount(accountRlp) - assert.equal(account.isContract(), false) - }) + t.test('isContract', function(st) { + const accountRlp = Buffer.from( + 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', + 'hex' + ) + let account = Account.fromRlpSerializedAccount(accountRlp) + st.notOk(account.isContract(), 'should return false for a non-contract account') - it('should return true for a contract account', function() { - const raw = { - nonce: '0x01', - balance: '0x0042', - stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' - } - const account = Account.fromAccountData(raw) - assert.ok(account.isContract()) - }) + const raw = { + nonce: '0x01', + balance: '0x0042', + stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' + } + account = Account.fromAccountData(raw) + st.ok(account.isContract(), 'should return true for a contract account') + st.end() }) - describe('isEmpty', function() { - it('should return true for an empty account', function() { - const account = new Account() - assert.ok(account.isEmpty()) - }) + t.test('isEmpty', function(st) { + let account = new Account() + st.ok(account.isEmpty(), 'should return true for an empty account') - it('should return false for a non-empty account', function() { - const raw = { - nonce: '0x01', - balance: '0x0042', - stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - codeHash: '0xd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b' - } - const account = Account.fromAccountData(raw) - assert.equal(account.isEmpty(), false) - }) + const raw = { + nonce: '0x01', + balance: '0x0042', + stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + codeHash: '0xd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b' + } + account = Account.fromAccountData(raw) + st.notOk(account.isEmpty(), 'should return false for a non-empty account') + st.end() }) - describe('validation', function() { - it('should only accept length 32 buffer for stateRoot', function() { - assert.throws(() => { - new Account(undefined, undefined, Buffer.from('hey'), undefined) - }) - }) + t.test('validation', function(st) { + st.throws(() => { + new Account(undefined, undefined, Buffer.from('hey'), undefined) + }, 'should only accept length 32 buffer for stateRoot') - it('should only accept length 32 buffer for codeHash', function() { - assert.throws(() => { - new Account(undefined, undefined, undefined, Buffer.from('hey')) - }) - }) + st.throws(() => { + new Account(undefined, undefined, undefined, Buffer.from('hey')) + }, 'should only accept length 32 buffer for codeHash') - it('should only accept an array in fromRlpSerializedAccount', function() { - const data = { balance: new BN(5) } - assert.throws(() => { - Account.fromRlpSerializedAccount(data as any) - }) - }) + const data = { balance: new BN(5) } + st.throws(() => { + Account.fromRlpSerializedAccount(data as any) + }, 'should only accept an array in fromRlpSerializedAccount') - it('should not accept nonce less than 0', function() { - assert.throws(() => { - new Account(new BN(-5)) - }) - }) + st.throws(() => { + new Account(new BN(-5)) + }, 'should not accept nonce less than 0') - it('should not accept balance less than 0', function() { - assert.throws(() => { - new Account(undefined, new BN(-5)) - }) - }) + st.throws(() => { + new Account(undefined, new BN(-5)) + }, 'should not accept balance less than 0') + st.end() }) }) -describe('isValidPrivate', function() { - const SECP256K1_N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) - it('should fail on short input', function() { - const tmp = '0011223344' - assert.throws(function() { +tape('Utility Functions', function(t) { + t.test('isValidPrivate', function(st) { + const SECP256K1_N = new BN( + 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', + 16 + ) + + let tmp = '0011223344' + st.throws(function() { isValidPrivate(Buffer.from(tmp, 'hex')) - }) - }) - it('should fail on too big input', function() { - const tmp = + }, 'should fail on short input') + + tmp = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - assert.throws(function() { + st.throws(function() { isValidPrivate(Buffer.from(tmp, 'hex')) - }) - }) - it('should fail on wrong input type', function() { - assert.throws(function() { + }, 'should fail on too big input') + + st.throws(function() { isValidPrivate(('WRONG_INPUT_TYPE') as Buffer) - }) - }) - it('should fail on invalid curve (zero)', function() { - const tmp = '0000000000000000000000000000000000000000000000000000000000000000' - assert.equal(isValidPrivate(Buffer.from(tmp, 'hex')), false) - }) - it('should fail on invalid curve (== N)', function() { - const tmp = SECP256K1_N.toString(16) - assert.equal(isValidPrivate(Buffer.from(tmp, 'hex')), false) - }) - it('should fail on invalid curve (>= N)', function() { - const tmp = SECP256K1_N.addn(1).toString(16) - assert.equal(isValidPrivate(Buffer.from(tmp, 'hex')), false) - }) - it('should work otherwise (< N)', function() { - const tmp = SECP256K1_N.subn(1).toString(16) - assert.equal(isValidPrivate(Buffer.from(tmp, 'hex')), true) + }, 'should fail on wrong input type') + + tmp = '0000000000000000000000000000000000000000000000000000000000000000' + st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on invalid curve (zero)') + + tmp = SECP256K1_N.toString(16) + st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on invalid curve (== N)') + + tmp = SECP256K1_N.addn(1).toString(16) + st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on invalid curve (>= N)') + + tmp = SECP256K1_N.subn(1).toString(16) + st.ok(isValidPrivate(Buffer.from(tmp, 'hex')), 'should work otherwise (< N)') + st.end() }) -}) -describe('isValidPublic', function() { - it('should fail on too short input', function() { - const pubKey = Buffer.from( + t.test('isValidPublic', function(st) { + let pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', 'hex' ) - assert.equal(isValidPublic(pubKey), false) - }) - it('should fail on too big input', function() { - const pubKey = Buffer.from( + st.notOk(isValidPublic(pubKey), 'should fail on too short input') + + pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d00', 'hex' ) - assert.equal(isValidPublic(pubKey), false) - }) - it('should fail on SEC1 key', function() { - const pubKey = Buffer.from( + st.notOk(isValidPublic(pubKey), 'should fail on too big input') + + pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - assert.equal(isValidPublic(pubKey), false) - }) - it("shouldn't fail on SEC1 key with sanitize enabled", function() { - const pubKey = Buffer.from( + st.notOk(isValidPublic(pubKey), 'should fail on SEC1 key') + + pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - assert.equal(isValidPublic(pubKey, true), true) - }) - it('should fail with an invalid SEC1 public key', function() { - const pubKey = Buffer.from( + st.ok(isValidPublic(pubKey, true), "shouldn't fail on SEC1 key wt.testh sant.testize enabled") + + pubKey = Buffer.from( '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - assert.equal(isValidPublic(pubKey, true), false) - }) - it('should work with compressed keys with sanitize enabled', function() { - const pubKey = Buffer.from( + st.notOk(isValidPublic(pubKey), 'should fail wt.testh an invalid SEC1 public key') + + pubKey = Buffer.from( '033a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a', 'hex' ) - assert.equal(isValidPublic(pubKey, true), true) - }) - it('should work with sanitize enabled', function() { - const pubKey = Buffer.from( + st.ok( + isValidPublic(pubKey, true), + 'should work wt.testh compressed keys wt.testh sant.testize enabled' + ) + + pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - assert.equal(isValidPublic(pubKey, true), true) - }) - it('should work otherwise', function() { - const pubKey = Buffer.from( + st.ok(isValidPublic(pubKey, true), 'should work wt.testh sant.testize enabled') + + pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - assert.equal(isValidPublic(pubKey), true) - }) - it('should throw if input is not Buffer', function() { - const pubKey = - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' + st.ok(isValidPublic(pubKey), 'should work otherwise') + + pubKey = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' as any try { isValidPublic((pubKey) as Buffer) } catch (err) { - assert(err.message.includes('This method only supports Buffer')) + st.ok( + err.message.includes('This method only supports Buffer'), + 'should throw if input is not Buffer' + ) } + st.end() }) -}) -describe('importPublic', function() { - const pubKey = - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - it('should work with an Ethereum public key', function() { - const tmp = + t.test('importPublic', function(st) { + const pubKey = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - assert.equal(importPublic(Buffer.from(tmp, 'hex')).toString('hex'), pubKey) - }) - it('should work with uncompressed SEC1 keys', function() { - const tmp = + + let tmp = + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' + st.equal( + importPublic(Buffer.from(tmp, 'hex')).toString('hex'), + pubKey, + 'should work wt.testh an Ethereum public key' + ) + + tmp = '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - assert.equal(importPublic(Buffer.from(tmp, 'hex')).toString('hex'), pubKey) - }) - it('should work with compressed SEC1 keys', function() { - const tmp = '033a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a' - assert.equal(importPublic(Buffer.from(tmp, 'hex')).toString('hex'), pubKey) - }) - it('should throw if input is not Buffer', function() { - assert.throws(function() { + st.equal( + importPublic(Buffer.from(tmp, 'hex')).toString('hex'), + pubKey, + 'should work wt.testh uncompressed SEC1 keys' + ) + + tmp = '033a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a' + st.equal( + importPublic(Buffer.from(tmp, 'hex')).toString('hex'), + pubKey, + 'should work wt.testh compressed SEC1 keys' + ) + + st.throws(function() { importPublic((pubKey) as Buffer) - }) + }, 'should throw if input is not Buffer') + st.end() }) -}) -describe('publicToAddress', function() { - it('should produce an address given a public key', function() { - const pubKey = Buffer.from( + t.test('publicToAddress', function(st) { + let pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' - const r = publicToAddress(pubKey) - assert.equal(r.toString('hex'), address) - }) - it('should produce an address given a SEC1 public key', function() { - const pubKey = Buffer.from( + let address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' + let r = publicToAddress(pubKey) + st.equal(r.toString('hex'), address, 'should produce an address given a public key') + + pubKey = Buffer.from( '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' - const r = publicToAddress(pubKey, true) - assert.equal(r.toString('hex'), address) - }) - it("shouldn't produce an address given an invalid SEC1 public key", function() { - const pubKey = Buffer.from( + address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' + r = publicToAddress(pubKey, true) + st.equal(r.toString('hex'), address, 'should produce an address given a SEC1 public key') + + pubKey = Buffer.from( '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) - assert.throws(function() { + st.throws(function() { publicToAddress(pubKey, true) - }) - }) - it("shouldn't produce an address given an invalid public key", function() { - const pubKey = Buffer.from( + }, "shouldn't produce an address given an invalid SEC1 public key") + + pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', 'hex' ) - assert.throws(function() { + st.throws(function() { publicToAddress(pubKey) - }) - }) - it('should throw if input is not a buffer', function() { - const pubKey: any = - '0x3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - assert.throws(function() { + }, "shouldn't produce an address given an invalid public key") + + pubKey = '0x3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' as any + st.throws(function() { publicToAddress(pubKey) - }) + }, 'should throw if input is not a buffer') + st.end() }) -}) -describe('privateToPublic', function() { - it('should produce a public key given a private key', function() { + t.test('privateToPublic', function(st) { const pubKey = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - const privateKey = Buffer.from( + let privateKey = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', 'hex' ) - const r: any = privateToPublic(privateKey).toString('hex') - assert.equal(r.toString('hex'), pubKey) - }) - it("shouldn't produce a public key given an invalid private key", function() { - const privateKey1 = Buffer.from( + const r = privateToPublic(privateKey) + st.equal(r.toString('hex'), pubKey, 'should produce a public key given a private key') + + privateKey = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f2a', 'hex' ) - const privateKey2 = Buffer.from( + st.throws(function() { + privateToPublic(privateKey) + }, "shouldn't produce a public key given an invalid private key") + + privateKey = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c', 'hex' ) - assert.throws(function() { - privateToPublic(privateKey1) - }) - assert.throws(function() { - privateToPublic(privateKey2) - }) - }) + st.throws(function() { + privateToPublic(privateKey) + }, "shouldn't produce a public key given an invalid private key") - it('should throw if private key is not Buffer', function() { - const privateKey = '0xea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f' + privateKey = '0xea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f' as any try { privateToPublic((privateKey) as Buffer) } catch (err) { - assert(err.message.includes('This method only supports Buffer')) - assert(err.message.includes(privateKey)) + st.ok( + err.message.includes('This method only supports Buffer'), + 'should throw if private key is not Buffer' + ) + st.ok(err.message.includes(privateKey), 'should throw if private key is not Buffer') } + st.end() }) -}) -describe('privateToAddress', function() { - it('should produce an address given a private key', function() { + t.test('privateToAddress', function(st) { const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' // Our private key const privateKey = Buffer.from( 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', 'hex' ) - const r: any = privateToAddress(privateKey).toString('hex') - assert.equal(r.toString('hex'), address) + const r = privateToAddress(privateKey) + st.equal(r.toString('hex'), address, 'should produce an address given a private key') + st.end() }) -}) -describe('generateAddress', function() { - it('should produce an address given a public key', function() { - const add: any = generateAddress( + t.test('generateAddress', function(st) { + const addr = generateAddress( Buffer.from('990ccf8a0de58091c028d6ff76bb235ee67c1c39', 'utf8'), toBuffer(14) - ).toString('hex') - assert.equal(add.toString('hex'), '936a4295d8d74e310c0c95f0a63e53737b998d12') + ) + st.equal( + addr.toString('hex'), + '936a4295d8d74e310c0c95f0a63e53737b998d12', + 'should produce an address given a public key' + ) + st.end() }) -}) -describe('generateAddress with hex prefix', function() { - it('should produce an address given a public key', function() { - const add: any = generateAddress( + t.test('generateAddress wt.testh hex prefix', function(st) { + const addr = generateAddress( toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), toBuffer(14) - ).toString('hex') - assert.equal(add.toString('hex'), 'd658a4b8247c14868f3c512fa5cbb6e458e4a989') + ) + st.equal( + addr.toString('hex'), + 'd658a4b8247c14868f3c512fa5cbb6e458e4a989', + 'should produce an address given a public key' + ) + st.end() }) -}) -describe('generateAddress with nonce 0 (special case)', function() { - it('should produce an address given a public key', function() { - const add: any = generateAddress( + t.test('generateAddress wt.testh nonce 0 (special case)', function(st) { + const addr = generateAddress( toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), toBuffer(0) - ).toString('hex') - assert.equal(add.toString('hex'), 'bfa69ba91385206bfdd2d8b9c1a5d6c10097a85b') + ) + st.equal( + addr.toString('hex'), + 'bfa69ba91385206bfdd2d8b9c1a5d6c10097a85b', + 'should produce an address given a public key' + ) + st.end() }) -}) -describe('generateAddress with non-buffer inputs', function() { - it('should throw if address is not Buffer', function() { - assert.throws(function() { + t.test('generateAddress wt.testh non-buffer inputs', function(st) { + st.throws(function() { generateAddress( ('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') as Buffer, toBuffer(0) ) - }) - }) - it('should throw if nonce is not Buffer', function() { - assert.throws(function() { + }, 'should throw if address is not Buffer') + + st.throws(function() { generateAddress( toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), (0) as Buffer ) - }) - }) -}) - -describe('generateAddress2: EIP-1014 testdata examples', function() { - for (let i = 0; i <= 6; i++) { - const e = eip1014Testdata[i] - it(`${e['comment']}: should generate the addresses provided`, function() { - const result = generateAddress2( - toBuffer(e['address']), - toBuffer(e['salt']), - toBuffer(e['initCode']) - ) - assert.equal('0x' + result.toString('hex'), e['result']) - }) - } -}) - -describe('generateAddress2: non-buffer inputs', function() { - const e = eip1014Testdata[0] - - it('should throw if address is not Buffer', function() { - assert.throws(function() { - generateAddress2( - (e['address']) as Buffer, - toBuffer(e['salt']), - toBuffer(e['initCode']) - ) - }) - }) - it('should throw if salt is not Buffer', function() { - assert.throws(function() { - generateAddress2( - toBuffer(e['address']), - (e['salt']) as Buffer, - toBuffer(e['initCode']) - ) - }) - }) - it('should throw if initCode is not Buffer', function() { - assert.throws(function() { - generateAddress2( - toBuffer(e['address']), - toBuffer(e['salt']), - (e['initCode']) as Buffer + }, 'should throw if nonce is not Buffer') + st.end() + }) + + t.test('generateAddress2: EIP-1014 testdata examples', function(st) { + for (const testdata of eip1014Testdata) { + const { address, comment, result, salt, initCode } = testdata + const addr = generateAddress2(toBuffer(address), toBuffer(salt), toBuffer(initCode)) + st.equal( + '0x' + addr.toString('hex'), + result, + `${comment}: should generate the addresses provided` ) - }) - }) -}) - -const eip55ChecksumAddresses = [ - // All caps - '0x52908400098527886E0F7030069857D2E4169EE7', - '0x8617E340B3D01FA5F11F306F4090FD50E238070D', - // All Lower - '0xde709f2102306220921060314715629080e2fb77', - '0x27b1fdb04752bbc536007a920d24acb045561c26', - // Normal - '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', - '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359', - '0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB', - '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb' -] - -const eip1191ChecksummAddresses = { - 1: [ - '0x88021160c5C792225E4E5452585947470010289d', - '0x27b1FdB04752bBc536007a920D24ACB045561c26', - '0x52908400098527886e0f7030069857D2e4169EE7', - '0x5aaeB6053f3E94C9b9A09f33669435e7Ef1bEAed', - '0x8617E340b3d01FA5F11F306f4090FD50E238070d', - '0xd1220a0CF47C7B9Be7A2E6ba89F429762E7B9Adb', - '0xdBf03b407c01e7cD3CBea99509d93f8dDDC8C6fB', - '0xDe709F2102306220921060314715629080E2fb77', - '0xfb6916095Ca1dF60bB79cE92ce3ea74C37c5D359' - ], - 30: [ - '0x6549F4939460DE12611948B3F82B88C3C8975323', - '0x27b1FdB04752BBc536007A920D24ACB045561c26', - '0x3599689E6292B81B2D85451025146515070129Bb', - '0x52908400098527886E0F7030069857D2E4169ee7', - '0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD', - '0x8617E340b3D01Fa5f11f306f4090fd50E238070D', - '0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB', - '0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB', - '0xDe709F2102306220921060314715629080e2FB77', - '0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359' - ], - 31: [ - '0x42712D45473476B98452F434E72461577D686318', - '0x27B1FdB04752BbC536007a920D24acB045561C26', - '0x3599689e6292b81b2D85451025146515070129Bb', - '0x52908400098527886E0F7030069857D2e4169EE7', - '0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd', - '0x66f9664F97F2b50f62d13eA064982F936DE76657', - '0x8617e340b3D01fa5F11f306F4090Fd50e238070d', - '0xDE709F2102306220921060314715629080e2Fb77', - '0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359', - '0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB', - '0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB' + } + st.end() + }) + + t.test('generateAddress2: non-buffer inputs', function(st) { + const { address, salt, initCode } = eip1014Testdata[0] + + st.throws(function() { + generateAddress2((address) as Buffer, toBuffer(salt), toBuffer(initCode)) + }, 'should throw if address is not Buffer') + + st.throws(function() { + generateAddress2(toBuffer(address), (salt) as Buffer, toBuffer(initCode)) + }, 'should throw if salt is not Buffer') + + st.throws(function() { + generateAddress2(toBuffer(address), toBuffer(salt), (initCode) as Buffer) + }, 'should throw if initCode is not Buffer') + st.end() + }) + + const eip55ChecksumAddresses = [ + // All caps + '0x52908400098527886E0F7030069857D2E4169EE7', + '0x8617E340B3D01FA5F11F306F4090FD50E238070D', + // All Lower + '0xde709f2102306220921060314715629080e2fb77', + '0x27b1fdb04752bbc536007a920d24acb045561c26', + // Normal + '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', + '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359', + '0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB', + '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb' ] -} -describe('.toChecksumAddress()', function() { - describe('EIP55', function() { - it('should work', function() { + const eip1191ChecksummAddresses = { + 1: [ + '0x88021160c5C792225E4E5452585947470010289d', + '0x27b1FdB04752bBc536007a920D24ACB045561c26', + '0x52908400098527886e0f7030069857D2e4169EE7', + '0x5aaeB6053f3E94C9b9A09f33669435e7Ef1bEAed', + '0x8617E340b3d01FA5F11F306f4090FD50E238070d', + '0xd1220a0CF47C7B9Be7A2E6ba89F429762E7B9Adb', + '0xdBf03b407c01e7cD3CBea99509d93f8dDDC8C6fB', + '0xDe709F2102306220921060314715629080E2fb77', + '0xfb6916095Ca1dF60bB79cE92ce3ea74C37c5D359' + ], + 30: [ + '0x6549F4939460DE12611948B3F82B88C3C8975323', + '0x27b1FdB04752BBc536007A920D24ACB045561c26', + '0x3599689E6292B81B2D85451025146515070129Bb', + '0x52908400098527886E0F7030069857D2E4169ee7', + '0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD', + '0x8617E340b3D01Fa5f11f306f4090fd50E238070D', + '0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB', + '0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB', + '0xDe709F2102306220921060314715629080e2FB77', + '0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359' + ], + 31: [ + '0x42712D45473476B98452F434E72461577D686318', + '0x27B1FdB04752BbC536007a920D24acB045561C26', + '0x3599689e6292b81b2D85451025146515070129Bb', + '0x52908400098527886E0F7030069857D2e4169EE7', + '0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd', + '0x66f9664F97F2b50f62d13eA064982F936DE76657', + '0x8617e340b3D01fa5F11f306F4090Fd50e238070d', + '0xDE709F2102306220921060314715629080e2Fb77', + '0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359', + '0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB', + '0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB' + ] + } + + t.test('toChecksumAddress()', function(st) { + st.test('EIP55', function(st) { for (let i = 0; i < eip55ChecksumAddresses.length; i++) { const tmp = eip55ChecksumAddresses[i] - assert.equal(toChecksumAddress(tmp.toLowerCase()), tmp) + st.equal(toChecksumAddress(tmp.toLowerCase()), tmp) } - }) - }) - - describe('EIP1191', function() { - it('Should encode the example addresses correctly', function() { - for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { - for (const addr of addresses) { - assert.equal(toChecksumAddress(addr.toLowerCase(), Number(chainId)), addr) - assert.equal(toChecksumAddress(addr.toLowerCase(), Buffer.from([chainId])), addr) - assert.equal(toChecksumAddress(addr.toLowerCase(), new BN(chainId)), addr) - assert.equal( - toChecksumAddress(addr.toLowerCase(), '0x' + Buffer.from([chainId]).toString('hex')), - addr - ) + st.end() + }) + + st.test('EIP1191', function(st) { + st.test('Should encode the example addresses correctly', function(st) { + for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { + for (const addr of addresses) { + st.equal(toChecksumAddress(addr.toLowerCase(), Number(chainId)), addr) + st.equal(toChecksumAddress(addr.toLowerCase(), Buffer.from([chainId])), addr) + st.equal(toChecksumAddress(addr.toLowerCase(), new BN(chainId)), addr) + st.equal( + toChecksumAddress(addr.toLowerCase(), '0x' + Buffer.from([chainId]).toString('hex')), + addr + ) + } } - } - }) - it('Should encode large chain ids greater than MAX_INTEGER correctly', function() { - const addr = '0x88021160C5C792225E4E5452585947470010289D' - const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') - assert.equal(toChecksumAddress(addr.toLowerCase(), chainIDBuffer), addr) - assert.equal(toChecksumAddress(addr.toLowerCase(), new BN(chainIDBuffer)), addr) - assert.equal( - toChecksumAddress(addr.toLowerCase(), '0x' + chainIDBuffer.toString('hex')), - addr - ) - const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) - assert.throws( - () => { + st.end() + }) + st.test('Should encode large chain ids greater than MAX_INTEGER correctly', function(st) { + const addr = '0x88021160C5C792225E4E5452585947470010289D' + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + st.equal(toChecksumAddress(addr.toLowerCase(), chainIDBuffer), addr) + st.equal(toChecksumAddress(addr.toLowerCase(), new BN(chainIDBuffer)), addr) + st.equal(toChecksumAddress(addr.toLowerCase(), '0x' + chainIDBuffer.toString('hex')), addr) + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + st.throws(() => { toChecksumAddress(addr.toLowerCase(), chainIDNumber) - }, - { - message: - 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)' - } - ) + }) + st.end() + }) + st.end() }) - }) - describe('input format', function() { - it('Should throw when the address is not hex-prefixed', function() { - assert.throws(function() { + st.test('input format', function(st) { + st.throws(function() { toChecksumAddress('52908400098527886E0F7030069857D2E4169EE7'.toLowerCase()) - }) - }) - it('Should throw when the chainId is not hex-prefixed', function() { - assert.throws(function() { + }, 'Should throw when the address is not hex-prefixed') + + st.throws(function() { toChecksumAddress('0xde709f2102306220921060314715629080e2fb77', '1234') - }) + }, 'Should throw when the chainId is not hex-prefixed') + st.end() }) }) -}) -describe('.isValidChecksumAddress()', function() { - describe('EIP55', function() { - it('should return true', function() { + t.test('isValidChecksumAddress()', function(st) { + st.test('EIP55', function(st) { for (let i = 0; i < eip55ChecksumAddresses.length; i++) { - assert.equal(isValidChecksumAddress(eip55ChecksumAddresses[i]), true) + st.ok(isValidChecksumAddress(eip55ChecksumAddresses[i])) } - }) - it('should validate', function() { - assert.equal(isValidChecksumAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6a'), false) - }) - }) - - describe('EIP1191', function() { - it('Should return true for the example addresses', function() { - for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { - for (const addr of addresses) { - assert.equal(isValidChecksumAddress(addr, Number(chainId)), true) - assert.equal(isValidChecksumAddress(addr, Buffer.from([chainId])), true) - assert.equal(isValidChecksumAddress(addr, new BN(chainId)), true) - assert.equal( - isValidChecksumAddress(addr, '0x' + Buffer.from([chainId]).toString('hex')), - true - ) + st.notOk(isValidChecksumAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6a')) + st.end() + }) + + st.test('EIP1191', function(st) { + st.test('Should return true for the example addresses', function(st) { + for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { + for (const addr of addresses) { + st.ok(isValidChecksumAddress(addr, Number(chainId))) + st.ok(isValidChecksumAddress(addr, Buffer.from([chainId]))) + st.ok(isValidChecksumAddress(addr, new BN(chainId))) + st.equal( + isValidChecksumAddress(addr, '0x' + Buffer.from([chainId]).toString('hex')), + true + ) + } } - } - }) + st.end() + }) - it('Should return false for invalid cases', function() { - // If we set the chain id, an EIP55 encoded address should be invalid - for (let i = 0; i < eip55ChecksumAddresses.length; i++) { - assert.equal(isValidChecksumAddress(eip55ChecksumAddresses[i], 1), false) - } + st.test('Should return false for invalid cases', function(st) { + // If we set the chain id, an EIP55 encoded address should be invalid + for (let i = 0; i < eip55ChecksumAddresses.length; i++) { + st.notOk(isValidChecksumAddress(eip55ChecksumAddresses[i], 1)) + } - assert.equal(isValidChecksumAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6a', 1), false) - }) + st.notOk(isValidChecksumAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6a', 1)) + st.end() + }) - it('Should return false if the wrong chain id is used', function() { - for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { - for (const addr of addresses) { - assert.equal(isValidChecksumAddress(addr, Number(chainId) + 1), false) + st.test('Should return false if the wrong chain id is used', function(st) { + for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { + for (const addr of addresses) { + st.notOk(isValidChecksumAddress(addr, Number(chainId) + 1)) + } } - } - }) - }) + st.end() + }) - describe('input format', function() { - it('Should throw when the address is not hex-prefixed', function() { - assert.throws(function() { - isValidChecksumAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + st.test('input format', function(st) { + st.throws(() => { + isValidChecksumAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + }, 'Should throw when the address is not hex-prefixed') + st.end() }) + + st.end() }) }) -}) -describe('.isValidAddress()', function() { - it('should return true', function() { - assert.equal(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6a'), true) - assert.equal(isValidAddress('0x52908400098527886E0F7030069857D2E4169EE7'), true) - }) - it('should return false', function() { - assert.equal(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6'), false) - assert.equal(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6aa'), false) - }) - it('should throw when input is not hex prefixed', function() { - assert.throws(function() { - isValidAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + t.test('isValidAddress()', function(st) { + st.test('should return true', function(st) { + st.ok(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6a')) + st.ok(isValidAddress('0x52908400098527886E0F7030069857D2E4169EE7')) + st.end() }) - assert.throws(function() { - isValidAddress('x2f015c60e0be116b1f0cd534704db9c92118fb6a') + st.test('should return false', function(st) { + st.notOk(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6')) + st.notOk(isValidAddress('0x2f015c60e0be116b1f0cd534704db9c92118fb6aa')) + st.end() }) - assert.throws(function() { - isValidAddress('0X52908400098527886E0F7030069857D2E4169EE7') + st.test('should throw when input is not hex prefixed', function(st) { + st.throws(function() { + isValidAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + }) + st.throws(function() { + isValidAddress('x2f015c60e0be116b1f0cd534704db9c92118fb6a') + }) + st.throws(function() { + isValidAddress('0X52908400098527886E0F7030069857D2E4169EE7') + }) + st.end() + }) + st.test('error message should have correct format', function(st) { + const input = '2f015c60e0be116b1f0cd534704db9c92118fb6a' + try { + isValidAddress('2f015c60e0be116b1f0cd534704db9c92118fb6a') + } catch (err) { + st.ok(err.message.includes('only supports 0x-prefixed hex strings')) + st.ok(err.message.includes(input)) + } + st.end() }) }) - 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/address.spec.ts b/test/address.spec.ts index ae92f17e..804084b9 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -1,105 +1,115 @@ -import assert from 'assert' -import { BN, toBuffer } from '../src' -import { Address } from '../src' +import tape from 'tape' +import { Address, BN, toBuffer } from '../src' const eip1014Testdata = require('./testdata/eip1014Examples.json') -describe('Address', () => { +tape('Address', t => { const ZERO_ADDR_S = '0x0000000000000000000000000000000000000000' - it('should validate address length', () => { + t.test('should validate address length', st => { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a11' - assert.throws(() => Address.fromString(str)) + st.throws(() => Address.fromString(str)) const shortStr = '0x2f015c60e0be116b1f0cd534704db9c92118fb' - assert.throws(() => Address.fromString(shortStr)) + st.throws(() => Address.fromString(shortStr)) const buf = toBuffer(str) - assert.throws(() => new Address(buf)) + st.throws(() => new Address(buf)) + st.end() }) - it('should generate a zero address', () => { + t.test('should generate a zero address', st => { const addr = Address.zero() - assert.deepEqual(addr.buf, toBuffer(ZERO_ADDR_S)) - assert.equal(addr.toString(), ZERO_ADDR_S) + st.deepEqual(addr.buf, toBuffer(ZERO_ADDR_S)) + st.equal(addr.toString(), ZERO_ADDR_S) + st.end() }) - it('should instantiate address from zero address string', () => { + t.test('should instantiate address from zero address string', st => { const addr = Address.fromString(ZERO_ADDR_S) - assert.deepEqual(addr.toString(), ZERO_ADDR_S) - assert.equal(addr.isZero(), true) + st.deepEqual(addr.toString(), ZERO_ADDR_S) + st.ok(addr.isZero()) + st.end() }) - it('should detect non-zero address', () => { + t.test('should detect non-zero address', st => { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const addr = Address.fromString(str) - assert.equal(addr.isZero(), false) + st.notOk(addr.isZero()) + st.end() }) - it('should instantiate from public key', () => { + t.test('should instantiate from public key', st => { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', 'hex' ) const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const addr = Address.fromPublicKey(pubKey) - assert.equal(addr.toString(), str) + st.equal(addr.toString(), str) + st.end() }) - it('should fail to instantiate from invalid public key', () => { + t.test('should fail to instantiate from invalid public key', st => { const pubKey = Buffer.from( '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', 'hex' ) - assert.throws(() => Address.fromPublicKey(pubKey)) + st.throws(() => Address.fromPublicKey(pubKey)) + st.end() }) - it('should instantiate from private key', () => { + t.test('should instantiate from private key', st => { // 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) + st.equal(addr.toString(), str) + st.end() }) - it('should generate address for created contract', () => { + t.test('should generate address for created contract', st => { const from = Address.fromString('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') const addr = Address.generate(from, new BN(14)) - assert.equal(addr.toString(), '0xd658a4b8247c14868f3c512fa5cbb6e458e4a989') + st.equal(addr.toString(), '0xd658a4b8247c14868f3c512fa5cbb6e458e4a989') const addr2 = Address.generate(from, new BN(0)) - assert.equal(addr2.toString(), '0xbfa69ba91385206bfdd2d8b9c1a5d6c10097a85b') + st.equal(addr2.toString(), '0xbfa69ba91385206bfdd2d8b9c1a5d6c10097a85b') + st.end() }) - it('should generate address for CREATE2', () => { + t.test('should generate address for CREATE2', st => { 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) + st.equal(addr.toString(), result) } + st.end() }) - it('should provide a buffer that does not mutate the original address', () => { + t.test('should provide a buffer that does not mutate the original address', st => { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const address = Address.fromString(str) const addressBuf = address.toBuffer() addressBuf.fill(0) - assert.equal(address.toString(), str) + st.equal(address.toString(), str) + st.end() }) - it('should compare equality properly', () => { + t.test('should compare equality properly', st => { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const address1 = Address.fromString(str) const address2 = new Address(Buffer.from(str.slice(2), 'hex')) - assert.ok(address1.equals(address2)) - assert.ok(address1.buf.equals(address2.buf)) + st.ok(address1.equals(address2)) + st.ok(address1.buf.equals(address2.buf)) const str2 = '0xcd4EC7b66fbc029C116BA9Ffb3e59351c20B5B06' const address3 = Address.fromString(str2) - assert.ok(!address1.equals(address3)) + st.ok(!address1.equals(address3)) const address3LowerCase = Address.fromString(str2.toLowerCase()) - assert.ok(address3.equals(address3LowerCase)) + st.ok(address3.equals(address3LowerCase)) const address4 = Address.zero() - assert.ok(!address1.equals(address4)) + st.ok(!address1.equals(address4)) + st.end() }) }) diff --git a/test/bytes.spec.ts b/test/bytes.spec.ts index b95c3f90..7ee5cf8e 100644 --- a/test/bytes.spec.ts +++ b/test/bytes.spec.ts @@ -1,7 +1,7 @@ -import assert from 'assert' -import BN from 'bn.js' +import tape from 'tape' import { Address, + BN, zeros, zeroAddress, isZeroAddress, @@ -19,237 +19,271 @@ import { baToJSON } from '../src' -describe('zeros function', function() { - it('should produce lots of 0s', function() { +tape('zeros function', function(t) { + t.test('should produce lots of 0s', function(st) { const z60 = zeros(30) const zs60 = '000000000000000000000000000000000000000000000000000000000000' - assert.equal(z60.toString('hex'), zs60) + st.equal(z60.toString('hex'), zs60) + st.end() }) }) -describe('zero address', function() { - it('should generate a zero address', function() { - assert.equal(zeroAddress(), '0x0000000000000000000000000000000000000000') +tape('zero address', function(t) { + t.test('should generate a zero address', function(st) { + st.equal(zeroAddress(), '0x0000000000000000000000000000000000000000') + st.end() }) }) -describe('is zero address', function() { - it('should return true when a zero address is passed', function() { - assert.equal(isZeroAddress('0x0000000000000000000000000000000000000000'), true) +tape('is zero address', function(t) { + t.test('should return true when a zero address is passed', function(st) { + st.equal(isZeroAddress('0x0000000000000000000000000000000000000000'), true) + st.end() }) - it('should return false when the address is not equal to zero', function() { + t.test('should return false when the address is not equal to zero', function(st) { const nonZeroAddress = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' - assert.equal(isZeroAddress(nonZeroAddress), false) + st.equal(isZeroAddress(nonZeroAddress), false) + st.end() }) - it('should throw when address is not hex-prefixed', function() { - assert.throws(function() { + t.test('should throw when address is not hex-prefixed', function(st) { + st.throws(function() { isZeroAddress('0000000000000000000000000000000000000000') }) + st.end() }) }) -describe('unpadBuffer', function() { - it('should unpad a Buffer', function() { +tape('unpadBuffer', function(t) { + t.test('should unpad a Buffer', function(st) { const buf = toBuffer('0x0000000006600') const r = unpadBuffer(buf) - assert.deepEqual(r, toBuffer('0x6600')) + st.ok(r.equals(toBuffer('0x6600'))) + st.end() }) - it('should throw if input is not a Buffer', function() { - assert.throws(function() { + t.test('should throw if input is not a Buffer', function(st) { + st.throws(function() { unpadBuffer(('0000000006600') as Buffer) }) + st.end() }) }) -describe('unpadArray', function() { - it('should unpad an Array', function() { +tape('unpadArray', function(t) { + t.test('should unpad an Array', function(st) { const arr = [0, 0, 0, 1] const r = unpadArray(arr) - assert.deepEqual(r, [1]) + st.deepEqual(r, [1]) + st.end() }) - it('should throw if input is not an Array', function() { - assert.throws(function() { + t.test('should throw if input is not an Array', function(st) { + st.throws(function() { unpadArray((toBuffer([0, 0, 0, 1])) as number[]) }) + st.end() }) }) -describe('unpadHexString', function() { - it('should unpad a hex prefixed string', function() { +tape('unpadHexString', function(t) { + t.test('should unpad a hex prefixed string', function(st) { const str = '0x0000000006600' const r = unpadHexString(str) - assert.equal(r, '6600') + st.equal(r, '6600') + st.end() }) - it('should throw if input is not hex-prefixed', function() { - assert.throws(function() { + t.test('should throw if input is not hex-prefixed', function(st) { + st.throws(function() { unpadHexString('0000000006600') }) + st.end() }) }) -describe('setLengthLeft', function() { - it('should left pad a Buffer', function() { +tape('setLengthLeft', function(t) { + t.test('should left pad a Buffer', function(st) { const buf = Buffer.from([9, 9]) const padded = setLengthLeft(buf, 3) - assert.equal(padded.toString('hex'), '000909') + st.equal(padded.toString('hex'), '000909') + st.end() }) - it('should left truncate a Buffer', function() { + t.test('should left truncate a Buffer', function(st) { const buf = Buffer.from([9, 0, 9]) const padded = setLengthLeft(buf, 2) - assert.equal(padded.toString('hex'), '0009') + st.equal(padded.toString('hex'), '0009') + st.end() }) - it('should throw if input is not a Buffer', function() { - assert.throws(function() { + t.test('should throw if input is not a Buffer', function(st) { + st.throws(function() { setLengthLeft(([9, 9]) as Buffer, 3) }) + st.end() }) }) -describe('setLengthRight', function() { - it('should right pad a Buffer', function() { +tape('setLengthRight', function(t) { + t.test('should right pad a Buffer', function(st) { const buf = Buffer.from([9, 9]) const padded = setLengthRight(buf, 3) - assert.equal(padded.toString('hex'), '090900') + st.equal(padded.toString('hex'), '090900') + st.end() }) - it('should right truncate a Buffer', function() { + t.test('should right truncate a Buffer', function(st) { const buf = Buffer.from([9, 0, 9]) const padded = setLengthRight(buf, 2) - assert.equal(padded.toString('hex'), '0900') + st.equal(padded.toString('hex'), '0900') + st.end() }) - it('should throw if input is not a Buffer', function() { - assert.throws(function() { + t.test('should throw if input is not a Buffer', function(st) { + st.throws(function() { setLengthRight(([9, 9]) as Buffer, 3) }) + st.end() }) }) -describe('bufferToHex', function() { - it('should convert a buffer to hex', function() { +tape('bufferToHex', function(t) { + t.test('should convert a buffer to hex', function(st) { const buf = Buffer.from('5b9ac8', 'hex') const hex = bufferToHex(buf) - assert.equal(hex, '0x5b9ac8') + st.equal(hex, '0x5b9ac8') + st.end() }) - it('empty buffer', function() { + t.test('empty buffer', function(st) { const buf = Buffer.alloc(0) const hex = bufferToHex(buf) - assert.strictEqual(hex, '0x') + st.strictEqual(hex, '0x') + st.end() }) }) -describe('bufferToInt', function() { - it('should convert a int to hex', function() { +tape('bufferToInt', function(t) { + t.test('should convert an int to hex', function(st) { const buf = Buffer.from('5b9ac8', 'hex') const i = bufferToInt(buf) - assert.equal(i, 6003400) - assert.equal(bufferToInt(Buffer.allocUnsafe(0)), 0) + st.equal(i, 6003400) + st.equal(bufferToInt(Buffer.allocUnsafe(0)), 0) + st.end() }) - it('should convert empty input to 0', function() { - assert.equal(bufferToInt(Buffer.allocUnsafe(0)), 0) + t.test('should convert empty input to 0', function(st) { + st.equal(bufferToInt(Buffer.allocUnsafe(0)), 0) + st.end() }) }) -describe('fromSigned', function() { - it('should convert an unsigned (negative) buffer to a singed number', function() { +tape('fromSigned', function(t) { + t.test('should convert an unsigned (negative) buffer to a signed number', function(st) { const neg = '-452312848583266388373324160190187140051835877600158453279131187530910662656' const buf = Buffer.allocUnsafe(32).fill(0) buf[0] = 255 - assert.equal(fromSigned(buf), neg) + st.equal(fromSigned(buf).toString(), neg) + st.end() }) - it('should convert an unsigned (positive) buffer to a singed number', function() { + t.test('should convert an unsigned (positive) buffer to a signed number', function(st) { const neg = '452312848583266388373324160190187140051835877600158453279131187530910662656' const buf = Buffer.allocUnsafe(32).fill(0) buf[0] = 1 - assert.equal(fromSigned(buf), neg) + st.equal(fromSigned(buf).toString(), neg) + st.end() }) }) -describe('toUnsigned', function() { - it('should convert a signed (negative) number to unsigned', function() { +tape('toUnsigned', function(t) { + t.test('should convert a signed (negative) number to unsigned', function(st) { const neg = '-452312848583266388373324160190187140051835877600158453279131187530910662656' const hex = 'ff00000000000000000000000000000000000000000000000000000000000000' const num = new BN(neg) - assert.equal(toUnsigned(num).toString('hex'), hex) + st.equal(toUnsigned(num).toString('hex'), hex) + st.end() }) - it('should convert a signed (positive) number to unsigned', function() { + t.test('should convert a signed (positive) number to unsigned', function(st) { const neg = '452312848583266388373324160190187140051835877600158453279131187530910662656' const hex = '0100000000000000000000000000000000000000000000000000000000000000' const num = new BN(neg) - assert.equal(toUnsigned(num).toString('hex'), hex) + st.equal(toUnsigned(num).toString('hex'), hex) + st.end() }) }) -describe('hex prefix', function() { +tape('hex prefix', function(t) { const string = 'd658a4b8247c14868f3c512fa5cbb6e458e4a989' - it('should add', function() { - assert.equal(addHexPrefix(string), '0x' + string) + t.test('should add', function(st) { + st.equal(addHexPrefix(string), '0x' + string) + st.end() }) - it('should return on non-string input', function() { - assert.equal(addHexPrefix(1 as any), 1) + t.test('should return on non-string input', function(st) { + st.equal(addHexPrefix(1 as any), 1) + st.end() }) }) -describe('toBuffer', function() { - it('should work', function() { +tape('toBuffer', function(t) { + t.test('should work', function(st) { // Buffer - assert.deepEqual(toBuffer(Buffer.allocUnsafe(0)), Buffer.allocUnsafe(0)) + st.ok(toBuffer(Buffer.allocUnsafe(0)).equals(Buffer.allocUnsafe(0))) // Array - assert.deepEqual(toBuffer([]), Buffer.allocUnsafe(0)) + st.ok(toBuffer([]).equals(Buffer.allocUnsafe(0))) // String - assert.deepEqual(toBuffer('0x11'), Buffer.from([17])) - assert.deepEqual(toBuffer('0x1234').toString('hex'), '1234') - assert.deepEqual(toBuffer('0x'), Buffer.from([])) + st.ok(toBuffer('0x11').equals(Buffer.from([17]))) + st.equal(toBuffer('0x1234').toString('hex'), '1234') + st.ok(toBuffer('0x').equals(Buffer.from([]))) // Number - assert.deepEqual(toBuffer(1), Buffer.from([1])) + st.ok(toBuffer(1).equals(Buffer.from([1]))) // null - assert.deepEqual(toBuffer(null), Buffer.allocUnsafe(0)) + st.ok(toBuffer(null).equals(Buffer.allocUnsafe(0))) // undefined - assert.deepEqual(toBuffer(undefined), Buffer.allocUnsafe(0)) + st.ok(toBuffer(undefined).equals(Buffer.allocUnsafe(0))) // 'toBN' - assert.deepEqual(toBuffer(new BN(1)), Buffer.from([1])) + st.ok(toBuffer(new BN(1)).equals(Buffer.from([1]))) // 'toArray' - assert.deepEqual( + st.ok( toBuffer({ toArray: function(): any { return [1] } - }), - Buffer.from([1]) + }).equals(Buffer.from([1])) ) + st.end() }) - it('should fail', function() { - assert.throws(function() { - // @ts-ignore - toBuffer({ test: 1 }) + t.test('should fail', function(st) { + st.throws(function() { + toBuffer({ test: 1 } as any) }) + st.end() }) - it('should fail with non 0x-prefixed hex strings', function() { - assert.throws(() => toBuffer('11'), '11') - assert.throws(() => toBuffer('')) - assert.throws(() => toBuffer('0xR'), '0xR') + t.test('should fail with non 0x-prefixed hex strings', function(st) { + st.throws(() => toBuffer('11'), '11') + st.throws(() => toBuffer('')) + st.throws(() => toBuffer('0xR'), '0xR') + st.end() }) - it('should convert a TransformableToBuffer like the Address class (i.e. provides a toBuffer method)', function() { - const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' - const address = Address.fromString(str) - const addressBuf = toBuffer(address) - assert.ok(addressBuf.equals(address.toBuffer())) - }) + t.test( + 'should convert a TransformableToBuffer like the Address class (i.e. provides a toBuffer method)', + function(st) { + const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' + const address = Address.fromString(str) + const addressBuf = toBuffer(address) + st.ok(addressBuf.equals(address.toBuffer())) + st.end() + } + ) }) -describe('baToJSON', function() { - it('should turn a array of buffers into a pure json object', function() { +tape('baToJSON', function(t) { + t.test('should turn a array of buffers into a pure json object', function(st) { const ba = [Buffer.from([0]), Buffer.from([1]), [Buffer.from([2])]] - assert.deepEqual(baToJSON(ba), ['0x00', '0x01', ['0x02']]) + st.deepEqual(baToJSON(ba), ['0x00', '0x01', ['0x02']]) + st.end() }) - it('should turn a buffers into string', function() { - assert.deepEqual(baToJSON(Buffer.from([0])), '0x00') + t.test('should turn a buffers into string', function(st) { + st.deepEqual(baToJSON(Buffer.from([0])), '0x00') + st.end() }) }) diff --git a/test/constants.spec.ts b/test/constants.spec.ts index c985c2b9..c8dd28b7 100644 --- a/test/constants.spec.ts +++ b/test/constants.spec.ts @@ -1,4 +1,4 @@ -import assert from 'assert' +import tape from 'tape' import { MAX_INTEGER, TWO_POW256, @@ -10,46 +10,42 @@ import { KECCAK256_RLP } from '../src' -describe('constants', function() { - it('should match constants', function() { - assert.equal( +tape('constants', function(t) { + t.test('should match constants', function(st) { + st.equal( MAX_INTEGER.toString('hex'), 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' ) - assert.equal( + st.equal( TWO_POW256.toString('hex'), '10000000000000000000000000000000000000000000000000000000000000000' ) - assert.equal( - KECCAK256_NULL_S, - 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' - ) + st.equal(KECCAK256_NULL_S, 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') - assert.equal( + st.equal( KECCAK256_NULL.toString('hex'), 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) - assert.equal( + st.equal( KECCAK256_RLP_ARRAY_S, '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' ) - assert.equal( + st.equal( KECCAK256_RLP_ARRAY.toString('hex'), '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' ) - assert.equal( - KECCAK256_RLP_S, - '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' - ) + st.equal(KECCAK256_RLP_S, '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421') - assert.equal( + st.equal( KECCAK256_RLP.toString('hex'), '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) + + st.end() }) }) diff --git a/test/externals.spec.ts b/test/externals.spec.ts index b6b25130..37fbad46 100644 --- a/test/externals.spec.ts +++ b/test/externals.spec.ts @@ -1,54 +1,61 @@ -import assert from 'assert' - +import tape from 'tape' import BN_export from 'bn.js' import * as rlp_export from 'rlp' - import * as src from '../src' -describe('External BN export', () => { - it('should export `BN`', () => { - assert.equal(src.BN, BN_export) +tape('External BN export', t => { + t.test('should export `BN`', st => { + st.equal(src.BN, BN_export) + st.end() }) - it('should use a BN function correctly', () => { + t.test('should use a BN function correctly', st => { const a = new src.BN('dead', 16) const b = new src.BN('101010', 2) const result = a.add(b) - assert.equal(result.toString(10), 57047) + st.equal(result.toString(10), '57047') + st.end() }) - it('should throw on exceptions', () => { + t.test('should throw on exceptions', st => { // should not allow 0 input - assert.throws(() => { + st.throws(() => { new src.BN(1).egcd(new src.BN('0')) }, /^Error: Assertion failed$/) + st.end() }) - // should not accept an unsafe integer - const num = Math.pow(2, 53) - assert.throws(() => { - return new src.BN(num, 10) - }, /^Error: Assertion failed$/) + t.test('should not accept an unsafe integer', st => { + const num = Math.pow(2, 53) + st.throws(() => { + return new src.BN(num, 10) + }, /^Error: Assertion failed$/) + st.end() + }) - // should throw error with num eq 0x4000000 - assert.throws(function() { - new src.BN(0).iaddn(0x4000000) - }, /^Error: Assertion failed$/) + t.test('should throw error with num eq 0x4000000', st => { + st.throws(function() { + new src.BN(0).iaddn(0x4000000) + }, /^Error: Assertion failed$/) + st.end() + }) }) -describe('External rlp export', () => { - it('should export `rlp`', () => { - assert.equal(src.rlp, rlp_export) +tape('External rlp export', t => { + t.test('should export `rlp`', st => { + st.equal(src.rlp, rlp_export) + st.end() }) - it('should use a rlp function correctly', () => { + t.test('should use a rlp function correctly', st => { const nestedList = [[], [[]], [[], [[]]]] const encoded = src.rlp.encode(nestedList) const decoded = src.rlp.decode(encoded) - assert.deepEqual(nestedList, decoded) + st.deepEqual(nestedList, decoded) + st.end() }) - it('should throw on exceptions', () => { + t.test('should throw on exceptions', st => { // bad values: wrong encoded a zero const val = Buffer.from( 'f9005f030182520894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3', @@ -60,7 +67,7 @@ describe('External rlp export', () => { } catch (e) { // pass } - assert.equal(result, undefined) + st.equal(result, undefined) // bad values: invalid length const a = Buffer.from( @@ -74,12 +81,13 @@ describe('External rlp export', () => { } catch (e) { // pass } - assert.equal(res, undefined) + st.equal(res, undefined) + st.end() }) }) -describe('External ethjsUtil export', () => { - it('should have all ethjsUtil methods', () => { +tape('External ethjsUtil export', t => { + t.test('should have all ethjsUtil methods', st => { const expected = [ 'arrayContainsArray', 'toBuffer', @@ -98,30 +106,33 @@ describe('External ethjsUtil export', () => { ] expected.forEach(prop => { - assert.ok(prop in src) + st.ok(prop in src) }) + st.end() }) - it('should use ethjsUtil functions correctly', () => { + t.test('should use ethjsUtil functions correctly', st => { // should convert intToHex - assert.equal(src.intToHex(new src.BN(0).toNumber()), '0x0') + st.equal(src.intToHex(new src.BN(0).toNumber()), '0x0') // should convert intToHex const i = 6003400 const hex = src.intToHex(i) - assert.equal(hex, '0x5b9ac8') + st.equal(hex, '0x5b9ac8') // should convert a int to a buffer const j = 6003400 const buf = src.intToBuffer(j) - assert.equal(buf.toString('hex'), '5b9ac8') + st.equal(buf.toString('hex'), '5b9ac8') + st.end() }) - it('should handle exceptions and invalid inputs', () => { + t.test('should handle exceptions and invalid inputs', st => { // should throw when invalid abi - assert.throws(() => src.getKeys([], (3289) as string), Error) + st.throws(() => src.getKeys([], (3289) as string), Error) // should detect invalid length hex string - assert.equal(src.isHexString('0x0', 2), false) + st.equal(src.isHexString('0x0', 2), false) + st.end() }) }) diff --git a/test/hash.spec.ts b/test/hash.spec.ts index e9a3fadf..9e28cb78 100644 --- a/test/hash.spec.ts +++ b/test/hash.spec.ts @@ -1,4 +1,4 @@ -import assert from 'assert' +import tape from 'tape' import { keccak, keccak256, @@ -15,236 +15,267 @@ import { toBuffer } from '../src' -describe('keccak', function() { - it('should produce a keccak224 hash', function() { +tape('keccak', function(t) { + t.test('should produce a keccak224 hash', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '9e66938bd8f32c8610444bb524630db496bd58b689f9733182df63ba' const hash = keccak(toBuffer(msg), 224) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should produce a keccak256 hash', function() { + t.test('should produce a keccak256 hash', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28' const hash = keccak(toBuffer(msg)) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should produce a keccak384 hash', function() { + t.test('should produce a keccak384 hash', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '923e0f6a1c324a698139c3f3abbe88ac70bf2e7c02b26192c6124732555a32cef18e81ac91d5d97ce969745409c5bbc6' const hash = keccak(toBuffer(msg), 384) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should produce a keccak512 hash', function() { + t.test('should produce a keccak512 hash', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '36fdacd0339307068e9ed191773a6f11f6f9f99016bd50f87fd529ab7c87e1385f2b7ef1ac257cc78a12dcb3e5804254c6a7b404a6484966b831eadc721c3d24' const hash = keccak(toBuffer(msg), 512) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should error if input is not Buffer', function() { + t.test('should error if input is not Buffer', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' - assert.throws(function() { + st.throws(function() { keccak((msg) as Buffer) }) + st.end() }) - it('should error if provided incorrect bits', function() { + t.test('should error if provided incorrect bits', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' - assert.throws(function() { + st.throws(function() { keccak(toBuffer(msg), 1024) }) + st.end() }) }) -describe('keccak256', function() { - it('should produce a hash (keccak(a, 256) alias)', function() { +tape('keccak256', function(t) { + t.test('should produce a hash (keccak(a, 256) alias)', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28' const hash = keccak256(toBuffer(msg)) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) }) -describe('keccakFromString', function() { - it('should produce a hash', function() { +tape('keccakFromString', function(t) { + t.test('should produce a hash', function(st) { const msg = '3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '22ae1937ff93ec72c4d46ff3e854661e3363440acd6f6e4adf8f1a8978382251' const hash = keccakFromString(msg) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should throw if input is not a string', function() { + t.test('should throw if input is not a string', function(st) { const buf = toBuffer('0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1') - assert.throws(function() { + st.throws(function() { keccakFromString((buf) as string) }) + st.end() }) }) -describe('keccakFromHexString', function() { - it('should produce a hash', function() { +tape('keccakFromHexString', function(t) { + t.test('should produce a hash', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28' const hash = keccakFromHexString(msg) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should throw if input is not hex-prefixed', function() { + t.test('should throw if input is not hex-prefixed', function(st) { const msg = '3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' - assert.throws(function() { + st.throws(function() { keccakFromHexString(msg) }) + st.end() }) - it('should throw if input is not a string', function() { + t.test('should throw if input is not a string', function(st) { const buf = toBuffer('0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1') - assert.throws(function() { + st.throws(function() { keccakFromHexString((buf) as string) }) + st.end() }) }) -describe('keccakFromArray', function() { - it('should produce a hash', function() { +tape('keccakFromArray', function(t) { + t.test('should produce a hash', function(st) { const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 0] const r = 'fba8669bd39e3257e64752758f3a0d3218865a15757c6b0bc48b8ef95bc8bfd5' const hash = keccakFromArray(arr) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should throw if input is not an array', function() { + t.test('should throw if input is not an array', function(st) { const buf = toBuffer([0, 1, 2, 3, 4, 5, 6, 7, 8, 0]) - assert.throws(function() { + st.throws(function() { keccakFromArray((buf) as number[]) }) + st.end() }) }) -describe('keccak-512', function() { - it('should produce a hash', function() { +tape('keccak-512', function(t) { + t.test('should produce a hash', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '36fdacd0339307068e9ed191773a6f11f6f9f99016bd50f87fd529ab7c87e1385f2b7ef1ac257cc78a12dcb3e5804254c6a7b404a6484966b831eadc721c3d24' const hash = keccak(toBuffer(msg), 512) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) }) -describe('sha256', function() { - it('should produce a sha256', function() { +tape('sha256', function(t) { + t.test('should produce a sha256', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '58bbda5e10bc11a32d808e40f9da2161a64f00b5557762a161626afe19137445' const hash = sha256(toBuffer(msg)) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should error if input is not Buffer', function() { + t.test('should error if input is not Buffer', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' - assert.throws(function() { + st.throws(function() { sha256((msg) as Buffer) }) + st.end() }) }) -describe('sha256FromString', function() { - it('should produce a sha256', function() { +tape('sha256FromString', function(t) { + t.test('should produce a sha256', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '58bbda5e10bc11a32d808e40f9da2161a64f00b5557762a161626afe19137445' const hash = sha256FromString(msg) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should error if input is not Buffer', function() { + t.test('should error if input is not Buffer', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' - assert.throws(function() { + st.throws(function() { sha256FromString((toBuffer(msg)) as string) }) + st.end() }) }) -describe('sha256FromArray', function() { - it('should produce a sha256', function() { +tape('sha256FromArray', function(t) { + t.test('should produce a sha256', function(st) { const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 0] const r = '5443c487d45d01c56150d91e7a071c69a97939b1c57874b73989a9ff7875e86b' const hash = sha256FromArray(arr) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should error if input is not Buffer', function() { + t.test('should error if input is not Buffer', function(st) { const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 0] - assert.throws(function() { + st.throws(function() { sha256FromArray((toBuffer(arr)) as number[]) }) + st.end() }) }) -describe('ripemd160', function() { - it('should produce a ripemd160', function() { +tape('ripemd160', function(t) { + t.test('should produce a ripemd160', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '4bb0246cbfdfddbe605a374f1187204c896fabfd' const hash = ripemd160(toBuffer(msg), false) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should produce a padded ripemd160', function() { + t.test('should produce a padded ripemd160', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '0000000000000000000000004bb0246cbfdfddbe605a374f1187204c896fabfd' const hash = ripemd160(toBuffer(msg), true) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should error if input is not Buffer', function() { + t.test('should error if input is not Buffer', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' - assert.throws(function() { + st.throws(function() { ripemd160((msg) as Buffer, false) }) + st.end() }) }) -describe('ripemd160FromString', function() { - it('should produce a ripemd160', function() { +tape('ripemd160FromString', function(t) { + t.test('should produce a ripemd160', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '4bb0246cbfdfddbe605a374f1187204c896fabfd' const hash = ripemd160FromString(msg, false) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should produce a padded ripemd160', function() { + t.test('should produce a padded ripemd160', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '0000000000000000000000004bb0246cbfdfddbe605a374f1187204c896fabfd' const hash = ripemd160FromString(msg, true) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should error if input is not a string', function() { + t.test('should error if input is not a string', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' - assert.throws(function() { + st.throws(function() { ripemd160FromString((toBuffer(msg)) as string, false) }) + st.end() }) }) -describe('ripemd160FromArray', function() { - it('should produce a ripemd160', function() { +tape('ripemd160FromArray', function(t) { + t.test('should produce a ripemd160', function(st) { const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 0] const r = 'ddbb5062318b209e3dbfc389fe61840363050071' const hash = ripemd160FromArray(arr, false) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should produce a padded ripemd160', function() { + t.test('should produce a padded ripemd160', function(st) { const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 0] const r = '000000000000000000000000ddbb5062318b209e3dbfc389fe61840363050071' const hash = ripemd160FromArray(arr, true) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) - it('should error if input is not an array', function() { + t.test('should error if input is not an array', function(st) { const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 0] - assert.throws(function() { + st.throws(function() { ripemd160FromArray((toBuffer(arr)) as number[], false) }) + st.end() }) }) -describe('rlphash', function() { - it('should produce a keccak-256 hash of the rlp data', function() { +tape('rlphash', function(t) { + t.test('should produce a keccak-256 hash of the rlp data', function(st) { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '33f491f24abdbdbf175e812b94e7ede338d1c7f01efb68574acd279a15a39cbe' const hash = rlphash(msg) - assert.equal(hash.toString('hex'), r) + st.equal(hash.toString('hex'), r) + st.end() }) }) diff --git a/test/object.spec.ts b/test/object.spec.ts index 5e700910..e99ad11b 100644 --- a/test/object.spec.ts +++ b/test/object.spec.ts @@ -1,7 +1,7 @@ -import assert from 'assert' +import tape from 'tape' import { zeros, defineProperties } from '../src' -describe('define', function() { +tape('define', function(t) { const fields = [ { name: 'aword', @@ -32,22 +32,23 @@ describe('define', function() { } ] - it('should trim zeros', function() { + t.test('should trim zeros', function(st) { const someOb: any = {} defineProperties(someOb, fields) // Define Properties someOb.r = '0x00004' - assert.equal(someOb.r.toString('hex'), '04') + st.equal(someOb.r.toString('hex'), '04') someOb.r = Buffer.from([0, 0, 0, 0, 4]) - assert.equal(someOb.r.toString('hex'), '04') + st.equal(someOb.r.toString('hex'), '04') + st.end() }) - it("shouldn't allow wrong size for exact size requirements", function() { + t.test("shouldn't allow wrong size for exact size requirements", function(st) { const someOb = {} defineProperties(someOb, fields) - assert.throws(function() { + st.throws(function() { const tmp = [ { name: 'mustBeExactSize', @@ -58,9 +59,10 @@ describe('define', function() { ] defineProperties(someOb, tmp) }) + st.end() }) - it('it should accept rlp encoded intial data', function() { + t.test('it should accept rlp encoded intial data', function(st) { const someOb: any = {} const data = { aword: '0x01', @@ -80,12 +82,12 @@ describe('define', function() { const expectedArray = ['0x01', '0x', '0x02', '0x03', '0x04'] defineProperties(someOb, fields, data) - assert.deepEqual(someOb.toJSON(true), expected, 'should produce the correctly labeled object') + st.deepEqual(someOb.toJSON(true), expected, 'should produce the correctly labeled object') const someOb2: any = {} const rlpEncoded = someOb.serialize().toString('hex') defineProperties(someOb2, fields, rlpEncoded) - assert.equal( + st.equal( someOb2.serialize().toString('hex'), rlpEncoded, 'the constuctor should accept rlp encoded buffers' @@ -93,21 +95,23 @@ describe('define', function() { const someOb3 = {} defineProperties(someOb3, fields, expectedArray) - assert.deepEqual(someOb.toJSON(), expectedArray, 'should produce the correctly object') + st.deepEqual(someOb.toJSON(), expectedArray, 'should produce the correctly object') + st.end() }) - it('it should not accept invalid values in the constuctor', function() { + t.test('it should not accept invalid values in the constuctor', function(st) { const someOb = {} - assert.throws(function() { + st.throws(function() { defineProperties(someOb, fields, 5) }, 'should throw on nonsensical data') - assert.throws(function() { + st.throws(function() { defineProperties(someOb, fields, Array(6)) }, 'should throw on invalid arrays') + st.end() }) - it('alias should work ', function() { + t.test('alias should work ', function(st) { const someOb: any = {} const data = { aword: '0x01', @@ -117,18 +121,20 @@ describe('define', function() { } defineProperties(someOb, fields, data) - assert.equal(someOb.blah.toString('hex'), '01') + st.equal(someOb.blah.toString('hex'), '01') someOb.blah = '0x09' - assert.equal(someOb.blah.toString('hex'), '09') - assert.equal(someOb.aword.toString('hex'), '09') + st.equal(someOb.blah.toString('hex'), '09') + st.equal(someOb.aword.toString('hex'), '09') + st.end() }) - it('alias should work #2', function() { + t.test('alias should work #2', function(st) { const someOb: any = {} const data = { blah: '0x1' } defineProperties(someOb, fields, data) - assert.equal(someOb.blah.toString('hex'), '01') - assert.equal(someOb.aword.toString('hex'), '01') + st.equal(someOb.blah.toString('hex'), '01') + st.equal(someOb.aword.toString('hex'), '01') + st.end() }) }) diff --git a/test/signature.spec.ts b/test/signature.spec.ts index 09dd25d8..302d0097 100644 --- a/test/signature.spec.ts +++ b/test/signature.spec.ts @@ -1,6 +1,6 @@ -import assert from 'assert' -import BN from 'bn.js' +import tape from 'tape' import { + BN, ecsign, ecrecover, privateToPublic, @@ -21,34 +21,40 @@ const ecprivkey = Buffer.from( ) const chainId = 3 // ropsten -describe('ecsign', function() { - it('should produce a signature', function() { +tape('ecsign', function(t) { + t.test('should produce a signature', function(st) { const sig = ecsign(echash, ecprivkey) - assert.deepEqual( - sig.r, - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') + st.ok( + sig.r.equals( + Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') + ) ) - assert.deepEqual( - sig.s, - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + st.ok( + sig.s.equals( + Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + ) ) - assert.equal(sig.v, 27) + st.equal(sig.v, 27) + st.end() }) - it('should produce a signature for Ropsten testnet', function() { + t.test('should produce a signature for Ropsten testnet', function(st) { const sig = ecsign(echash, ecprivkey, chainId) - assert.deepEqual( - sig.r, - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') + st.ok( + sig.r.equals( + Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') + ) ) - assert.deepEqual( - sig.s, - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + st.ok( + sig.s.equals( + Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + ) ) - assert.equal(sig.v, 41) + st.equal(sig.v, 41) + st.end() }) - it('should produce a signature for chainId=150', function() { + t.test('should produce a signature for chainId=150', function(st) { const expectedSigR = Buffer.from( '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex' @@ -60,103 +66,114 @@ describe('ecsign', function() { const expectedSigV = Buffer.from('014f', 'hex') const sig = ecsign(echash, ecprivkey, 150) - assert.deepEqual(sig.r, expectedSigR) - assert.deepEqual(sig.s, expectedSigS) - assert.equal(sig.v, 150 * 2 + 35) + st.ok(sig.r.equals(expectedSigR)) + st.ok(sig.s.equals(expectedSigS)) + st.equal(sig.v, 150 * 2 + 35) let sigBuffer = ecsign(echash, ecprivkey, new BN(150)) - assert.deepEqual(sigBuffer.r, expectedSigR) - assert.deepEqual(sigBuffer.s, expectedSigS) - assert.deepEqual(sigBuffer.v, expectedSigV) + st.ok(sigBuffer.r.equals(expectedSigR)) + st.ok(sigBuffer.s.equals(expectedSigS)) + st.ok(sigBuffer.v.equals(expectedSigV)) sigBuffer = ecsign(echash, ecprivkey, Buffer.from([150])) - assert.deepEqual(sigBuffer.v, expectedSigV) + st.ok(sigBuffer.v.equals(expectedSigV)) sigBuffer = ecsign(echash, ecprivkey, '0x96') - assert.deepEqual(sigBuffer.v, expectedSigV) + st.ok(sigBuffer.v.equals(expectedSigV)) - assert.throws(function() { + st.throws(function() { ecsign(echash, ecprivkey, '96') }) + st.end() }) -}) -it('should produce a signature for a high number chainId greater than MAX_SAFE_INTEGER', function() { - const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') - const expectedSigR = Buffer.from( - '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', - 'hex' - ) - const expectedSigS = Buffer.from( - '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', - 'hex' + t.test( + 'should produce a signature for a high number chainId greater than MAX_SAFE_INTEGER', + function(st) { + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + const expectedSigR = Buffer.from( + '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', + 'hex' + ) + const expectedSigS = Buffer.from( + '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', + 'hex' + ) + const expectedSigV = Buffer.from('f2ded8deec6713', 'hex') + + let sigBuffer = ecsign(echash, ecprivkey, new BN(chainIDBuffer)) + st.ok(sigBuffer.r.equals(expectedSigR)) + st.ok(sigBuffer.s.equals(expectedSigS)) + st.ok(sigBuffer.v.equals(expectedSigV)) + + sigBuffer = ecsign(echash, ecprivkey, chainIDBuffer) + st.ok(sigBuffer.v.equals(expectedSigV)) + + sigBuffer = ecsign(echash, ecprivkey, '0x' + chainIDBuffer.toString('hex')) + st.ok(sigBuffer.v.equals(expectedSigV)) + + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + st.throws(() => { + // If we would use a number for the `chainId` parameter then it should throw. + // (The numbers are too high to perform arithmetic on) + ecsign(echash, ecprivkey, chainIDNumber) + }) + st.end() + } ) - const expectedSigV = Buffer.from('f2ded8deec6713', 'hex') - - let sigBuffer = ecsign(echash, ecprivkey, new BN(chainIDBuffer)) - assert.deepEqual(sigBuffer.r, expectedSigR) - assert.deepEqual(sigBuffer.s, expectedSigS) - assert.deepEqual(sigBuffer.v, expectedSigV) - - sigBuffer = ecsign(echash, ecprivkey, chainIDBuffer) - assert.deepEqual(sigBuffer.v, expectedSigV) - - sigBuffer = ecsign(echash, ecprivkey, '0x' + chainIDBuffer.toString('hex')) - assert.deepEqual(sigBuffer.v, expectedSigV) - - const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) - assert.throws(() => { - // If we would use a number for the `chainId` parameter then it should throw. - // (The numbers are too high to perform arithmetic on) - ecsign(echash, ecprivkey, chainIDNumber) - }) }) -describe('ecrecover', function() { - it('should recover a public key', function() { +tape('ecrecover', function(t) { + t.test('should recover a public key', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') const v = 27 const pubkey = ecrecover(echash, v, r, s) - assert.deepEqual(pubkey, privateToPublic(ecprivkey)) + st.ok(pubkey.equals(privateToPublic(ecprivkey))) + st.end() }) - it('should recover a public key (chainId = 3)', function() { + t.test('should recover a public key (chainId = 3)', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') const v = 41 const pubkey = ecrecover(echash, v, r, s, chainId) - assert.deepEqual(pubkey, privateToPublic(ecprivkey)) + st.ok(pubkey.equals(privateToPublic(ecprivkey))) + st.end() }) - it('should recover a public key (chainId = 150)', function() { + t.test('should recover a public key (chainId = 150)', function(st) { const chainId = 150 const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') const v = chainId * 2 + 35 const pubkey = ecrecover(echash, v, r, s, chainId) - assert.deepEqual(pubkey, privateToPublic(ecprivkey)) + st.ok(pubkey.equals(privateToPublic(ecprivkey))) + st.end() }) - it('should fail on an invalid signature (v = 21)', function() { + t.test('should fail on an invalid signature (v = 21)', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - assert.throws(function() { + st.throws(function() { ecrecover(echash, 21, r, s) }) + st.end() }) - it('should fail on an invalid signature (v = 29)', function() { + t.test('should fail on an invalid signature (v = 29)', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - assert.throws(function() { + st.throws(function() { ecrecover(echash, 29, r, s) }) + st.end() }) - it('should fail on an invalid signature (swapped points)', function() { + t.test('should fail on an invalid signature (swapped points)', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - assert.throws(function() { + st.throws(function() { ecrecover(echash, 27, s, r) }) + st.end() }) - it('should return the right sender when using very high chain id / v values', function() { + t.test('should return the right sender when using very high chain id / v values', function(st) { // This data is from a transaction of the YoloV3 network, block 77, txhash c6121a23ca17b8ff70d4706c7d134920c1da43c8329444c96b4c63a55af1c760 /* { @@ -186,74 +203,82 @@ describe('ecrecover', function() { const vBuffer = Buffer.from('f2ded8deec6714', 'hex') const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') let sender = ecrecover(msgHash, vBuffer, r, s, chainIDBuffer) - assert.ok(sender.equals(senderPubKey), 'sender pubkey correct (Buffer)') + st.ok(sender.equals(senderPubKey), 'sender pubkey correct (Buffer)') const vBN = new BN(vBuffer) const chainIDBN = new BN(chainIDBuffer) sender = ecrecover(msgHash, vBN, r, s, chainIDBN) - assert.ok(sender.equals(senderPubKey), 'sender pubkey correct (BN)') + st.ok(sender.equals(senderPubKey), 'sender pubkey correct (BN)') const vHexString = '0xf2ded8deec6714' const chainIDHexString = '0x796f6c6f763378' sender = ecrecover(msgHash, vHexString, r, s, chainIDHexString) - assert.ok(sender.equals(senderPubKey), 'sender pubkey correct (HexString)') + st.ok(sender.equals(senderPubKey), 'sender pubkey correct (HexString)') - assert.throws(function() { + st.throws(function() { ecrecover(msgHash, 'f2ded8deec6714', r, s, chainIDHexString) }) - assert.throws(function() { + st.throws(function() { ecrecover(msgHash, vHexString, r, s, '796f6c6f763378') }) const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) const vNumber = parseInt(vBuffer.toString('hex'), 16) - assert.throws(() => { + st.throws(() => { // If we would use numbers for the `v` and `chainId` parameters, then it should throw. // (The numbers are too high to perform arithmetic on) ecrecover(msgHash, vNumber, r, s, chainIDNumber) }) + st.end() }) }) -describe('hashPersonalMessage', function() { - it('should produce a deterministic hash', function() { +tape('hashPersonalMessage', function(t) { + t.test('should produce a deterministic hash', function(st) { const h = hashPersonalMessage(Buffer.from('Hello world')) - assert.deepEqual( - h, - Buffer.from('8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede', 'hex') + st.ok( + h.equals( + Buffer.from('8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede', 'hex') + ) ) + st.end() }) - it('should throw if input is not a buffer', function() { + t.test('should throw if input is not a buffer', function(st) { try { hashPersonalMessage(([0, 1, 2, 3, 4]) as Buffer) } catch (err) { - assert(err.message.includes('This method only supports Buffer')) + st.ok(err.message.includes('This method only supports Buffer')) } + st.end() }) }) -describe('isValidSignature', function() { - it('should fail on an invalid signature (shorter r))', function() { +tape('isValidSignature', function(t) { + t.test('should fail on an invalid signature (shorter r))', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1ab', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - assert.equal(isValidSignature(27, r, s), false) + st.notOk(isValidSignature(27, r, s)) + st.end() }) - it('should fail on an invalid signature (shorter s))', function() { + t.test('should fail on an invalid signature (shorter s))', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca', 'hex') - assert.equal(isValidSignature(27, r, s), false) + st.notOk(isValidSignature(27, r, s)) + st.end() }) - it('should fail on an invalid signature (v = 21)', function() { + t.test('should fail on an invalid signature (v = 21)', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - assert.equal(isValidSignature(21, r, s), false) + st.notOk(isValidSignature(21, r, s)) + st.end() }) - it('should fail on an invalid signature (v = 29)', function() { + t.test('should fail on an invalid signature (v = 29)', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - assert.equal(isValidSignature(29, r, s), false) + st.notOk(isValidSignature(29, r, s)) + st.end() }) - it('should fail when on homestead and s > secp256k1n/2', function() { + t.test('should fail when on homestead and s > secp256k1n/2', function(st) { const SECP256K1_N_DIV_2 = new BN( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16 @@ -263,9 +288,10 @@ describe('isValidSignature', function() { const s = Buffer.from(SECP256K1_N_DIV_2.add(new BN('1', 16)).toString(16), 'hex') const v = 27 - assert.equal(isValidSignature(v, r, s, true), false) + st.notOk(isValidSignature(v, r, s, true)) + st.end() }) - it('should not fail when not on homestead but s > secp256k1n/2', function() { + t.test('should not fail when not on homestead but s > secp256k1n/2', function(st) { const SECP256K1_N_DIV_2 = new BN( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16 @@ -275,93 +301,97 @@ describe('isValidSignature', function() { const s = Buffer.from(SECP256K1_N_DIV_2.add(new BN('1', 16)).toString(16), 'hex') const v = 27 - assert.equal(isValidSignature(v, r, s, false), true) + st.ok(isValidSignature(v, r, s, false)) + st.end() }) - it('should work otherwise', function() { + t.test('should work otherwise', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') const v = 27 - assert.equal(isValidSignature(v, r, s), true) + st.ok(isValidSignature(v, r, s)) + st.end() }) - it('should work otherwise(chainId=3)', function() { + t.test('should work otherwise (chainId=3)', function(st) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') const v = 41 - assert.equal(isValidSignature(v, r, s, false, chainId), true) + st.ok(isValidSignature(v, r, s, false, chainId)) + st.end() }) - it('should work otherwise(chainId=150)', function() { + t.test('should work otherwise (chainId=150)', function(st) { const chainId = 150 const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') const v = chainId * 2 + 35 - assert.equal(isValidSignature(v, r, s, false, chainId), true) - assert.equal(isValidSignature(intToBuffer(v), r, s, false, intToBuffer(chainId)), true) - assert.equal(isValidSignature(new BN(v), r, s, false, new BN(chainId)), true) - assert.equal( + st.ok(isValidSignature(v, r, s, false, chainId)) + st.ok(isValidSignature(intToBuffer(v), r, s, false, intToBuffer(chainId))) + st.ok(isValidSignature(new BN(v), r, s, false, new BN(chainId))) + st.ok( isValidSignature( '0x' + intToBuffer(v).toString('hex'), r, s, false, '0x' + intToBuffer(chainId).toString('hex') - ), - true + ) ) + st.end() }) - it('should work otherwise(chainId larger than MAX_INTEGER)', function() { + t.test('should work otherwise (chainId larger than MAX_INTEGER)', function(st) { const r = Buffer.from('ec212841e0b7aaffc3b3e33a08adf32fa07159e856ef23db85175a4f6d71dc0f', 'hex') const s = Buffer.from('4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5', 'hex') const vBuffer = Buffer.from('f2ded8deec6714', 'hex') const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') - assert.equal(isValidSignature(vBuffer, r, s, false, chainIDBuffer), true) - assert.equal(isValidSignature(new BN(vBuffer), r, s, false, new BN(chainIDBuffer)), true) - assert.equal( + st.ok(isValidSignature(vBuffer, r, s, false, chainIDBuffer)) + st.ok(isValidSignature(new BN(vBuffer), r, s, false, new BN(chainIDBuffer))) + st.ok( isValidSignature( '0x' + vBuffer.toString('hex'), r, s, false, '0x' + chainIDBuffer.toString('hex') - ), - true + ) ) const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) const vNumber = parseInt(vBuffer.toString('hex'), 16) - assert.throws(() => { + st.throws(() => { // If we would use numbers for the `v` and `chainId` parameters, then it should throw. // (The numbers are too high to perform arithmetic on) isValidSignature(vNumber, r, s, false, chainIDNumber) }) + st.end() }) // FIXME: add homestead test }) -describe('message sig', function() { +tape('message sig', function(t) { const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - it('should return hex strings that the RPC can use', function() { + t.test('should return hex strings that the RPC can use', function(st) { const sig = '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca661b' - assert.equal(toRpcSig(27, r, s), sig) - assert.deepEqual(fromRpcSig(sig), { + st.equal(toRpcSig(27, r, s), sig) + st.deepEqual(fromRpcSig(sig), { v: 27, r, s }) + st.end() }) - it('should return hex strings that the RPC can use (chainId=150)', function() { + t.test('should return hex strings that the RPC can use (chainId=150)', function(st) { const sig = '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f' const chainId = 150 const v = chainId * 2 + 35 - assert.equal(toRpcSig(v, r, s, chainId), sig) - assert.equal(toRpcSig(intToBuffer(v), r, s, intToBuffer(chainId)), sig) - assert.equal(toRpcSig(new BN(v), r, s, new BN(chainId)), sig) - assert.equal( + st.equal(toRpcSig(v, r, s, chainId), sig) + st.equal(toRpcSig(intToBuffer(v), r, s, intToBuffer(chainId)), sig) + st.equal(toRpcSig(new BN(v), r, s, new BN(chainId)), sig) + st.equal( toRpcSig( '0x' + intToBuffer(v).toString('hex'), r, @@ -370,53 +400,61 @@ describe('message sig', function() { ), sig ) - assert.deepEqual(fromRpcSig(sig), { + st.deepEqual(fromRpcSig(sig), { v, r, s }) + st.end() }) - it('should return hex strings that the RPC can use (chainId larger than MAX_SAFE_INTEGER)', function() { - const sig = - '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66f2ded8deec6714' - const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') - const vBuffer = Buffer.from('f2ded8deec6714', 'hex') - assert.equal(toRpcSig(vBuffer, r, s, chainIDBuffer), sig) - assert.equal(toRpcSig(new BN(vBuffer), r, s, new BN(chainIDBuffer)), sig) - assert.equal( - toRpcSig('0x' + vBuffer.toString('hex'), r, s, '0x' + chainIDBuffer.toString('hex')), - sig - ) + t.test( + 'should return hex strings that the RPC can use (chainId larger than MAX_SAFE_INTEGER)', + function(st) { + const sig = + '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66f2ded8deec6714' + const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') + const vBuffer = Buffer.from('f2ded8deec6714', 'hex') + st.equal(toRpcSig(vBuffer, r, s, chainIDBuffer), sig) + st.equal(toRpcSig(new BN(vBuffer), r, s, new BN(chainIDBuffer)), sig) + st.equal( + toRpcSig('0x' + vBuffer.toString('hex'), r, s, '0x' + chainIDBuffer.toString('hex')), + sig + ) - const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) - const vNumber = parseInt(vBuffer.toString('hex'), 16) - assert.throws(function() { - toRpcSig(vNumber, r, s, chainIDNumber) - }) - }) + const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) + const vNumber = parseInt(vBuffer.toString('hex'), 16) + st.throws(function() { + toRpcSig(vNumber, r, s, chainIDNumber) + }) + st.end() + } + ) - it('should throw on shorter length', function() { - assert.throws(function() { + t.test('should throw on shorter length', function(st) { + st.throws(function() { fromRpcSig('') }) - assert.throws(function() { + st.throws(function() { fromRpcSig( '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca' ) }) + st.end() }) - it('pad short r and s values', function() { - assert.equal( + t.test('pad short r and s values', function(st) { + st.equal( toRpcSig(27, r.slice(20), s.slice(20)), '0x00000000000000000000000000000000000000004a1579cf389ef88b20a1abe90000000000000000000000000000000000000000326fa689f228040429e3ca661b' ) + st.end() }) - it('should throw on invalid v value', function() { - assert.throws(function() { + t.test('should throw on invalid v value', function(st) { + st.throws(function() { toRpcSig(1, r, s) }) + st.end() }) }) diff --git a/test/types.spec.ts b/test/types.spec.ts index 16ae5f31..0b2322ed 100644 --- a/test/types.spec.ts +++ b/test/types.spec.ts @@ -1,112 +1,124 @@ -import assert from 'assert' -import BN from 'bn.js' -import { toType, TypeOutput, intToBuffer, bufferToHex, intToHex, bnToHex, toBuffer } from '../src' +import tape from 'tape' +import { + BN, + toType, + TypeOutput, + intToBuffer, + bufferToHex, + intToHex, + bnToHex, + toBuffer +} from '../src' -describe('toType', function() { - describe('from Number', function() { +tape('toType', function(t) { + t.test('from Number', function(st) { const num = 1000 - it('should convert to Number', function() { + st.test('should convert to Number', function(st) { const result = toType(num, TypeOutput.Number) - assert.strictEqual(result, num) + st.strictEqual(result, num) + st.end() }) - it('should convert to BN', function() { + st.test('should convert to BN', function(st) { const result = toType(num, TypeOutput.BN) - assert.ok(result.eq(new BN(num))) + st.ok(result.eq(new BN(num))) + st.end() }) - it('should convert to Buffer', function() { + st.test('should convert to Buffer', function(st) { const result = toType(num, TypeOutput.Buffer) - assert.ok(result.equals(intToBuffer(num))) + st.ok(result.equals(intToBuffer(num))) + st.end() }) - it('should convert to PrefixedHexString', function() { + st.test('should convert to PrefixedHexString', function(st) { const result = toType(num, TypeOutput.PrefixedHexString) - assert.strictEqual(result, bufferToHex(new BN(num).toArrayLike(Buffer))) - }) - it('should throw an error if greater than MAX_SAFE_INTEGER', function() { - assert.throws( - () => { - const num = Number.MAX_SAFE_INTEGER + 1 - toType(num, TypeOutput.BN) - }, - { - message: - 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)' - } - ) + st.strictEqual(result, bufferToHex(new BN(num).toArrayLike(Buffer))) + st.end() + }) + st.test('should throw an error if greater than MAX_SAFE_INTEGER', function(st) { + st.throws(() => { + const num = Number.MAX_SAFE_INTEGER + 1 + toType(num, TypeOutput.BN) + }, /^Error: The provided number is greater than MAX_SAFE_INTEGER \(please use an alternative input type\)$/) + st.end() }) }) - describe('from BN', function() { + t.test('from BN', function(st) { const num = new BN(1000) - it('should convert to Number', function() { + st.test('should convert to Number', function(st) { const result = toType(num, TypeOutput.Number) - assert.strictEqual(result, num.toNumber()) + st.strictEqual(result, num.toNumber()) + st.end() }) - it('should convert to BN', function() { + st.test('should convert to BN', function(st) { const result = toType(num, TypeOutput.BN) - assert.ok(result.eq(num)) + st.ok(result.eq(num)) + st.end() }) - it('should convert to Buffer', function() { + st.test('should convert to Buffer', function(st) { const result = toType(num, TypeOutput.Buffer) - assert.ok(result.equals(num.toArrayLike(Buffer))) + st.ok(result.equals(num.toArrayLike(Buffer))) + st.end() }) - it('should convert to PrefixedHexString', function() { + st.test('should convert to PrefixedHexString', function(st) { const result = toType(num, TypeOutput.PrefixedHexString) - assert.strictEqual(result, bufferToHex(num.toArrayLike(Buffer))) - }) - it('should throw an error if converting to Number and greater than MAX_SAFE_INTEGER', function() { - const num = new BN(Number.MAX_SAFE_INTEGER).addn(1) - assert.throws( - () => { + st.strictEqual(result, bufferToHex(num.toArrayLike(Buffer))) + st.end() + }) + st.test( + 'should throw an error if converting to Number and greater than MAX_SAFE_INTEGER', + function(st) { + const num = new BN(Number.MAX_SAFE_INTEGER).addn(1) + st.throws(() => { toType(num, TypeOutput.Number) - }, - { - message: - 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative output type)' - } - ) - }) + }, /^Error: The provided number is greater than MAX_SAFE_INTEGER \(please use an alternative output type\)$/) + st.end() + } + ) }) - describe('from Buffer', function() { + t.test('from Buffer', function(st) { const num = intToBuffer(1000) - it('should convert to Number', function() { + st.test('should convert to Number', function(st) { const result = toType(num, TypeOutput.Number) - assert.ok(intToBuffer(result).equals(num)) + st.ok(intToBuffer(result).equals(num)) + st.end() }) - it('should convert to BN', function() { + st.test('should convert to BN', function(st) { const result = toType(num, TypeOutput.BN) - assert.ok(result.eq(new BN(num))) + st.ok(result.eq(new BN(num))) + st.end() }) - it('should convert to Buffer', function() { + st.test('should convert to Buffer', function(st) { const result = toType(num, TypeOutput.Buffer) - assert.ok(result.equals(num)) + st.ok(result.equals(num)) + st.end() }) - it('should convert to PrefixedHexString', function() { + st.test('should convert to PrefixedHexString', function(st) { const result = toType(num, TypeOutput.PrefixedHexString) - assert.strictEqual(result, bufferToHex(num)) + st.strictEqual(result, bufferToHex(num)) + st.end() }) }) - describe('from HexPrefixedString', function() { + t.test('from HexPrefixedString', function(st) { const num = intToHex(1000) - it('should convert to Number', function() { + st.test('should convert to Number', function(st) { const result = toType(num, TypeOutput.Number) - assert.strictEqual(intToHex(result), num) + st.strictEqual(intToHex(result), num) + st.end() }) - it('should convert to BN', function() { + st.test('should convert to BN', function(st) { const result = toType(num, TypeOutput.BN) - assert.strictEqual(bnToHex(result), num) + st.strictEqual(bnToHex(result), num) + st.end() }) - it('should convert to Buffer', function() { + st.test('should convert to Buffer', function(st) { const result = toType(num, TypeOutput.Buffer) - assert.ok(result.equals(toBuffer(num))) - }) - it('should throw an error if is not 0x-prefixed', function() { - assert.throws( - () => { - toType('1', TypeOutput.Number) - }, - { - message: 'A string must be provided with a 0x-prefix, given: 1' - } - ) + st.ok(result.equals(toBuffer(num))) + st.end() + }) + st.test('should throw an error if is not 0x-prefixed', function(st) { + st.throws(() => { + toType('1', TypeOutput.Number) + }, /^Error: A string must be provided with a 0x-prefix, given: 1$/) + st.end() }) }) }) From 33dd73ae2f805e42594690c9ba15c0d3da8bc604 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 4 Mar 2021 14:03:44 -0800 Subject: [PATCH 45/46] clean up `require` usage --- src/account.ts | 13 ++++++------- src/address.ts | 4 ++-- src/externals.ts | 7 ++----- src/hash.ts | 2 +- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/account.ts b/src/account.ts index fb8d46ba..b4893b76 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,6 +1,12 @@ import assert from 'assert' import BN from 'bn.js' import * as rlp from 'rlp' +import { + privateKeyVerify, + publicKeyCreate, + publicKeyVerify, + publicKeyConvert +} from 'ethereum-cryptography/secp256k1' import { stripHexPrefix } from 'ethjs-util' import { KECCAK256_RLP, KECCAK256_NULL } from './constants' import { zeros, bufferToHex, toBuffer } from './bytes' @@ -8,13 +14,6 @@ import { keccak, keccak256, keccakFromString, rlphash } from './hash' import { assertIsHexString, assertIsBuffer } from './helpers' import { BNLike, BufferLike, bnToRlp, toType, TypeOutput } from './types' -const { - privateKeyVerify, - publicKeyCreate, - publicKeyVerify, - publicKeyConvert -} = require('ethereum-cryptography/secp256k1') - export interface AccountData { nonce?: BNLike balance?: BNLike diff --git a/src/address.ts b/src/address.ts index c80c3d34..e5ff90ea 100644 --- a/src/address.ts +++ b/src/address.ts @@ -1,5 +1,5 @@ -const assert = require('assert') -import BN = require('bn.js') +import assert from 'assert' +import BN from 'bn.js' import { toBuffer, zeros } from './bytes' import { isValidAddress, diff --git a/src/externals.ts b/src/externals.ts index 22eb12af..17fae578 100644 --- a/src/externals.ts +++ b/src/externals.ts @@ -4,11 +4,8 @@ * @packageDocumentation */ -// TODO: This can be replaced with a normal ESM import once -// the new major version of the typescript config package -// is released and adopted here. -import BN = require('bn.js') -import rlp = require('rlp') +import BN from 'bn.js' +import * as rlp from 'rlp' /** * [`BN`](https://github.com/indutny/bn.js) diff --git a/src/hash.ts b/src/hash.ts index 79472a96..d6a541de 100644 --- a/src/hash.ts +++ b/src/hash.ts @@ -1,4 +1,4 @@ -const { keccak224, keccak384, keccak256: k256, keccak512 } = require('ethereum-cryptography/keccak') +import { keccak224, keccak384, keccak256 as k256, keccak512 } from 'ethereum-cryptography/keccak' const createHash = require('create-hash') import * as rlp from 'rlp' import { toBuffer, setLengthLeft } from './bytes' From f51bfcab9e5505dfed4819ef1336f9fc00a12c3d Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Thu, 17 Jun 2021 11:47:50 +0200 Subject: [PATCH 46/46] Adding moved repository badge --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 67e8679d..c8f51a87 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +

+ + + +

+ # SYNOPSIS [![NPM Status][npm-badge]][npm-link] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy