Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accounts Creation - 4.x rewrite #4577

Merged
merged 20 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,13 @@ Released with 1.0.0-beta.37 code base.
1. The function `outputBigNumberFormatter` in `web3-core-helper` renamed to `outputBigIntFormatter` under `web3-common`
2. Removed `this.defaultBlock` context from `inputDefaultBlockNumberFormatter` in `web3-core-helper` and converted to additional parameter
3. Removed `this.defaultBlock` context from `inputTransactionFormatter` in `web3-core-helper` and converted to additional parameter

#### web3-utils

1. The following functions `soliditySha3` `soliditySha3Raw` `encodePacked` now includes type validation and requires type specficiation, instead of guessing the value type
2. The functions `soliditySha3` `soliditySha3Raw` `encodePacked` does not support BN and now supports `BigInt`
3. The functions `flattenTypes` and `jsonInterfaceMethodToString` moved to the `web3-eth-abi` package
nazarhussain marked this conversation as resolved.
Show resolved Hide resolved

### web3-eth-accounts

1. `create` function does not take in the optional parameter `entropy`
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@
"ts-node": "^10.4.0",
"typescript": "^4.5.2"
},
"packageManager": "yarn@1.22.15"
"packageManager": "yarn@1.22.15",
"dependencies": {}
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 4 additions & 0 deletions packages/web3-eth-accounts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,9 @@
"prettier": "^2.4.1",
"ts-jest": "^27.0.7",
"typescript": "^4.5.2"
},
"dependencies": {
"ethereum-cryptography": "^0.2.0",
"web3-utils": "4.0.0-alpha.0"
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
}
}
63 changes: 63 additions & 0 deletions packages/web3-eth-accounts/src/account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { utils, getPublicKey } from 'ethereum-cryptography/secp256k1';
import { toChecksumAddress, bytesToHex, sha3Raw } from 'web3-utils';
import { PrivateKeyError } from './errors';
// Will be added later
export const encrypt = (): boolean => true;

// Will be added later
export const sign = (): boolean => true;

// Will be added later
export const signTransaction = (): boolean => true;
luu-alex marked this conversation as resolved.
Show resolved Hide resolved

/**
* Get address from private key
*/
export const privateKeyToAccount = (
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
privateKey: string | Uint8Array,
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
): {
address: string;
privateKey: string;
signTransaction: () => boolean; // From 1.x
sign: () => boolean;
encrypt: () => boolean;
} => {
const stringPrivateKey =
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
typeof privateKey === 'object' ? Buffer.from(privateKey).toString('hex') : privateKey;
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
const updatedKey = stringPrivateKey.startsWith('0x')
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
? stringPrivateKey.slice(2)
: stringPrivateKey;

// Must be 64 hex characters
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
if (updatedKey.length !== 64) {
throw new PrivateKeyError(updatedKey);
}
const publicKey = getPublicKey(updatedKey);

const publicKeyString = `0x${publicKey.slice(2)}`;
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
const publicHash = sha3Raw(publicKeyString);
const publicHashHex = bytesToHex(publicHash);
const address = toChecksumAddress(publicHashHex.slice(-40));
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
return { address, privateKey: stringPrivateKey, signTransaction, sign, encrypt };
};

/**
* Returns a random hex string by the given bytes size
*/
export const create = (): {
address: string;
privateKey: string;
signTransaction: Function; // From 1.x
sign: Function;
encrypt: Function;
} => {
const privateKey = utils.randomPrivateKey();
const address = getPublicKey(privateKey);
return {
privateKey: `0x${Buffer.from(privateKey).toString('hex')}`,
address: `0x${Buffer.from(address).toString('hex')}`,
signTransaction,
sign,
encrypt,
};
};
21 changes: 21 additions & 0 deletions packages/web3-eth-accounts/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* eslint-disable max-classes-per-file */

export abstract class Web3Error extends Error {
public readonly name: string;

public constructor(value: unknown, msg: string) {
super(`Invalid value given "${String(value)}". Error: ${msg}.`);
this.name = this.constructor.name;
Error.captureStackTrace(this, Web3Error);
}

public toJSON() {
return { name: this.name, message: this.message };
}
}
luu-alex marked this conversation as resolved.
Show resolved Hide resolved

export class PrivateKeyError extends Web3Error {
public constructor(value: string) {
super(value, 'Private key must be 32 bytes long');
}
}
1 change: 1 addition & 0 deletions packages/web3-eth-accounts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './account';
1 change: 1 addition & 0 deletions packages/web3-eth-accounts/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type HexString = string;
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
24 changes: 24 additions & 0 deletions packages/web3-eth-accounts/test/fixtures/account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { sign, signTransaction, encrypt } from '../../src/account';

export const validPrivateKeytoAccountData: [string, any][] = [
[
'0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709',
{
address: '0xb8CE9ab6943e0eCED004cDe8e3bBed6568B2Fa01',
privateKey: '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709',
sign,
signTransaction,
encrypt,
},
],
[
'0x9e93921f9bca358a96aa66efcccbde12850473be95f63c1453e29656feafeb35',
{
address: '0x118C2E5F57FD62C2B5b46a5ae9216F4FF4011a07',
privateKey: '0x9e93921f9bca358a96aa66efcccbde12850473be95f63c1453e29656feafeb35',
sign,
signTransaction,
encrypt,
},
],
];
29 changes: 29 additions & 0 deletions packages/web3-eth-accounts/test/unit/account.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { isHexStrict } from 'web3-utils';
import { create, privateKeyToAccount } from '../../src/account';
import { validPrivateKeytoAccountData } from '../fixtures/account';

