mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-12 10:30:36 +01:00
feat: entropy refactor
This commit is contained in:
parent
720a47cbd0
commit
94d43e1855
10 changed files with 275 additions and 183 deletions
|
@ -159,9 +159,10 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateFromEntropy(user: Buffer) {
|
async generateFromEntropy(user: Buffer) {
|
||||||
const random = await randomBytes(user.length < 32 ? 32 - user.length : 0);
|
if (user.length !== 32 && user.length !== 16) {
|
||||||
const buf = Buffer.concat([user, random], 32);
|
throw new Error('Entropy has to be 16 or 32 bytes long');
|
||||||
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
|
}
|
||||||
|
this.secret = bip39.entropyToMnemonic(user.toString('hex'));
|
||||||
}
|
}
|
||||||
|
|
||||||
_getExternalWIFByIndex(index: number): string | false {
|
_getExternalWIFByIndex(index: number): string | false {
|
||||||
|
|
|
@ -63,18 +63,10 @@ export class LegacyWallet extends AbstractWallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateFromEntropy(user: Buffer): Promise<void> {
|
async generateFromEntropy(user: Buffer): Promise<void> {
|
||||||
let i = 0;
|
if (user.length !== 32) {
|
||||||
do {
|
throw new Error('Entropy should be 32 bytes');
|
||||||
i += 1;
|
|
||||||
const random = await randomBytes(user.length < 32 ? 32 - user.length : 0);
|
|
||||||
const buf = Buffer.concat([user, random], 32);
|
|
||||||
try {
|
|
||||||
this.secret = ECPair.fromPrivateKey(buf).toWIF();
|
|
||||||
return;
|
|
||||||
} catch (e) {
|
|
||||||
if (i === 5) throw e;
|
|
||||||
}
|
}
|
||||||
} while (true);
|
this.secret = ECPair.fromPrivateKey(user).toWIF();
|
||||||
}
|
}
|
||||||
|
|
||||||
getAddress(): string | false {
|
getAddress(): string | false {
|
||||||
|
|
|
@ -131,7 +131,7 @@ interface FButtonProps {
|
||||||
last?: boolean;
|
last?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
onLongPress: () => void;
|
onLongPress?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FButton = ({ text, icon, width, first, last, ...props }: FButtonProps) => {
|
export const FButton = ({ text, icon, width, first, last, ...props }: FButtonProps) => {
|
||||||
|
|
|
@ -43,7 +43,8 @@
|
||||||
"entropy": {
|
"entropy": {
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"title": "Entropy",
|
"title": "Entropy",
|
||||||
"undo": "Undo"
|
"undo": "Undo",
|
||||||
|
"amountOfEntropy": "{bits} of {limit} bits"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"broadcast": "Broadcast failed.",
|
"broadcast": "Broadcast failed.",
|
||||||
|
@ -382,6 +383,8 @@
|
||||||
"add_bitcoin": "Bitcoin",
|
"add_bitcoin": "Bitcoin",
|
||||||
"add_bitcoin_explain": "Simple and powerful Bitcoin wallet",
|
"add_bitcoin_explain": "Simple and powerful Bitcoin wallet",
|
||||||
"add_create": "Create",
|
"add_create": "Create",
|
||||||
|
"add_entropy": "Entropy",
|
||||||
|
"add_entropy_bytes": "{bytes} bytes of entropy",
|
||||||
"add_entropy_generated": "{gen} bytes of generated entropy",
|
"add_entropy_generated": "{gen} bytes of generated entropy",
|
||||||
"add_entropy_provide": "Provide entropy via dice rolls",
|
"add_entropy_provide": "Provide entropy via dice rolls",
|
||||||
"add_entropy_remain": "{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.",
|
"add_entropy_remain": "{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.",
|
||||||
|
@ -395,6 +398,10 @@
|
||||||
"add_title": "Add Wallet",
|
"add_title": "Add Wallet",
|
||||||
"add_wallet_name": "Name",
|
"add_wallet_name": "Name",
|
||||||
"add_wallet_type": "Type",
|
"add_wallet_type": "Type",
|
||||||
|
"add_wallet_seed_length": "Seed Length",
|
||||||
|
"add_wallet_seed_length_message": "Choose the length of the seed phrase you wish to use for this wallet.",
|
||||||
|
"add_wallet_seed_length_12": "12 words",
|
||||||
|
"add_wallet_seed_length_24": "24 words",
|
||||||
"clipboard_bitcoin": "You have a Bitcoin address on your clipboard. Would you like to use it for a transaction?",
|
"clipboard_bitcoin": "You have a Bitcoin address on your clipboard. Would you like to use it for a transaction?",
|
||||||
"clipboard_lightning": "You have a Lightning invoice on your clipboard. Would you like to use it for a transaction?",
|
"clipboard_lightning": "You have a Lightning invoice on your clipboard. Would you like to use it for a transaction?",
|
||||||
"details_address": "Address",
|
"details_address": "Address",
|
||||||
|
|
|
@ -11,7 +11,7 @@ const ImportWallet = lazy(() => import('../screen/wallets/import'));
|
||||||
const PleaseBackup = lazy(() => import('../screen/wallets/PleaseBackup'));
|
const PleaseBackup = lazy(() => import('../screen/wallets/PleaseBackup'));
|
||||||
const PleaseBackupLNDHub = lazy(() => import('../screen/wallets/pleaseBackupLNDHub'));
|
const PleaseBackupLNDHub = lazy(() => import('../screen/wallets/pleaseBackupLNDHub'));
|
||||||
const PleaseBackupLdk = lazy(() => import('../screen/wallets/pleaseBackupLdk'));
|
const PleaseBackupLdk = lazy(() => import('../screen/wallets/pleaseBackupLdk'));
|
||||||
const ProvideEntropy = lazy(() => import('../screen/wallets/provideEntropy'));
|
const ProvideEntropy = lazy(() => import('../screen/wallets/ProvideEntropy'));
|
||||||
const WalletsAddMultisig = lazy(() => import('../screen/wallets/addMultisig'));
|
const WalletsAddMultisig = lazy(() => import('../screen/wallets/addMultisig'));
|
||||||
const WalletsAddMultisigStep2 = lazy(() => import('../screen/wallets/addMultisigStep2'));
|
const WalletsAddMultisigStep2 = lazy(() => import('../screen/wallets/addMultisigStep2'));
|
||||||
const WalletsAddMultisigHelp = lazy(() => import('../screen/wallets/addMultisigHelp'));
|
const WalletsAddMultisigHelp = lazy(() => import('../screen/wallets/addMultisigHelp'));
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useNavigation } from '@react-navigation/native';
|
||||||
import React, { useEffect, useReducer } from 'react';
|
import React, { useEffect, useReducer } from 'react';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Alert,
|
||||||
Keyboard,
|
Keyboard,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
LayoutAnimation,
|
LayoutAnimation,
|
||||||
|
@ -53,7 +54,7 @@ interface State {
|
||||||
label: string;
|
label: string;
|
||||||
selectedWalletType: ButtonSelected;
|
selectedWalletType: ButtonSelected;
|
||||||
backdoorPressed: number;
|
backdoorPressed: number;
|
||||||
entropy: string | any[] | undefined;
|
entropy: Buffer | undefined;
|
||||||
entropyButtonText: string;
|
entropyButtonText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,18 +162,13 @@ const WalletsAdd: React.FC = () => {
|
||||||
});
|
});
|
||||||
}, [colorScheme, setOptions]);
|
}, [colorScheme, setOptions]);
|
||||||
|
|
||||||
const entropyGenerated = (newEntropy: string | any[]) => {
|
const entropyGenerated = (newEntropy: Buffer) => {
|
||||||
let entropyTitle;
|
let entropyTitle;
|
||||||
if (!newEntropy) {
|
if (!newEntropy) {
|
||||||
entropyTitle = loc.wallets.add_entropy_provide;
|
entropyTitle = loc.wallets.add_entropy_provide;
|
||||||
} else if (newEntropy.length < 32) {
|
|
||||||
entropyTitle = loc.formatString(loc.wallets.add_entropy_remain, {
|
|
||||||
gen: newEntropy.length,
|
|
||||||
rem: 32 - newEntropy.length,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
entropyTitle = loc.formatString(loc.wallets.add_entropy_generated, {
|
entropyTitle = loc.formatString(loc.wallets.add_entropy_bytes, {
|
||||||
gen: newEntropy.length,
|
bytes: newEntropy.length,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setEntropy(newEntropy);
|
setEntropy(newEntropy);
|
||||||
|
@ -203,7 +199,7 @@ const WalletsAdd: React.FC = () => {
|
||||||
dispatch({ type: 'INCREMENT_BACKDOOR_PRESSED', payload: value });
|
dispatch({ type: 'INCREMENT_BACKDOOR_PRESSED', payload: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const setEntropy = (value: string | any[]) => {
|
const setEntropy = (value: Buffer) => {
|
||||||
dispatch({ type: 'SET_ENTROPY', payload: value });
|
dispatch({ type: 'SET_ENTROPY', payload: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -236,7 +232,6 @@ const WalletsAdd: React.FC = () => {
|
||||||
if (selectedWalletType === ButtonSelected.ONCHAIN) {
|
if (selectedWalletType === ButtonSelected.ONCHAIN) {
|
||||||
if (entropy) {
|
if (entropy) {
|
||||||
try {
|
try {
|
||||||
// @ts-ignore: Return later to update
|
|
||||||
await w.generateFromEntropy(entropy);
|
await w.generateFromEntropy(entropy);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log(e.toString());
|
console.log(e.toString());
|
||||||
|
@ -334,8 +329,34 @@ const WalletsAdd: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToEntropy = () => {
|
const navigateToEntropy = () => {
|
||||||
|
Alert.alert(
|
||||||
|
loc.wallets.add_wallet_seed_length,
|
||||||
|
loc.wallets.add_wallet_seed_length_message,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: loc._.cancel,
|
||||||
|
onPress: () => {},
|
||||||
|
style: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: loc.wallets.add_wallet_seed_length_12,
|
||||||
|
onPress: () => {
|
||||||
// @ts-ignore: Return later to update
|
// @ts-ignore: Return later to update
|
||||||
navigate('ProvideEntropy', { onGenerated: entropyGenerated });
|
navigate('ProvideEntropy', { onGenerated: entropyGenerated, words: 12 });
|
||||||
|
},
|
||||||
|
style: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: loc.wallets.add_wallet_seed_length_24,
|
||||||
|
onPress: () => {
|
||||||
|
// @ts-ignore: Return later to update
|
||||||
|
navigate('ProvideEntropy', { onGenerated: entropyGenerated, words: 24 });
|
||||||
|
},
|
||||||
|
style: 'default',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{ cancelable: true },
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToImportWallet = () => {
|
const navigateToImportWallet = () => {
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||||
import BN from 'bignumber.js';
|
import BN from 'bignumber.js';
|
||||||
import PropTypes from 'prop-types';
|
import React, { useEffect, useReducer, useState } from 'react';
|
||||||
import React, { useReducer, useState } from 'react';
|
import {
|
||||||
import { Dimensions, Image, PixelRatio, ScrollView, StyleSheet, Text, TouchableOpacity, useWindowDimensions, View } from 'react-native';
|
Alert,
|
||||||
|
Dimensions,
|
||||||
|
Image,
|
||||||
|
PixelRatio,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
useWindowDimensions,
|
||||||
|
} from 'react-native';
|
||||||
import { Icon } from 'react-native-elements';
|
import { Icon } from 'react-native-elements';
|
||||||
|
|
||||||
import { BlueSpacing20 } from '../../BlueComponents';
|
import { BlueSpacing20 } from '../../BlueComponents';
|
||||||
|
@ -11,48 +21,81 @@ import SafeArea from '../../components/SafeArea';
|
||||||
import { Tabs } from '../../components/Tabs';
|
import { Tabs } from '../../components/Tabs';
|
||||||
import { BlueCurrentTheme, useTheme } from '../../components/themes';
|
import { BlueCurrentTheme, useTheme } from '../../components/themes';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
|
import { randomBytes } from '../../class/rng';
|
||||||
|
|
||||||
const ENTROPY_LIMIT = 256;
|
export enum EActionType {
|
||||||
|
push = 'push',
|
||||||
|
pop = 'pop',
|
||||||
|
limit = 'limit',
|
||||||
|
noop = 'noop',
|
||||||
|
}
|
||||||
|
|
||||||
const shiftLeft = (value, places) => value.multipliedBy(2 ** places);
|
type TEntropy = { value: number; bits: number };
|
||||||
const shiftRight = (value, places) => value.div(2 ** places).dp(0, BN.ROUND_DOWN);
|
type TState = { entropy: BN; bits: number; items: number[]; limit: number };
|
||||||
|
type TAction =
|
||||||
|
| ({ type: EActionType.push } & TEntropy)
|
||||||
|
| { type: EActionType.pop }
|
||||||
|
| { type: EActionType.limit; limit: number }
|
||||||
|
| { type: EActionType.noop };
|
||||||
|
type TPush = (v: TEntropy | null) => void;
|
||||||
|
type TPop = () => void;
|
||||||
|
|
||||||
const initialState = { entropy: BN(0), bits: 0, items: [] };
|
const initialState: TState = {
|
||||||
export const eReducer = (state = initialState, action) => {
|
entropy: BN(0),
|
||||||
|
bits: 0,
|
||||||
|
items: [],
|
||||||
|
limit: 256,
|
||||||
|
};
|
||||||
|
|
||||||
|
const shiftLeft = (value: BN, places: number) => value.multipliedBy(2 ** places);
|
||||||
|
const shiftRight = (value: BN, places: number) => value.div(2 ** places).dp(0, BN.ROUND_DOWN);
|
||||||
|
|
||||||
|
export const eReducer = (state: TState = initialState, action: TAction): TState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'push': {
|
case EActionType.noop:
|
||||||
let { value, bits } = action;
|
return state;
|
||||||
|
|
||||||
|
case EActionType.push: {
|
||||||
|
let value: number | BN = action.value;
|
||||||
|
let bits: number = action.bits;
|
||||||
|
|
||||||
if (value >= 2 ** bits) {
|
if (value >= 2 ** bits) {
|
||||||
throw new TypeError("Can't push value exceeding size in bits");
|
throw new TypeError("Can't push value exceeding size in bits");
|
||||||
}
|
}
|
||||||
if (state.bits === ENTROPY_LIMIT) return state;
|
if (state.bits === state.limit) return state;
|
||||||
if (state.bits + bits > ENTROPY_LIMIT) {
|
if (state.bits + bits > state.limit) {
|
||||||
value = shiftRight(BN(value), bits + state.bits - ENTROPY_LIMIT);
|
value = shiftRight(BN(value), bits + state.bits - state.limit);
|
||||||
bits = ENTROPY_LIMIT - state.bits;
|
bits = state.limit - state.bits;
|
||||||
}
|
}
|
||||||
const entropy = shiftLeft(state.entropy, bits).plus(value);
|
const entropy = shiftLeft(state.entropy, bits).plus(value);
|
||||||
const items = [...state.items, bits];
|
const items = [...state.items, bits];
|
||||||
return { entropy, bits: state.bits + bits, items };
|
return { ...state, entropy, bits: state.bits + bits, items };
|
||||||
}
|
}
|
||||||
case 'pop': {
|
|
||||||
|
case EActionType.pop: {
|
||||||
if (state.bits === 0) return state;
|
if (state.bits === 0) return state;
|
||||||
const bits = state.items.pop();
|
const bits = state.items.pop()!;
|
||||||
const entropy = shiftRight(state.entropy, bits);
|
const entropy = shiftRight(state.entropy, bits);
|
||||||
return { entropy, bits: state.bits - bits, items: [...state.items] };
|
return { ...state, entropy, bits: state.bits - bits, items: [...state.items] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case EActionType.limit: {
|
||||||
|
return { ...state, limit: action.limit };
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const entropyToHex = ({ entropy, bits }) => {
|
export const entropyToHex = ({ entropy, bits }: { entropy: BN; bits: number }): string => {
|
||||||
if (bits === 0) return '0x';
|
if (bits === 0) return '0x';
|
||||||
const hex = entropy.toString(16);
|
const hex = entropy.toString(16);
|
||||||
const hexSize = Math.floor((bits - 1) / 4) + 1;
|
const hexSize = Math.floor((bits - 1) / 4) + 1;
|
||||||
return '0x' + '0'.repeat(hexSize - hex.length) + hex;
|
return '0x' + '0'.repeat(hexSize - hex.length) + hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEntropy = (number, base) => {
|
export const getEntropy = (number: number, base: number): TEntropy | null => {
|
||||||
if (base === 1) return null;
|
if (base === 1) return null;
|
||||||
let maxPow = 1;
|
let maxPow = 1;
|
||||||
while (2 ** (maxPow + 1) <= base) {
|
while (2 ** (maxPow + 1) <= base) {
|
||||||
|
@ -77,12 +120,12 @@ export const getEntropy = (number, base) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// cut entropy to bytes, convert to Buffer
|
// cut entropy to bytes, convert to Buffer
|
||||||
export const convertToBuffer = ({ entropy, bits }) => {
|
export const convertToBuffer = ({ entropy, bits }: { entropy: BN; bits: number }): Buffer => {
|
||||||
if (bits < 8) return Buffer.from([]);
|
if (bits < 8) return Buffer.from([]);
|
||||||
const bytes = Math.floor(bits / 8);
|
const bytes = Math.floor(bits / 8);
|
||||||
|
|
||||||
// convert to byte array
|
// convert to byte array
|
||||||
let arr = [];
|
const arr: string[] = [];
|
||||||
const ent = entropy.toString(16).split('').reverse();
|
const ent = entropy.toString(16).split('').reverse();
|
||||||
ent.forEach((v, index) => {
|
ent.forEach((v, index) => {
|
||||||
if (index % 2 === 1) {
|
if (index % 2 === 1) {
|
||||||
|
@ -91,18 +134,19 @@ export const convertToBuffer = ({ entropy, bits }) => {
|
||||||
arr.unshift(v);
|
arr.unshift(v);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
arr = arr.map(i => parseInt(i, 16));
|
|
||||||
|
let arr2 = arr.map(i => parseInt(i, 16));
|
||||||
|
|
||||||
if (arr.length > bytes) {
|
if (arr.length > bytes) {
|
||||||
arr.shift();
|
arr2.shift();
|
||||||
} else if (arr.length < bytes) {
|
} else if (arr2.length < bytes) {
|
||||||
const zeros = [...Array(bytes - arr.length)].map(() => 0);
|
const zeros = [...Array(bytes - arr2.length)].map(() => 0);
|
||||||
arr = [...zeros, ...arr];
|
arr2 = [...zeros, ...arr2];
|
||||||
}
|
}
|
||||||
return Buffer.from(arr);
|
return Buffer.from(arr2);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Coin = ({ push }) => (
|
const Coin = ({ push }: { push: TPush }) => (
|
||||||
<View style={styles.coinRoot}>
|
<View style={styles.coinRoot}>
|
||||||
<TouchableOpacity accessibilityRole="button" onPress={() => push(getEntropy(0, 2))} style={styles.coinBody}>
|
<TouchableOpacity accessibilityRole="button" onPress={() => push(getEntropy(0, 2))} style={styles.coinBody}>
|
||||||
<Image style={styles.coinImage} source={require('../../img/coin1.png')} />
|
<Image style={styles.coinImage} source={require('../../img/coin1.png')} />
|
||||||
|
@ -113,26 +157,7 @@ const Coin = ({ push }) => (
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
Coin.propTypes = {
|
const diceIcon = (i: number): string => {
|
||||||
push: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Dice = ({ push, sides }) => {
|
|
||||||
const { width } = useWindowDimensions();
|
|
||||||
const { colors } = useTheme();
|
|
||||||
const diceWidth = width / 4;
|
|
||||||
const stylesHook = StyleSheet.create({
|
|
||||||
dice: {
|
|
||||||
borderColor: colors.buttonBackgroundColor,
|
|
||||||
},
|
|
||||||
diceText: {
|
|
||||||
color: colors.foregroundColor,
|
|
||||||
},
|
|
||||||
diceContainer: {
|
|
||||||
backgroundColor: colors.elevated,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const diceIcon = i => {
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 1:
|
case 1:
|
||||||
return 'dice-one';
|
return 'dice-one';
|
||||||
|
@ -147,7 +172,23 @@ const Dice = ({ push, sides }) => {
|
||||||
default:
|
default:
|
||||||
return 'dice-six';
|
return 'dice-six';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Dice = ({ push, sides }: { push: TPush; sides: number }) => {
|
||||||
|
const { width } = useWindowDimensions();
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const diceWidth = width / 4;
|
||||||
|
const stylesHook = StyleSheet.create({
|
||||||
|
dice: {
|
||||||
|
borderColor: colors.buttonBackgroundColor,
|
||||||
|
},
|
||||||
|
diceText: {
|
||||||
|
color: colors.foregroundColor,
|
||||||
|
},
|
||||||
|
diceContainer: {
|
||||||
|
backgroundColor: colors.elevated,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView contentContainerStyle={[styles.diceContainer, stylesHook.diceContainer]}>
|
<ScrollView contentContainerStyle={[styles.diceContainer, stylesHook.diceContainer]}>
|
||||||
|
@ -168,48 +209,53 @@ const Dice = ({ push, sides }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Dice.propTypes = {
|
|
||||||
sides: PropTypes.number.isRequired,
|
|
||||||
push: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonFontSize =
|
const buttonFontSize =
|
||||||
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
||||||
? 22
|
? 22
|
||||||
: PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
|
: PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
|
||||||
|
|
||||||
const Buttons = ({ pop, save, colors }) => (
|
const Buttons = ({ pop, save, colors }: { pop: TPop; save: () => void; colors: any }) => (
|
||||||
<FContainer>
|
<FContainer>
|
||||||
<FButton
|
<FButton
|
||||||
onPress={pop}
|
onPress={pop}
|
||||||
|
text={loc.entropy.undo}
|
||||||
icon={
|
icon={
|
||||||
<View style={styles.buttonsIcon}>
|
<View style={styles.buttonsIcon}>
|
||||||
<Icon name="undo" size={buttonFontSize} type="font-awesome" color={colors.buttonAlternativeTextColor} />
|
<Icon name="undo" size={buttonFontSize} type="font-awesome" color={colors.buttonAlternativeTextColor} />
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
text={loc.entropy.undo}
|
|
||||||
/>
|
/>
|
||||||
<FButton
|
<FButton
|
||||||
onPress={save}
|
onPress={save}
|
||||||
|
text={loc.entropy.save}
|
||||||
icon={
|
icon={
|
||||||
<View style={styles.buttonsIcon}>
|
<View style={styles.buttonsIcon}>
|
||||||
<Icon name="arrow-down" size={buttonFontSize} type="font-awesome" color={colors.buttonAlternativeTextColor} />
|
<Icon name="arrow-down" size={buttonFontSize} type="font-awesome" color={colors.buttonAlternativeTextColor} />
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
text={loc.entropy.save}
|
|
||||||
/>
|
/>
|
||||||
</FContainer>
|
</FContainer>
|
||||||
);
|
);
|
||||||
|
|
||||||
Buttons.propTypes = {
|
const TollTab = ({ active }: { active: boolean }) => {
|
||||||
pop: PropTypes.func.isRequired,
|
const { colors } = useTheme();
|
||||||
save: PropTypes.func.isRequired,
|
return <Icon name="toll" type="material" color={active ? colors.buttonAlternativeTextColor : colors.buttonBackgroundColor} />;
|
||||||
colors: PropTypes.shape.isRequired,
|
};
|
||||||
|
|
||||||
|
const D6Tab = ({ active }: { active: boolean }) => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return <Icon name="dice" type="font-awesome-5" color={active ? colors.buttonAlternativeTextColor : colors.buttonBackgroundColor} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const D20Tab = ({ active }: { active: boolean }) => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return <Icon name="dice-d20" type="font-awesome-5" color={active ? colors.buttonAlternativeTextColor : colors.buttonBackgroundColor} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Entropy = () => {
|
const Entropy = () => {
|
||||||
const [entropy, dispatch] = useReducer(eReducer, initialState);
|
const [entropy, dispatch] = useReducer(eReducer, initialState);
|
||||||
const { onGenerated } = useRoute().params;
|
// @ts-ignore: navigation is not typed yet
|
||||||
|
const { onGenerated, words } = useRoute().params;
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const [tab, setTab] = useState(1);
|
const [tab, setTab] = useState(1);
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
|
@ -223,12 +269,65 @@ const Entropy = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const push = v => v && dispatch({ type: 'push', value: v.value, bits: v.bits });
|
useEffect(() => {
|
||||||
const pop = () => dispatch({ type: 'pop' });
|
dispatch({ type: EActionType.limit, limit: words === 24 ? 256 : 128 });
|
||||||
const save = () => {
|
}, [dispatch, words]);
|
||||||
|
|
||||||
|
const handlePush: TPush = v => {
|
||||||
|
if (v === null) {
|
||||||
|
dispatch({ type: EActionType.noop });
|
||||||
|
} else {
|
||||||
|
dispatch({ type: EActionType.push, value: v.value, bits: v.bits });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePop: TPop = () => {
|
||||||
|
dispatch({ type: EActionType.pop });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
let buf = convertToBuffer(entropy);
|
||||||
|
const bufLength = words === 24 ? 32 : 16;
|
||||||
|
const remaining = bufLength - buf.length;
|
||||||
|
|
||||||
|
let entropyTitle = '';
|
||||||
|
if (buf.length < bufLength) {
|
||||||
|
entropyTitle = loc.formatString(loc.wallets.add_entropy_remain, {
|
||||||
|
gen: buf.length,
|
||||||
|
rem: remaining,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
entropyTitle = loc.formatString(loc.wallets.add_entropy_generated, {
|
||||||
|
gen: buf.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Alert.alert(
|
||||||
|
loc.wallets.add_entropy,
|
||||||
|
entropyTitle,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: loc._.ok,
|
||||||
|
onPress: async () => {
|
||||||
|
if (remaining > 0) {
|
||||||
|
const random = await randomBytes(remaining);
|
||||||
|
buf = Buffer.concat([buf, random], bufLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore: navigation is not typed yet
|
||||||
navigation.pop();
|
navigation.pop();
|
||||||
const buf = convertToBuffer(entropy);
|
|
||||||
onGenerated(buf);
|
onGenerated(buf);
|
||||||
|
},
|
||||||
|
style: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: loc._.cancel,
|
||||||
|
onPress: () => {},
|
||||||
|
style: 'default',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{ cancelable: true },
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const hex = entropyToHex(entropy);
|
const hex = entropyToHex(entropy);
|
||||||
|
@ -240,34 +339,19 @@ const Entropy = () => {
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
<TouchableOpacity accessibilityRole="button" onPress={() => setShow(!show)}>
|
<TouchableOpacity accessibilityRole="button" onPress={() => setShow(!show)}>
|
||||||
<View style={[styles.entropy, stylesHook.entropy]}>
|
<View style={[styles.entropy, stylesHook.entropy]}>
|
||||||
<Text style={[styles.entropyText, stylesHook.entropyText]}>{show ? hex : `${bits} of 256 bits`}</Text>
|
<Text style={[styles.entropyText, stylesHook.entropyText]}>
|
||||||
|
{show ? hex : loc.formatString(loc.entropy.amountOfEntropy, { bits, limit: entropy.limit })}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<Tabs
|
<Tabs active={tab} onSwitch={setTab} tabs={[TollTab, D6Tab, D20Tab]} />
|
||||||
active={tab}
|
|
||||||
onSwitch={setTab}
|
|
||||||
tabs={[
|
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
|
||||||
({ active }) => (
|
|
||||||
<Icon name="toll" type="material" color={active ? colors.buttonAlternativeTextColor : colors.buttonBackgroundColor} />
|
|
||||||
),
|
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
|
||||||
({ active }) => (
|
|
||||||
<Icon name="dice" type="font-awesome-5" color={active ? colors.buttonAlternativeTextColor : colors.buttonBackgroundColor} />
|
|
||||||
),
|
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
|
||||||
({ active }) => (
|
|
||||||
<Icon name="dice-d20" type="font-awesome-5" color={active ? colors.buttonAlternativeTextColor : colors.buttonBackgroundColor} />
|
|
||||||
),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{tab === 0 && <Coin push={push} />}
|
{tab === 0 && <Coin push={handlePush} />}
|
||||||
{tab === 1 && <Dice sides={6} push={push} />}
|
{tab === 1 && <Dice sides={6} push={handlePush} />}
|
||||||
{tab === 2 && <Dice sides={20} push={push} />}
|
{tab === 2 && <Dice sides={20} push={handlePush} />}
|
||||||
|
|
||||||
<Buttons pop={pop} save={save} colors={colors} />
|
<Buttons pop={handlePop} save={handleSave} colors={colors} />
|
||||||
</SafeArea>
|
</SafeArea>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -140,31 +140,26 @@ describe('P2SH Segwit HD (BIP49)', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can consume user generated entropy', async () => {
|
it('can consume user generated entropy', async () => {
|
||||||
const hd = new HDSegwitP2SHWallet();
|
const hd1 = new HDSegwitP2SHWallet();
|
||||||
const zeroes = [...Array(32)].map(() => 0);
|
const zeroes16 = [...Array(16)].map(() => 0);
|
||||||
await hd.generateFromEntropy(Buffer.from(zeroes));
|
await hd1.generateFromEntropy(Buffer.from(zeroes16));
|
||||||
|
assert.strictEqual(hd1.getSecret(), 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
|
||||||
|
|
||||||
|
const hd2 = new HDSegwitP2SHWallet();
|
||||||
|
const zeroes32 = [...Array(32)].map(() => 0);
|
||||||
|
await hd2.generateFromEntropy(Buffer.from(zeroes32));
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
hd.getSecret(),
|
hd2.getSecret(),
|
||||||
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art',
|
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can fullfill user generated entropy if less than 32 bytes provided', async () => {
|
it('throws an error if not 32 bytes provided', async () => {
|
||||||
const hd = new HDSegwitP2SHWallet();
|
const hd = new HDSegwitP2SHWallet();
|
||||||
const zeroes = [...Array(16)].map(() => 0);
|
const values = [...Array(31)].map(() => 1);
|
||||||
await hd.generateFromEntropy(Buffer.from(zeroes));
|
await assert.rejects(async () => await hd.generateFromEntropy(Buffer.from(values)), {
|
||||||
const secret = hd.getSecret();
|
message: 'Entropy has to be 16 or 32 bytes long',
|
||||||
assert.strictEqual(secret.startsWith('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon'), true);
|
});
|
||||||
|
|
||||||
const secretWithoutChecksum = secret.split(' ');
|
|
||||||
secretWithoutChecksum.pop();
|
|
||||||
const secretWithoutChecksumString = secretWithoutChecksum.join(' ');
|
|
||||||
assert.strictEqual(
|
|
||||||
secretWithoutChecksumString.endsWith('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon'),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(secret.split(' ').length === 12 || secret.split(' ').length === 24);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can sign and verify messages', async () => {
|
it('can sign and verify messages', async () => {
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import * as bitcoin from 'bitcoinjs-lib';
|
import * as bitcoin from 'bitcoinjs-lib';
|
||||||
import { ECPairFactory } from 'ecpair';
|
|
||||||
|
|
||||||
import ecc from '../../blue_modules/noble_ecc';
|
|
||||||
import { LegacyWallet } from '../../class';
|
import { LegacyWallet } from '../../class';
|
||||||
const ECPair = ECPairFactory(ecc);
|
|
||||||
|
|
||||||
describe('Legacy wallet', () => {
|
describe('Legacy wallet', () => {
|
||||||
it('can validate addresses', () => {
|
it('can validate addresses', () => {
|
||||||
|
@ -146,17 +143,12 @@ describe('Legacy wallet', () => {
|
||||||
assert.strictEqual(l.getSecret(), 'KwFfNUhSDaASSAwtG7ssQM1uVX8RgX5GHWnnLfhfiQDigjioWXHH');
|
assert.strictEqual(l.getSecret(), 'KwFfNUhSDaASSAwtG7ssQM1uVX8RgX5GHWnnLfhfiQDigjioWXHH');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can fullfill user generated entropy if less than 32 bytes provided', async () => {
|
it('throws an error if not 32 bytes provided', async () => {
|
||||||
const l = new LegacyWallet();
|
const l = new LegacyWallet();
|
||||||
const values = [...Array(16)].map(() => 1);
|
const values = [...Array(31)].map(() => 1);
|
||||||
await l.generateFromEntropy(Buffer.from(values));
|
await assert.rejects(async () => await l.generateFromEntropy(Buffer.from(values)), {
|
||||||
assert.strictEqual(l.getSecret().startsWith('KwFfNUhSDaASSAwtG7ssQM'), true);
|
message: 'Entropy should be 32 bytes',
|
||||||
assert.strictEqual(l.getSecret().endsWith('GHWnnLfhfiQDigjioWXHH'), false);
|
});
|
||||||
const keyPair = ECPair.fromWIF(l.getSecret());
|
|
||||||
assert.strictEqual(keyPair.privateKey.toString('hex').startsWith('01010101'), true);
|
|
||||||
assert.strictEqual(keyPair.privateKey.toString('hex').endsWith('01010101'), false);
|
|
||||||
assert.strictEqual(keyPair.privateKey.toString('hex').endsWith('00000000'), false);
|
|
||||||
assert.strictEqual(keyPair.privateKey.toString('hex').endsWith('ffffffff'), false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can sign and verify messages', async () => {
|
it('can sign and verify messages', async () => {
|
||||||
|
|
|
@ -1,69 +1,69 @@
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import { convertToBuffer, entropyToHex, eReducer, getEntropy } from '../../screen/wallets/provideEntropy';
|
import { convertToBuffer, EActionType, entropyToHex, eReducer, getEntropy } from '../../screen/wallets/ProvideEntropy';
|
||||||
|
|
||||||
describe('Entropy reducer and format', () => {
|
describe('Entropy reducer and format', () => {
|
||||||
it('handles push and pop correctly', () => {
|
it('handles push and pop correctly', () => {
|
||||||
let state = eReducer(undefined, { type: null });
|
let state = eReducer(undefined, { type: EActionType.noop });
|
||||||
assert.equal(entropyToHex(state), '0x');
|
assert.equal(entropyToHex(state), '0x');
|
||||||
|
|
||||||
state = eReducer(state, { type: 'push', value: 0, bits: 1 });
|
state = eReducer(state, { type: EActionType.push, value: 0, bits: 1 });
|
||||||
assert.equal(entropyToHex(state), '0x0');
|
assert.equal(entropyToHex(state), '0x0');
|
||||||
|
|
||||||
state = eReducer(state, { type: 'push', value: 0, bits: 1 });
|
state = eReducer(state, { type: EActionType.push, value: 0, bits: 1 });
|
||||||
assert.equal(entropyToHex(state), '0x0');
|
assert.equal(entropyToHex(state), '0x0');
|
||||||
|
|
||||||
state = eReducer(state, { type: 'push', value: 0, bits: 3 });
|
state = eReducer(state, { type: EActionType.push, value: 0, bits: 3 });
|
||||||
assert.equal(entropyToHex(state), '0x00');
|
assert.equal(entropyToHex(state), '0x00');
|
||||||
|
|
||||||
state = eReducer(state, { type: 'pop' });
|
state = eReducer(state, { type: EActionType.pop });
|
||||||
assert.equal(entropyToHex(state), '0x0');
|
assert.equal(entropyToHex(state), '0x0');
|
||||||
|
|
||||||
state = eReducer(state, { type: 'pop' });
|
state = eReducer(state, { type: EActionType.pop });
|
||||||
state = eReducer(state, { type: 'pop' });
|
state = eReducer(state, { type: EActionType.pop });
|
||||||
assert.equal(entropyToHex(state), '0x');
|
assert.equal(entropyToHex(state), '0x');
|
||||||
|
|
||||||
state = eReducer(state, { type: 'push', value: 1, bits: 1 });
|
state = eReducer(state, { type: EActionType.push, value: 1, bits: 1 });
|
||||||
assert.equal(entropyToHex(state), '0x1'); // 0b1
|
assert.equal(entropyToHex(state), '0x1'); // 0b1
|
||||||
|
|
||||||
state = eReducer(state, { type: 'push', value: 0, bits: 1 });
|
state = eReducer(state, { type: EActionType.push, value: 0, bits: 1 });
|
||||||
assert.equal(entropyToHex(state), '0x2'); // 0b10
|
assert.equal(entropyToHex(state), '0x2'); // 0b10
|
||||||
|
|
||||||
state = eReducer(state, { type: 'push', value: 0b01, bits: 2 });
|
state = eReducer(state, { type: EActionType.push, value: 0b01, bits: 2 });
|
||||||
assert.equal(entropyToHex(state), '0x9'); // 0b1001
|
assert.equal(entropyToHex(state), '0x9'); // 0b1001
|
||||||
|
|
||||||
state = eReducer(state, { type: 'push', value: 0b10, bits: 2 });
|
state = eReducer(state, { type: EActionType.push, value: 0b10, bits: 2 });
|
||||||
assert.equal(entropyToHex(state), '0x26'); // 0b100110
|
assert.equal(entropyToHex(state), '0x26'); // 0b100110
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles 128 bits correctly', () => {
|
it('handles 128 bits correctly', () => {
|
||||||
const state = eReducer(undefined, { type: 'push', value: 0, bits: 128 });
|
const state = eReducer(undefined, { type: EActionType.push, value: 0, bits: 128 });
|
||||||
assert.equal(entropyToHex(state), '0x00000000000000000000000000000000');
|
assert.equal(entropyToHex(state), '0x00000000000000000000000000000000');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles 256 bits correctly', () => {
|
it('handles 256 bits correctly', () => {
|
||||||
let state = eReducer(undefined, { type: null }); // get init state
|
let state = eReducer(undefined, { type: EActionType.noop }); // get init state
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
for (const i of [...Array(256)]) {
|
for (const i of [...Array(256)]) {
|
||||||
state = eReducer(state, { type: 'push', value: 1, bits: 1 });
|
state = eReducer(state, { type: EActionType.push, value: 1, bits: 1 });
|
||||||
}
|
}
|
||||||
assert.equal(entropyToHex(state), '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
assert.equal(entropyToHex(state), '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles pop when empty without error', () => {
|
it('handles pop when empty without error', () => {
|
||||||
const state = eReducer(undefined, { type: 'pop' });
|
const state = eReducer(undefined, { type: EActionType.pop });
|
||||||
assert.equal(entropyToHex(state), '0x');
|
assert.equal(entropyToHex(state), '0x');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles 256 bits limit', () => {
|
it('handles 256 bits limit', () => {
|
||||||
let state = eReducer(undefined, { type: 'push', value: 0, bits: 254 });
|
let state = eReducer(undefined, { type: EActionType.push, value: 0, bits: 254 });
|
||||||
state = eReducer(state, { type: 'push', value: 0b101, bits: 3 });
|
state = eReducer(state, { type: EActionType.push, value: 0b101, bits: 3 });
|
||||||
assert.equal(entropyToHex(state), '0x0000000000000000000000000000000000000000000000000000000000000002');
|
assert.equal(entropyToHex(state), '0x0000000000000000000000000000000000000000000000000000000000000002');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Throws error if you try to push value exceeding size in bits', () => {
|
it('Throws error if you try to push value exceeding size in bits', () => {
|
||||||
assert.throws(() => eReducer(undefined, { type: 'push', value: 8, bits: 3 }), {
|
assert.throws(() => eReducer(undefined, { type: EActionType.push, value: 8, bits: 3 }), {
|
||||||
name: 'TypeError',
|
name: 'TypeError',
|
||||||
message: "Can't push value exceeding size in bits",
|
message: "Can't push value exceeding size in bits",
|
||||||
});
|
});
|
||||||
|
@ -104,48 +104,48 @@ describe('getEntropy function', () => {
|
||||||
|
|
||||||
describe('convertToBuffer function', () => {
|
describe('convertToBuffer function', () => {
|
||||||
it('zero bits', () => {
|
it('zero bits', () => {
|
||||||
const state = eReducer(undefined, { type: null });
|
const state = eReducer(undefined, { type: EActionType.noop });
|
||||||
assert.deepEqual(convertToBuffer(state), Buffer.from([]));
|
assert.deepEqual(convertToBuffer(state), Buffer.from([]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('8 zero bits', () => {
|
it('8 zero bits', () => {
|
||||||
const state = eReducer(undefined, { type: 'push', value: 0, bits: 8 });
|
const state = eReducer(undefined, { type: EActionType.push, value: 0, bits: 8 });
|
||||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0]));
|
assert.deepEqual(convertToBuffer(state), Buffer.from([0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('8 filled bits', () => {
|
it('8 filled bits', () => {
|
||||||
const state = eReducer(undefined, { type: 'push', value: 0b11111111, bits: 8 });
|
const state = eReducer(undefined, { type: EActionType.push, value: 0b11111111, bits: 8 });
|
||||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11111111]));
|
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11111111]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('9 zero bits', () => {
|
it('9 zero bits', () => {
|
||||||
const state = eReducer(undefined, { type: 'push', value: 0, bits: 9 });
|
const state = eReducer(undefined, { type: EActionType.push, value: 0, bits: 9 });
|
||||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0]));
|
assert.deepEqual(convertToBuffer(state), Buffer.from([0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('9 filled bits', () => {
|
it('9 filled bits', () => {
|
||||||
const state = eReducer(undefined, { type: 'push', value: 0b111111111, bits: 9 });
|
const state = eReducer(undefined, { type: EActionType.push, value: 0b111111111, bits: 9 });
|
||||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11111111]));
|
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11111111]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('9 bits', () => {
|
it('9 bits', () => {
|
||||||
const state = eReducer(undefined, { type: 'push', value: 0b111100111, bits: 9 });
|
const state = eReducer(undefined, { type: EActionType.push, value: 0b111100111, bits: 9 });
|
||||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11100111]));
|
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11100111]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('3 bytes', () => {
|
it('3 bytes', () => {
|
||||||
let state = eReducer(undefined, { type: 'push', value: 1, bits: 8 });
|
let state = eReducer(undefined, { type: EActionType.push, value: 1, bits: 8 });
|
||||||
state = eReducer(state, { type: 'push', value: 2, bits: 8 });
|
state = eReducer(state, { type: EActionType.push, value: 2, bits: 8 });
|
||||||
state = eReducer(state, { type: 'push', value: 3, bits: 8 });
|
state = eReducer(state, { type: EActionType.push, value: 3, bits: 8 });
|
||||||
assert.deepEqual(convertToBuffer(state), Buffer.from([1, 2, 3]));
|
assert.deepEqual(convertToBuffer(state), Buffer.from([1, 2, 3]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('256 bits or 32bytes', () => {
|
it('256 bits or 32bytes', () => {
|
||||||
let state = eReducer(undefined, { type: null }); // get init state
|
let state = eReducer(undefined, { type: EActionType.noop }); // get init state
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
for (const i of [...Array(256)]) {
|
for (const i of [...Array(256)]) {
|
||||||
state = eReducer(state, { type: 'push', value: 1, bits: 1 });
|
state = eReducer(state, { type: EActionType.push, value: 1, bits: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const bytes = [...Array(32)].map(() => 255);
|
const bytes = [...Array(32)].map(() => 255);
|
Loading…
Add table
Reference in a new issue