Skip to content
This repository has been archived by the owner on May 16, 2024. It is now read-only.

Commit

Permalink
Grind key revert (#341)
Browse files Browse the repository at this point in the history
* porting legacy stark grind methods to core sdk

* DX-2184: Passing tests for GrindKey legacy

* DX-2184: Added tests to charecterize the legacy grind key implementation

* Dx 2184 gk3 (#342)

* Dx-2184: Rebasing the changes off gring-key-revert branch

* DX-2184: First pass of Backwards Compatible logic
Co-authored-by: Nik <[email protected]>

* DX-2184: Added Tests for Backwards compatible key gen
Co-authored-by: Nik <[email protected]>

* DX-2184: Removed todo
Co-authored-by: Nik <[email protected]>

* DX-2184: ethAddress is non-optional now

* DX-2184: Using 2.0.0 grindKey as the default version

Co-authored-by: Carl Menezes <[email protected]>
Co-authored-by: Nik <[email protected]>

* DX-2184: Added Changelog

Co-authored-by: Carl Menezes <[email protected]>
Co-authored-by: Nik <[email protected]>

* DX-2184: Added tests for legacy imx-sdk-js version

* PR fix

---------

Co-authored-by: Carl Menezes <[email protected]>
Co-authored-by: Nik <[email protected]>

---------

Co-authored-by: Chris James <[email protected]>
Co-authored-by: Carl Menezes <[email protected]>
Co-authored-by: Nik <[email protected]>
  • Loading branch information
4 people authored Jul 20, 2023
1 parent aad87d4 commit e688709
Show file tree
Hide file tree
Showing 9 changed files with 2,620 additions and 82 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- Added support to generate backwards compatible stark key using `generateLegacyStarkPrivateKey` for accounts created using `imx-sdk-js` versions before 1.43.5

## [2.0.1] - 2023-06-16
- Fix for bug in generateLegacyStarkPrivateKey for some wallets, see https://github.com/immutable/imx-core-sdk/pull/334 for details.

Expand Down
112 changes: 112 additions & 0 deletions src/utils/stark/legacy/crypto/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import BN from 'bn.js';
import { curves, ec as Ec } from 'elliptic';
import hashJS from 'hash.js';

import { constantPointsHex } from './points';
import { Instruction, InstructionWithFee } from './types';

const DEFAULT_ACCOUNT_MAPPING_KEY = 'STARKWARE_ACCOUNT_MAPPING';
const DEFAULT_SIGNATURE_MESSAGE =
'Only sign this request if you’ve initiated an action with Immutable X.';

const DEFAULT_ACCOUNT_LAYER = 'starkex';
const DEFAULT_ACCOUNT_APPLICATION = 'immutablex';
const DEFAULT_ACCOUNT_INDEX = '1';

const NFT_ASSET_ID_PREFIX = 'NFT:';
const MINTABLE_ASSET_ID_PREFIX = 'MINTABLE:';

const prime = new BN(
'800000000000011000000000000000000000000000000000000000000000001',
16,
);
const order = new BN(
'08000000 00000010 ffffffff ffffffff b781126d cae7b232 1e66a241 adc64d2f',
16,
);

const starkEc = new Ec(
new curves.PresetCurve({
type: 'short',
prime: null,
p: prime as any,
a: '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001',
b: '06f21413 efbe40de 150e596d 72f7a8c5 609ad26c 15c915c1 f4cdfcb9 9cee9e89',
n: order as any,
hash: hashJS.sha256,
gRed: false,
g: constantPointsHex[1],
}),
);

const constantPoints = constantPointsHex.map((coords: string[]) =>
starkEc.curve.point(new BN(coords[0], 16), new BN(coords[1], 16)),
);
const shiftPoint = constantPoints[0];

// Instruction type mapping encoded in BigNumber
// see https://docs.starkware.co/starkex-v3/starkex-deep-dive/message-encodings/signatures
const instructionEncodingMap: {
[instruction in Instruction | InstructionWithFee]: BN;
} = {
order: new BN('0'),
transfer: new BN('1'),
orderWithFee: new BN('3'),
transferWithFee: new BN('4'),
registerUser: new BN('1000'),
deposit: new BN('1001'),
withdraw: new BN('1002'),
cancelOrder: new BN('1003'),
};

const ZERO_BN = new BN('0');
const ONE_BN = new BN('1');
const TWO_POW_22_BN = new BN('400000', 16);
const TWO_POW_31_BN = new BN('80000000', 16);
const TWO_POW_63_BN = new BN('8000000000000000', 16);
// Equals 2**251 + 17 * 2**192 + 1.
const PRIME_BN = new BN(
'800000000000011000000000000000000000000000000000000000000000001',
16,
);
// Equals 2**251. This value limits msgHash and the signature parts.
const MAX_ECDSA_BN = new BN(
'800000000000000000000000000000000000000000000000000000000000000',
16,
);

const MISSING_HEX_PREFIX = 'Hex strings expected to be prefixed with 0x.';

const ORDER = new BN(
'08000000 00000010 ffffffff ffffffff b781126d cae7b232 1e66a241 adc64d2f',
16,
);
const SECP_ORDER = new BN(
'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141',
16,
);

export {
constantPoints,
DEFAULT_ACCOUNT_APPLICATION,
DEFAULT_ACCOUNT_INDEX,
DEFAULT_ACCOUNT_LAYER,
DEFAULT_ACCOUNT_MAPPING_KEY,
DEFAULT_SIGNATURE_MESSAGE,
instructionEncodingMap,
MAX_ECDSA_BN,
MINTABLE_ASSET_ID_PREFIX,
MISSING_HEX_PREFIX,
NFT_ASSET_ID_PREFIX,
ONE_BN,
ORDER,
prime,
PRIME_BN,
SECP_ORDER,
shiftPoint,
starkEc,
TWO_POW_22_BN,
TWO_POW_31_BN,
TWO_POW_63_BN,
ZERO_BN,
};
120 changes: 120 additions & 0 deletions src/utils/stark/legacy/crypto/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable no-param-reassign */
/* eslint-disable no-plusplus */
/* eslint-disable @typescript-eslint/no-explicit-any */
import assert from 'assert';
import BN from 'bn.js';
import { ec } from 'elliptic';
import * as encUtils from 'enc-utils';
import { hdkey } from 'ethereumjs-wallet';
import hashJS from 'hash.js';

import {
MISSING_HEX_PREFIX,
ORDER,
PRIME_BN,
SECP_ORDER,
starkEc,
ZERO_BN,
} from './constants';

export function isHexPrefixed(str: string): boolean {
return str.substring(0, 2) === '0x';
}

export function checkHexValue(hex: string): void {
assert(isHexPrefixed(hex), MISSING_HEX_PREFIX);
const hexBn = new BN(encUtils.removeHexPrefix(hex), 16);
assert(hexBn.gte(ZERO_BN));
assert(hexBn.lt(PRIME_BN));
}

export function getIntFromBits(
hex: string,
start: number,
end: number | undefined = undefined,
): number {
const bin = encUtils.hexToBinary(hex);
const bits = bin.slice(start, end);
const int = encUtils.binaryToNumber(bits);
return int;
}

export function getAccountPath(
layer: string,
application: string,
ethereumAddress: string,
index: string,
): string {
const layerHash = hashJS.sha256().update(layer).digest('hex');
const applicationHash = hashJS.sha256().update(application).digest('hex');
const layerInt = getIntFromBits(layerHash, -31);
const applicationInt = getIntFromBits(applicationHash, -31);
const ethAddressInt1 = getIntFromBits(ethereumAddress, -31);
const ethAddressInt2 = getIntFromBits(ethereumAddress, -62, -31);
return `m/2645'/${layerInt}'/${applicationInt}'/${ethAddressInt1}'/${ethAddressInt2}'/${index}`;
}

export function hashKeyWithIndex(key: string, index: number): BN {
return new BN(
hashJS
.sha256()
.update(
encUtils.hexToBuffer(
encUtils.removeHexPrefix(key) +
encUtils.sanitizeBytes(encUtils.numberToHex(index), 2),
),
)
.digest('hex'),
16,
);
}

export function grindKey(privateKey: string): string {
let i = 0;
let key: BN = hashKeyWithIndex(privateKey, i);

while (!key.lt(SECP_ORDER.sub(SECP_ORDER.mod(ORDER)))) {
key = hashKeyWithIndex(key.toString(16), i);
i = i++;
}
return key.mod(ORDER).toString('hex');
}

export function getKeyPair(privateKey: string): ec.KeyPair {
return starkEc.keyFromPrivate(privateKey, 'hex');
}

export function getPrivateKeyFromPath(seed: string, path: string): string {
return hdkey
.fromMasterSeed(Buffer.from(seed.slice(2), 'hex')) // assuming seed is '0x...'
.derivePath(path)
.getWallet()
.getPrivateKeyString();
}

export function getKeyPairFromPath(seed: string, path: string): ec.KeyPair {
assert(isHexPrefixed(seed), MISSING_HEX_PREFIX);
const privateKey = getPrivateKeyFromPath(seed, path);
return getKeyPair(grindKey(privateKey));
}

export function getPublic(keyPair: ec.KeyPair, compressed = false): string {
return keyPair.getPublic(compressed, 'hex');
}

export function getStarkPublicKey(keyPair: ec.KeyPair): string {
return getPublic(keyPair, true);
}

export function getKeyPairFromPublicKey(publicKey: string): ec.KeyPair {
return starkEc.keyFromPublic(encUtils.hexToArray(publicKey));
}

export function getKeyPairFromPrivateKey(privateKey: string): ec.KeyPair {
return starkEc.keyFromPrivate(privateKey, 'hex');
}

export function getXCoordinate(publicKey: string): string {
const keyPair = getKeyPairFromPublicKey(publicKey);
return encUtils.sanitizeBytes((keyPair as any).pub.getX().toString(16), 2);
}
2 changes: 2 additions & 0 deletions src/utils/stark/legacy/crypto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './constants';
export * from './crypto';
Loading

0 comments on commit e688709

Please sign in to comment.