describe('accounts', () => {
describe('create', () => {
describe('valid cases', () => {
it('%s', () => {
for (let i = 0; i < 10; i += 1) {
luu-alex marked this conversation as resolved.
Show resolved Hide resolved
const account = create();
expect(typeof account.privateKey).toBe('string');
expect(typeof account.address).toBe('string');
expect(isHexStrict(account.address)).toBe(true);
expect(typeof account.encrypt).toBe('function');
expect(typeof account.sign).toBe('function');
expect(typeof account.signTransaction).toBe('function');
}
});
});
});

describe('fromPrivate', () => {
describe('valid cases', () => {
it.each(validPrivateKeytoAccountData)('%s', (input, output) => {
expect(privateKeyToAccount(input)).toEqual(output);
});
});
});
});
20 changes: 0 additions & 20 deletions packages/web3-eth-accounts/test/unit/constructor.test.ts

This file was deleted.

16 changes: 9 additions & 7 deletions packages/web3-utils/src/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,28 +355,30 @@ export const toWei = (number: Numbers, unit: EtherUnits): string => {
};

export const toChecksumAddress = (address: Address): string => {
if (typeof address === 'undefined') return '';

if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
if (!/^(0x)?[0-9a-f]{40}$/i.test(address) && isHexStrict(address)) {
spacesailor24 marked this conversation as resolved.
Show resolved Hide resolved
throw new InvalidAddressError(address);
}

const lowerCaseAddress = address.toLowerCase().replace(/^0x/i, '');
const hash = (keccak256(Buffer.from(lowerCaseAddress)) as Buffer).toString('hex');

const hash = bytesToHex(keccak256(Buffer.from(lowerCaseAddress)));

if (
hash === null ||
hash === 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
)
return ''; // // EIP-1052 if hash is equal to c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, keccak was given empty data

const addressHash = hash.replace(/^0x/i, '');

let checksumAddress = '0x';

for (let i = 0; i < address.length; i += 1) {
for (let i = 0; i < lowerCaseAddress.length; i += 1) {
// If ith character is 8 to f then make it uppercase
if (parseInt(addressHash[i], 16) > 7) {
checksumAddress += address[i].toUpperCase();
checksumAddress += lowerCaseAddress[i].toUpperCase();
} else {
checksumAddress += address[i];
checksumAddress += lowerCaseAddress[i];
}
}
return checksumAddress;
Expand Down
4 changes: 2 additions & 2 deletions packages/web3-utils/src/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const SHA3_EMPTY_BYTES = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfa

/**
*
* computes the Keccak-256 hash of the string input and returns a hexstring
* computes the Keccak-256 hash of the input and returns a hexstring
*/
export const sha3 = (data: Bytes): string | null => {
const updatedData = typeof data === 'string' && isHexStrict(data) ? hexToBytes(data) : data;
Expand All @@ -32,7 +32,7 @@ export const sha3 = (data: Bytes): string | null => {
/**
*Will calculate the sha3 of the input but does return the hash value instead of null if for example a empty string is passed.
*/
export const sha3Raw = (data: string): string => {
export const sha3Raw = (data: Bytes): string => {
const hash = sha3(data);
if (hash === null) {
return SHA3_EMPTY_BYTES;
Expand Down
1 change: 1 addition & 0 deletions packages/web3-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from './errors';
export * from './validation';
export * from './types';
export * from './hash';
export * from './random';
export * from './string_manipulation';
14 changes: 14 additions & 0 deletions packages/web3-utils/src/random.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { randomBytes } from 'crypto';
/**
* Returns a random hex string by the given bytes size
*
* @param {Number} size
* @returns {string}
*/
export const randomHex = (byteSize: number): string => {
const randomValues =
typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues
spacesailor24 marked this conversation as resolved.
Show resolved Hide resolved
? window.crypto.getRandomValues(new Uint8Array(byteSize))
: randomBytes(byteSize);
return `0x${randomValues.toString('hex')}`;
};
5 changes: 5 additions & 0 deletions packages/web3-utils/test/fixtures/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,8 @@ export const toWeiInvalidData: [[any, any], string][] = [
[['data', 'kwei'], 'Invalid value given "data". Error: not a valid number.'],
[['1234', 'uwei'], 'Invalid value given "uwei". Error: invalid unit.'],
];
export const toCheckSumValidData: [string, string][] = [
['0x0089d53f703f7e0843953d48133f74ce247184c2', '0x0089d53F703f7E0843953D48133f74cE247184c2'],
['0x5fbc2b6c19ee3dd5f9af96ff337ddc89e30ceaef', '0x5FBc2b6C19EE3DD5f9Af96ff337DDC89e30ceAef'],
['0xa54D3c09E34aC96807c1CC397404bF2B98DC4eFb', '0xa54d3c09E34aC96807c1CC397404bF2B98DC4eFb'],
];
12 changes: 12 additions & 0 deletions packages/web3-utils/test/unit/converters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
toUtf8,
toWei,
utf8ToHex,
toChecksumAddress,
} from '../../src/converters';
import {
asciiToHexValidData,
Expand All @@ -41,6 +42,7 @@ import {
toWeiValidData,
utf8ToHexInvalidData,
utf8ToHexValidData,
toCheckSumValidData,
} from '../fixtures/converters';

describe('converters', () => {
Expand Down Expand Up @@ -321,4 +323,14 @@ describe('converters', () => {
});
});
});
describe('toChecksumAddress', () => {
describe('valid cases', () => {
it.each(toCheckSumValidData)('%s', (input, output) => {
expect(toChecksumAddress(input)).toEqual(output);
});
});
describe('invalid cases', () => {
it.todo('should throw error for invalid cases');
});
});
});