diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a0e598..14a8b6f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,36 @@ 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. ] +### 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** + +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`. + +### 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 diff --git a/README.md b/README.md index 95733ae0..1d22e35c 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 @@ -46,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 9a7b907a..bdfde4cb 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) @@ -15,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 new file mode 100644 index 00000000..42391678 --- /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: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. + +**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:27](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L27)* + +___ + +### codeHash + +• **codeHash**: *Buffer* + +*Defined in [account.ts:29](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L29)* + +___ + +### nonce + +• **nonce**: *BN* + +*Defined in [account.ts:26](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L26)* + +___ + +### stateRoot + +• **stateRoot**: *Buffer* + +*Defined in [account.ts:28](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L28)* + +## Methods + +### isContract + +▸ **isContract**(): *boolean* + +*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. + +**Returns:** *boolean* + +___ + +### isEmpty + +▸ **isEmpty**(): *boolean* + +*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. + +**Returns:** *boolean* + +___ + +### serialize + +▸ **serialize**(): *Buffer* + +*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`. + +**Returns:** *Buffer* + +___ + +### `Static` fromAccountData + +▸ **fromAccountData**(`accountData`: [AccountData](../interfaces/_account_.accountdata.md)): *[Account](_account_.account.md)‹›* + +*Defined in [account.ts:31](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L31)* + +**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:42](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L42)* + +**Parameters:** + +Name | Type | +------ | ------ | +`serialized` | Buffer | + +**Returns:** *[Account](_account_.account.md)‹›* + +___ + +### `Static` fromValuesArray + +▸ **fromValuesArray**(`values`: Buffer[]): *[Account](_account_.account.md)‹›* + +*Defined in [account.ts:52](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L52)* + +**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..126266a0 --- /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](../modules/_types_.md#bnlike)* + +*Defined in [account.ts:20](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L20)* + +___ + +### `Optional` codeHash + +• **codeHash**? : *[BufferLike](../modules/_types_.md#bufferlike)* + +*Defined in [account.ts:22](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L22)* + +___ + +### `Optional` nonce + +• **nonce**? : *[BNLike](../modules/_types_.md#bnlike)* + +*Defined in [account.ts:19](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L19)* + +___ + +### `Optional` stateRoot + +• **stateRoot**? : *[BufferLike](../modules/_types_.md#bufferlike)* + +*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/__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..0abc65de 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:246](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L246)* ## 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:168](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L168)* 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:189](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L189)* 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:269](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L269)* 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:116](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L116)* 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:156](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L156)* 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: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. @@ -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: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. @@ -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:289](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L289)* 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:252](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L252)* 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:260](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L260)* 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: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. @@ -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:131](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L131)* 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:280](https://github.com/ethereumjs/ethereumjs-util/blob/master/src/account.ts#L280)* 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/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)* 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..e3d2c6df 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,23 +1,125 @@ -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 } from './bytes' +import { keccak, keccak256, keccakFromString, rlphash } from './hash' +import { assertIsHexString, assertIsBuffer } from './helpers' +import { BNLike, BufferLike, bnToRlp } from './types' + 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 +} + +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 assigns and validates the values. + * 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, + ) { + 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') + } + } + + /** + * 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` determining if the account is a contract. + */ + isContract(): boolean { + return !this.codeHash.equals(KECCAK256_NULL) + } + + /** + * 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 ( + this.balance.isZero() && + this.nonce.isZero() && + this.stateRoot.equals(KECCAK256_RLP) && + this.codeHash.equals(KECCAK256_NULL) + ) + } } /** @@ -28,28 +130,19 @@ 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. * * 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. */ 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 +285,21 @@ export const importPublic = function(publicKey: Buffer): Buffer { } return publicKey } + +/** + * Returns the zero address. + */ +export const zeroAddress = function(): string { + const addressLength = 20 + const addr = zeros(addressLength) + return bufferToHex(addr) +} + +/** + * Checks if a given address is the zero address. + */ +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..3fd4eacd 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,11 +1,11 @@ -import * as ethjsUtil from 'ethjs-util' +import { isHexString } from 'ethjs-util' /** * Throws if a string is not hex prefixed * @param {string} input string to check hex prefix of */ export const assertIsHexString = function(input: string): void { - if (!ethjsUtil.isHexString(input)) { + if (!isHexString(input)) { const msg = `This method only supports 0x-prefixed hex strings but input was: ${input}` throw new Error(msg) } diff --git a/src/index.ts b/src/index.ts index bf462b49..848d0c77 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' @@ -39,6 +39,11 @@ export * from './object' */ export * from './externals' +/** + * Helpful TypeScript types + */ +export * from './types' + /** * Export ethjs-util methods */ diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..64ec1a35 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,41 @@ +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 + +/* + * 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 +} + +/** + * 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/test/account.spec.ts b/test/account.spec.ts index 44df2c17..c999b357 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,199 @@ 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: '0xd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b', + } + 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) + }) + }) + + 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)) + }) + }) + }) +}) + describe('isValidPrivate', function() { const SECP256K1_N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) it('should fail on short input', function() { @@ -196,112 +391,22 @@ 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, - ]) + 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() { - 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, - ]) + const privateKey1 = Buffer.from( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f2a', + 'hex', + ) + const privateKey2 = Buffer.from( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c', + 'hex', + ) assert.throws(function() { privateToPublic(privateKey1) }) @@ -325,40 +430,10 @@ 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, - ]) + const privateKey = Buffer.from( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', + 'hex', + ) 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, }