mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-13 11:09:20 +01:00
ADD: Entropy generator (#1233)
This commit is contained in:
parent
003ad1213f
commit
09b81c5941
45 changed files with 923 additions and 17 deletions
|
@ -2537,6 +2537,42 @@ export function BlueBigCheckmark({ style }) {
|
|||
);
|
||||
}
|
||||
|
||||
const tabsStyles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row',
|
||||
height: 50,
|
||||
borderColor: '#e3e3e3',
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
tabRoot: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderColor: 'white',
|
||||
borderBottomWidth: 2,
|
||||
},
|
||||
});
|
||||
|
||||
export const BlueTabs = ({ active, onSwitch, tabs }) => (
|
||||
<View style={tabsStyles.root}>
|
||||
{tabs.map((Tab, i) => (
|
||||
<TouchableOpacity
|
||||
key={i}
|
||||
onPress={() => onSwitch(i)}
|
||||
style={[
|
||||
tabsStyles.tabRoot,
|
||||
active === i && {
|
||||
borderColor: BlueApp.settings.buttonAlternativeTextColor,
|
||||
borderBottomWidth: 2,
|
||||
},
|
||||
{ width: width / tabs.length },
|
||||
]}
|
||||
>
|
||||
<Tab active={active === i} />
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
|
||||
export class DynamicQRCode extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
|
|
@ -36,6 +36,7 @@ import HodlHodlMyContracts from './screen/wallets/hodlHodlMyContracts';
|
|||
import Marketplace from './screen/wallets/marketplace';
|
||||
import ReorderWallets from './screen/wallets/reorderWallets';
|
||||
import SelectWallet from './screen/wallets/selectWallet';
|
||||
import ProvideEntropy from './screen/wallets/provideEntropy';
|
||||
|
||||
import TransactionDetails from './screen/transactions/details';
|
||||
import TransactionStatus from './screen/transactions/transactionStatus';
|
||||
|
@ -161,6 +162,7 @@ const AddWalletRoot = () => (
|
|||
<AddWalletStack.Screen name="ImportWallet" component={ImportWallet} options={ImportWallet.navigationOptions} />
|
||||
<AddWalletStack.Screen name="PleaseBackup" component={PleaseBackup} options={PleaseBackup.navigationOptions} />
|
||||
<AddWalletStack.Screen name="PleaseBackupLNDHub" component={PleaseBackupLNDHub} options={PleaseBackupLNDHub.navigationOptions} />
|
||||
<AddWalletStack.Screen name="ProvideEntropy" component={ProvideEntropy} options={ProvideEntropy.navigationOptions} />
|
||||
</AddWalletStack.Navigator>
|
||||
);
|
||||
|
||||
|
@ -299,7 +301,7 @@ const Navigation = () => (
|
|||
options={{ headerShown: false, animationEnabled: false }}
|
||||
/>
|
||||
<RootStack.Screen name="WalletsRoot" component={WalletsRoot} options={{ headerShown: false, animationEnabled: false }} />
|
||||
<RootStack.Screen name="AddWalletRoot" component={AddWalletRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="AddWalletRoot" component={AddWalletRoot} options={{ headerShown: false, gestureEnabled: false }} />
|
||||
<RootStack.Screen name="SendDetailsRoot" component={SendDetailsRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="LNDCreateInvoiceRoot" component={LNDCreateInvoiceRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="ScanLndInvoiceRoot" component={ScanLndInvoiceRoot} options={{ headerShown: false }} />
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
"_requiredBy": [
|
||||
"/bc-ur"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/bc-bech32/-/bc-bech32-1.0.2.tgz",
|
||||
"_spec": "1.0.2",
|
||||
"_where": "/home/user/BlueWallet",
|
||||
"description": "this library is for implementing [BlockChain Commons bc-32](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-0004-bc32.md)",
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
"_requiredBy": [
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/bc-ur/-/bc-ur-0.1.6.tgz",
|
||||
"_spec": "0.1.6",
|
||||
"_where": "/home/user/BlueWallet",
|
||||
"author": {
|
||||
|
|
|
@ -74,6 +74,12 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
|
||||
}
|
||||
|
||||
async generateFromEntropy(user) {
|
||||
const random = await randomBytes(user.length < 32 ? 32 - user.length : 0);
|
||||
const buf = Buffer.concat([user, random], 32);
|
||||
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
|
||||
}
|
||||
|
||||
_getExternalWIFByIndex(index) {
|
||||
return this._getWIFByIndex(false, index);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,21 @@ export class LegacyWallet extends AbstractWallet {
|
|||
this.secret = bitcoin.ECPair.makeRandom({ rng: () => buf }).toWIF();
|
||||
}
|
||||
|
||||
async generateFromEntropy(user) {
|
||||
let i = 0;
|
||||
do {
|
||||
i += 1;
|
||||
const random = await randomBytes(user.length < 32 ? 32 - user.length : 0);
|
||||
const buf = Buffer.concat([user, random], 32);
|
||||
try {
|
||||
this.secret = bitcoin.ECPair.fromPrivateKey(buf).toWIF();
|
||||
return;
|
||||
} catch (e) {
|
||||
if (i === 5) throw e;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
|
|
BIN
img/coin1.png
Normal file
BIN
img/coin1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
img/coin2.png
Normal file
BIN
img/coin2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
|
@ -50,6 +50,9 @@
|
|||
coming_soon: 'In die toekoms',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Beursiet',
|
||||
|
@ -247,4 +250,9 @@
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
coming_soon: 'Kuza ngokukhawuleza',
|
||||
lightning: 'Umbane',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Ingxowa',
|
||||
|
@ -250,4 +253,9 @@
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Pròximament',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Detalls del moneder',
|
||||
|
@ -247,4 +250,9 @@ module.exports = {
|
|||
additional_info: 'Informació addicional',
|
||||
open_direct_channel: 'Obrir un canal directe amb aquest node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -47,6 +47,9 @@ module.exports = {
|
|||
coming_soon: 'Již brzy',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Peněženka',
|
||||
|
@ -246,4 +249,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -47,6 +47,9 @@ module.exports = {
|
|||
coming_soon: 'Kommer snart',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Wallet',
|
||||
|
@ -247,4 +250,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -49,6 +49,9 @@ module.exports = {
|
|||
coming_soon: 'Demnächst verfügbar',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Wallet',
|
||||
|
@ -251,4 +254,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Direkten Kanal zu diesem Knoten eröffnen:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -50,6 +50,9 @@ module.exports = {
|
|||
coming_soon: 'Σύντομα',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Πορτοφόλι',
|
||||
|
@ -249,4 +252,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Coming soon',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Wallet',
|
||||
|
@ -247,4 +250,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Viene pronto',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Detalles de la billetera',
|
||||
|
@ -250,4 +253,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -50,6 +50,9 @@ module.exports = {
|
|||
coming_soon: 'Tulossa',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Lompakko',
|
||||
|
@ -249,4 +252,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -49,6 +49,9 @@ module.exports = {
|
|||
coming_soon: 'Bientôt',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Portefeuille',
|
||||
|
@ -249,4 +252,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -47,6 +47,9 @@ module.exports = {
|
|||
coming_soon: 'Dolazi uskoro',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Volet',
|
||||
|
@ -246,4 +249,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Hamarosan',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Tárca',
|
||||
|
@ -249,4 +252,9 @@ module.exports = {
|
|||
additional_info: 'További információk',
|
||||
open_direct_channel: 'Közvetlen csatorna nyitása erre a csomópontra:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Akan datang',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Dompet',
|
||||
|
@ -246,4 +249,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -50,6 +50,9 @@ module.exports = {
|
|||
coming_soon: 'In arrivo',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Portafoglio',
|
||||
|
@ -249,4 +252,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -47,6 +47,9 @@ module.exports = {
|
|||
coming_soon: '準備中',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'ウォレット',
|
||||
|
@ -248,4 +251,9 @@ module.exports = {
|
|||
additional_info: '追加情報',
|
||||
open_direct_channel: 'このノードの直接チャネルを作成:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Kommer snart',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Lommebok',
|
||||
|
@ -247,4 +250,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Komt binnenkort',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Portemonnee',
|
||||
|
@ -250,4 +253,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -50,6 +50,9 @@ module.exports = {
|
|||
coming_soon: 'Em breve',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Carteira',
|
||||
|
@ -253,4 +256,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Brevemente',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'wallet',
|
||||
|
@ -251,4 +254,9 @@ module.exports = {
|
|||
additional_info: 'Informação adicional',
|
||||
open_direct_channel: 'Abrir canal directo com este node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,10 @@ module.exports = {
|
|||
coming_soon: 'Скоро будет',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Сгенерировать энторию с помощью игральных костей',
|
||||
entropy_generated: '{gen} байтов сгенерированной энтории',
|
||||
entropy_remain:
|
||||
'{gen} байтов сгенерированной энтории. Оставшиеся {rem} байт будут получены из системного генератора случайных чисел.',
|
||||
},
|
||||
details: {
|
||||
title: 'Информация о кошельке',
|
||||
|
@ -251,4 +255,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Энтропия',
|
||||
save: 'Сохранить',
|
||||
undo: 'Отмена',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Už čoskoro',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Peňaženka',
|
||||
|
@ -246,4 +249,9 @@ module.exports = {
|
|||
additional_info: 'Doplňujúce informácie',
|
||||
open_direct_channel: 'Otvoriť priamy kanál s týmto uzlom:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Kommer snart',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Plånbok',
|
||||
|
@ -248,4 +251,9 @@ module.exports = {
|
|||
additional_info: 'Ytterligare information',
|
||||
open_direct_channel: 'Öppna en direkt kanal med denna nod:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -47,6 +47,9 @@ module.exports = {
|
|||
coming_soon: 'เร็วๆนี้',
|
||||
lightning: 'ไลท์นิง',
|
||||
bitcoin: 'บิตคอยน์',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'กระเป๋าสตางค์',
|
||||
|
@ -246,4 +249,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Yakında',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Cüzdan',
|
||||
|
@ -247,4 +250,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Буде скоро',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Інформація про Гаманець',
|
||||
|
@ -251,4 +254,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,9 @@ module.exports = {
|
|||
coming_soon: 'Coming soon',
|
||||
lightning: 'Lightning',
|
||||
bitcoin: 'Bitcoin',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: 'Wallet',
|
||||
|
@ -247,4 +250,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -46,6 +46,9 @@ module.exports = {
|
|||
coming_soon: '即将来临',
|
||||
lightning: '闪电',
|
||||
bitcoin: '比特币',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: '钱包',
|
||||
|
@ -242,4 +245,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -46,6 +46,9 @@ module.exports = {
|
|||
coming_soon: '即將來臨',
|
||||
lightning: '閃電',
|
||||
bitcoin: '比特幣',
|
||||
entropy_provide: 'Provide entropy via dice rolls',
|
||||
entropy_generated: '{gen} bytes of generated entropy',
|
||||
entropy_remain: '{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.',
|
||||
},
|
||||
details: {
|
||||
title: '錢包',
|
||||
|
@ -242,4 +245,9 @@ module.exports = {
|
|||
additional_info: 'Additional Information',
|
||||
open_direct_channel: 'Open direct channel with this node:',
|
||||
},
|
||||
entropy: {
|
||||
title: 'Entropy',
|
||||
save: 'Save',
|
||||
undo: 'Undo',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -70,8 +70,8 @@
|
|||
"@sentry/react-native": "1.4.5",
|
||||
"amplitude-js": "5.11.0",
|
||||
"assert": "1.5.0",
|
||||
"bc-ur": "file:blue_modules/bc-ur",
|
||||
"bc-bech32": "file:blue_modules/bc-bech32",
|
||||
"bc-ur": "file:blue_modules/bc-ur",
|
||||
"bech32": "1.1.3",
|
||||
"bignumber.js": "9.0.0",
|
||||
"bip21": "2.0.2",
|
||||
|
|
|
@ -70,8 +70,8 @@ const GeneralSettings = () => {
|
|||
/>
|
||||
<BlueCard>
|
||||
<BlueText>
|
||||
When enabled, you will see advanced options such as different wallet types and the ability to specify the LNDHub instance you
|
||||
wish to connect to.
|
||||
When enabled, you will see advanced options such as different wallet types, the ability to specify the LNDHub instance you wish
|
||||
to connect to and custom entropy during wallet creation.
|
||||
</BlueText>
|
||||
</BlueCard>
|
||||
<BlueSpacing20 />
|
||||
|
|
|
@ -166,6 +166,20 @@ export default class WalletsAdd extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
let entropyTitle;
|
||||
if (!this.state.entropy) {
|
||||
entropyTitle = loc.wallets.add.entropy_provide;
|
||||
} else if (this.state.entropy.length < 32) {
|
||||
entropyTitle = loc.formatString(loc.wallets.add.entropy_remain, {
|
||||
gen: this.state.entropy.length,
|
||||
rem: 32 - this.state.entropy.length,
|
||||
});
|
||||
} else {
|
||||
entropyTitle = loc.formatString(loc.wallets.add.entropy_generated, {
|
||||
gen: this.state.entropy.length,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<StatusBar barStyle="light-content" />
|
||||
|
@ -359,7 +373,18 @@ export default class WalletsAdd extends Component {
|
|||
w.setLabel(this.state.label || loc.wallets.details.title);
|
||||
}
|
||||
if (this.state.activeBitcoin) {
|
||||
await w.generate();
|
||||
if (this.state.entropy) {
|
||||
try {
|
||||
await w.generateFromEntropy(this.state.entropy);
|
||||
} catch (e) {
|
||||
console.log(e.toString());
|
||||
alert(e.toString());
|
||||
this.props.navigation.goBack();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await w.generate();
|
||||
}
|
||||
BlueApp.wallets.push(w);
|
||||
await BlueApp.saveToDisk();
|
||||
EV(EV.enum.WALLETS_COUNT_CHANGED);
|
||||
|
@ -388,6 +413,15 @@ export default class WalletsAdd extends Component {
|
|||
this.props.navigation.navigate('ImportWallet');
|
||||
}}
|
||||
/>
|
||||
{this.state.isAdvancedOptionsEnabled && (
|
||||
<BlueButtonLink
|
||||
style={styles.import}
|
||||
title={entropyTitle}
|
||||
onPress={() => {
|
||||
this.props.navigation.navigate('ProvideEntropy', { onGenerated: entropy => this.setState({ entropy }) });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</ScrollView>
|
||||
|
|
375
screen/wallets/provideEntropy.js
Normal file
375
screen/wallets/provideEntropy.js
Normal file
|
@ -0,0 +1,375 @@
|
|||
import React, { useReducer, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import BN from 'bignumber.js';
|
||||
import { Dimensions, View, ScrollView, Text, Image, TouchableOpacity, StyleSheet } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
|
||||
import { SafeBlueArea, BlueNavigationStyle, BlueTabs } from '../../BlueComponents';
|
||||
const loc = require('../../loc');
|
||||
const BlueApp = require('../../BlueApp');
|
||||
|
||||
const ENTROPY_LIMIT = 256;
|
||||
|
||||
const shiftLeft = (value, places) => value.multipliedBy(2 ** places);
|
||||
const shiftRight = (value, places) => value.div(2 ** places).dp(0, BN.ROUND_DOWN);
|
||||
|
||||
const initialState = { entropy: BN(0), bits: 0, items: [] };
|
||||
export const eReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case 'push': {
|
||||
let { value, bits } = action;
|
||||
if (value >= 2 ** bits) {
|
||||
throw new TypeError("Can't push value exceeding size in bits");
|
||||
}
|
||||
if (state.bits === ENTROPY_LIMIT) return state;
|
||||
if (state.bits + bits > ENTROPY_LIMIT) {
|
||||
value = shiftRight(BN(value), bits + state.bits - ENTROPY_LIMIT);
|
||||
bits = ENTROPY_LIMIT - state.bits;
|
||||
}
|
||||
const entropy = shiftLeft(state.entropy, bits).plus(value);
|
||||
const items = [...state.items, bits];
|
||||
return { entropy, bits: state.bits + bits, items };
|
||||
}
|
||||
case 'pop': {
|
||||
if (state.bits === 0) return state;
|
||||
const bits = state.items.pop();
|
||||
const entropy = shiftRight(state.entropy, bits);
|
||||
return { entropy, bits: state.bits - bits, items: [...state.items] };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const entropyToHex = ({ entropy, bits }) => {
|
||||
if (bits === 0) return '0x';
|
||||
const hex = entropy.toString(16);
|
||||
const hexSize = Math.floor((bits - 1) / 4) + 1;
|
||||
return '0x' + '0'.repeat(hexSize - hex.length) + hex;
|
||||
};
|
||||
|
||||
export const getEntropy = (number, base) => {
|
||||
if (base === 1) return null;
|
||||
let maxPow = 1;
|
||||
while (2 ** (maxPow + 1) <= base) {
|
||||
maxPow += 1;
|
||||
}
|
||||
|
||||
let bits = maxPow;
|
||||
let summ = 0;
|
||||
while (bits >= 1) {
|
||||
const block = 2 ** bits;
|
||||
if (number < summ + block) {
|
||||
return { value: number - summ, bits };
|
||||
}
|
||||
summ += block;
|
||||
bits -= 1;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// cut entropy to bytes, convert to Buffer
|
||||
export const convertToBuffer = ({ entropy, bits }) => {
|
||||
if (bits < 8) return Buffer.from([]);
|
||||
const bytes = Math.floor(bits / 8);
|
||||
|
||||
// convert to byte array
|
||||
let arr = [];
|
||||
const ent = entropy.toString(16).split('').reverse();
|
||||
ent.forEach((v, index) => {
|
||||
if (index % 2 === 1) {
|
||||
arr[0] = v + arr[0];
|
||||
} else {
|
||||
arr.unshift(v);
|
||||
}
|
||||
});
|
||||
arr = arr.map(i => parseInt(i, 16));
|
||||
|
||||
if (arr.length > bytes) {
|
||||
arr.shift();
|
||||
} else if (arr.length < bytes) {
|
||||
const zeros = [...Array(bytes - arr.length)].map(() => 0);
|
||||
arr = [...zeros, ...arr];
|
||||
}
|
||||
return Buffer.from(arr);
|
||||
};
|
||||
|
||||
const Coin = ({ push }) => (
|
||||
<View style={styles.coinRoot}>
|
||||
<TouchableOpacity onPress={() => push(getEntropy(0, 2))}>
|
||||
<View style={styles.coinBody}>
|
||||
<Image style={styles.coinImage} source={require('../../img/coin1.png')} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => push(getEntropy(1, 2))}>
|
||||
<View style={styles.coinBody}>
|
||||
<Image style={styles.coinImage} source={require('../../img/coin2.png')} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
||||
Coin.propTypes = {
|
||||
push: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const Dice = ({ push, sides }) => {
|
||||
const diceIcon = i => {
|
||||
switch (i) {
|
||||
case 1:
|
||||
return 'dice-one';
|
||||
case 2:
|
||||
return 'dice-two';
|
||||
case 3:
|
||||
return 'dice-three';
|
||||
case 4:
|
||||
return 'dice-four';
|
||||
case 5:
|
||||
return 'dice-five';
|
||||
default:
|
||||
return 'dice-six';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView style={styles.diceScroll} contentContainerStyle={styles.diceContainer}>
|
||||
{[...Array(sides)].map((_, i) => (
|
||||
<TouchableOpacity key={i} onPress={() => push(getEntropy(i, sides))}>
|
||||
<View style={styles.diceRoot}>
|
||||
{sides === 6 ? (
|
||||
<Icon style={styles.diceIcon} name={diceIcon(i + 1)} size={70} color="grey" type="font-awesome-5" />
|
||||
) : (
|
||||
<View style={styles.dice}>
|
||||
<Text>{i + 1}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
Dice.propTypes = {
|
||||
sides: PropTypes.number.isRequired,
|
||||
push: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const Buttons = ({ pop, save }) => (
|
||||
<View style={styles.buttonsRoot}>
|
||||
<TouchableOpacity onPress={pop}>
|
||||
<View style={styles.buttonsBody}>
|
||||
<View style={styles.buttonsRow}>
|
||||
<View style={styles.buttonsIcon}>
|
||||
<Icon name="undo" size={16} type="font-awesome" color={BlueApp.settings.buttonAlternativeTextColor} />
|
||||
</View>
|
||||
<Text style={styles.buttonsLabel}>{loc.entropy.undo}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={save}>
|
||||
<View style={styles.buttonsBody}>
|
||||
<View style={styles.buttonsRow}>
|
||||
<View style={styles.buttonsIcon}>
|
||||
<Icon name="arrow-down" size={16} type="font-awesome" color={BlueApp.settings.buttonAlternativeTextColor} />
|
||||
</View>
|
||||
<Text style={[styles.buttonsLabel, styles.buttonsLabelRight]}>{loc.entropy.save}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
||||
Buttons.propTypes = {
|
||||
pop: PropTypes.func.isRequired,
|
||||
save: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const Entropy = () => {
|
||||
const [entropy, dispatch] = useReducer(eReducer, initialState);
|
||||
const { onGenerated } = useRoute().params;
|
||||
const navigation = useNavigation();
|
||||
const [tab, setTab] = useState(1);
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
const push = v => v && dispatch({ type: 'push', value: v.value, bits: v.bits });
|
||||
const pop = () => dispatch({ type: 'pop' });
|
||||
const save = () => {
|
||||
navigation.pop();
|
||||
const buf = convertToBuffer(entropy);
|
||||
onGenerated(buf);
|
||||
};
|
||||
|
||||
const hex = entropyToHex(entropy);
|
||||
let bits = entropy.bits.toString();
|
||||
bits = ' '.repeat(bits.length < 3 ? 3 - bits.length : 0) + bits;
|
||||
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<TouchableOpacity onPress={() => setShow(!show)}>
|
||||
<View style={styles.entropy}>
|
||||
<Text style={styles.entropyText}>{show ? hex : `${bits} of 256 bits`}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
<BlueTabs
|
||||
active={tab}
|
||||
onSwitch={setTab}
|
||||
tabs={[
|
||||
({ active }) => (
|
||||
<Icon
|
||||
name="toll"
|
||||
type="material"
|
||||
color={active ? BlueApp.settings.buttonAlternativeTextColor : BlueApp.settings.buttonBackgroundColor}
|
||||
/>
|
||||
),
|
||||
({ active }) => (
|
||||
<Icon
|
||||
name="dice"
|
||||
type="font-awesome-5"
|
||||
color={active ? BlueApp.settings.buttonAlternativeTextColor : BlueApp.settings.buttonBackgroundColor}
|
||||
/>
|
||||
),
|
||||
({ active }) => (
|
||||
<Icon
|
||||
name="dice-d20"
|
||||
type="font-awesome-5"
|
||||
color={active ? BlueApp.settings.buttonAlternativeTextColor : BlueApp.settings.buttonBackgroundColor}
|
||||
/>
|
||||
),
|
||||
]}
|
||||
/>
|
||||
|
||||
{tab === 0 && <Coin push={push} />}
|
||||
{tab === 1 && <Dice sides={6} push={push} />}
|
||||
{tab === 2 && <Dice sides={20} push={push} />}
|
||||
|
||||
<Buttons pop={pop} save={save} />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
||||
Entropy.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
navigate: PropTypes.func,
|
||||
goBack: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
Entropy.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
title: loc.entropy.title,
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
entropy: {
|
||||
padding: 5,
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
backgroundColor: '#f2f2f2',
|
||||
borderRadius: 9,
|
||||
minHeight: 49,
|
||||
paddingHorizontal: 8,
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
entropyText: {
|
||||
fontSize: 15,
|
||||
fontFamily: 'Courier',
|
||||
},
|
||||
coinRoot: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
coinBody: {
|
||||
flex: 0.33,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: 1,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderColor: 'grey',
|
||||
margin: 10,
|
||||
},
|
||||
coinImage: {
|
||||
flex: 0.9,
|
||||
aspectRatio: 1,
|
||||
},
|
||||
diceScroll: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
diceContainer: {
|
||||
alignItems: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
paddingBottom: 100,
|
||||
},
|
||||
diceRoot: {
|
||||
width: Dimensions.get('window').width / 4,
|
||||
aspectRatio: 1,
|
||||
},
|
||||
dice: {
|
||||
margin: 3,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderColor: 'grey',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: 1,
|
||||
},
|
||||
diceIcon: {
|
||||
margin: 3,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: 1,
|
||||
color: 'grey',
|
||||
},
|
||||
buttonsRoot: {
|
||||
flexDirection: 'row',
|
||||
alignSelf: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
position: 'absolute',
|
||||
bottom: 30,
|
||||
borderRadius: 30,
|
||||
minHeight: 48,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
buttonsBody: {
|
||||
flex: 1,
|
||||
minWidth: 130,
|
||||
backgroundColor: BlueApp.settings.buttonBackgroundColor,
|
||||
},
|
||||
buttonsRow: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
buttonsIcon: {
|
||||
minWidth: 30,
|
||||
minHeight: 30,
|
||||
left: 5,
|
||||
backgroundColor: 'transparent',
|
||||
transform: [{ rotate: '-45deg' }],
|
||||
alignItems: 'center',
|
||||
marginBottom: -11,
|
||||
},
|
||||
buttonsLabel: {
|
||||
color: BlueApp.settings.buttonAlternativeTextColor,
|
||||
fontWeight: '500',
|
||||
left: 5,
|
||||
backgroundColor: 'transparent',
|
||||
paddingRight: 20,
|
||||
},
|
||||
buttonsLabelRight: {
|
||||
paddingRight: 20,
|
||||
},
|
||||
});
|
||||
|
||||
export default Entropy;
|
|
@ -1,31 +1,33 @@
|
|||
/* global jest */
|
||||
|
||||
jest.mock('react-native-watch-connectivity', () => {
|
||||
return {
|
||||
getIsWatchAppInstalled: jest.fn(),
|
||||
subscribeToMessages: jest.fn(),
|
||||
updateApplicationContext: jest.fn(),
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('react-native-secure-key-store', () => {
|
||||
return {
|
||||
setResetOnAppUninstallTo: jest.fn(),
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('react-native-quick-actions', () => {
|
||||
return {
|
||||
clearShortcutItems: jest.fn(),
|
||||
setQuickActions: jest.fn(),
|
||||
isSupported: jest.fn(),
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('react-native-default-preference', () => {
|
||||
return {
|
||||
setName: jest.fn(),
|
||||
set: jest.fn(),
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('react-native-fs', () => {
|
||||
return {
|
||||
|
@ -70,5 +72,7 @@ jest.mock('react-native-fs', () => {
|
|||
TemporaryDirectoryPath: jest.fn(),
|
||||
LibraryDirectoryPath: jest.fn(),
|
||||
PicturesDirectoryPath: jest.fn(),
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('react-native-gesture-handler', () => jest.requireActual('react-native-gesture-handler/__mocks__/RNGestureHandlerModule.js'));
|
||||
|
|
|
@ -108,3 +108,31 @@ it('Legacy HD (BIP44) can generate addressess based on xpub', async function ()
|
|||
assert.strictEqual(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
|
||||
assert.strictEqual(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
|
||||
});
|
||||
|
||||
it('can consume user generated entropy', async () => {
|
||||
const hd = new HDSegwitP2SHWallet();
|
||||
const zeroes = [...Array(32)].map(() => 0);
|
||||
await hd.generateFromEntropy(Buffer.from(zeroes));
|
||||
assert.strictEqual(
|
||||
hd.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',
|
||||
);
|
||||
});
|
||||
|
||||
it('can fullfill user generated entropy if less than 32 bytes provided', async () => {
|
||||
const hd = new HDSegwitP2SHWallet();
|
||||
const zeroes = [...Array(16)].map(() => 0);
|
||||
await hd.generateFromEntropy(Buffer.from(zeroes));
|
||||
const secret = hd.getSecret();
|
||||
assert.strictEqual(secret.startsWith('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon'), true);
|
||||
|
||||
let secretWithoutChecksum = secret.split(' ');
|
||||
secretWithoutChecksum.pop();
|
||||
secretWithoutChecksum = secretWithoutChecksum.join(' ');
|
||||
assert.strictEqual(
|
||||
secretWithoutChecksum.endsWith('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon'),
|
||||
false,
|
||||
);
|
||||
|
||||
assert.ok(secret.split(' ').length === 12 || secret.split(' ').length === 24);
|
||||
});
|
||||
|
|
|
@ -39,4 +39,33 @@ describe('Legacy wallet', () => {
|
|||
assert.strictEqual(tx.outs.length, 1);
|
||||
assert.strictEqual('1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB', bitcoin.address.fromOutputScript(tx.outs[0].script)); // to address
|
||||
});
|
||||
|
||||
it("throws error if you can 't create wallet from this entropy", async () => {
|
||||
const l = new LegacyWallet();
|
||||
const zeroes = [...Array(32)].map(() => 0);
|
||||
await assert.rejects(async () => await l.generateFromEntropy(Buffer.from(zeroes)), {
|
||||
name: 'TypeError',
|
||||
message: 'Private key not in range [1, n)',
|
||||
});
|
||||
});
|
||||
|
||||
it('can consume user generated entropy', async () => {
|
||||
const l = new LegacyWallet();
|
||||
const values = [...Array(32)].map(() => 1);
|
||||
await l.generateFromEntropy(Buffer.from(values));
|
||||
assert.strictEqual(l.getSecret(), 'KwFfNUhSDaASSAwtG7ssQM1uVX8RgX5GHWnnLfhfiQDigjioWXHH');
|
||||
});
|
||||
|
||||
it('can fullfill user generated entropy if less than 32 bytes provided', async () => {
|
||||
const l = new LegacyWallet();
|
||||
const values = [...Array(16)].map(() => 1);
|
||||
await l.generateFromEntropy(Buffer.from(values));
|
||||
assert.strictEqual(l.getSecret().startsWith('KwFfNUhSDaASSAwtG7ssQM'), true);
|
||||
assert.strictEqual(l.getSecret().endsWith('GHWnnLfhfiQDigjioWXHH'), false);
|
||||
const keyPair = bitcoin.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);
|
||||
});
|
||||
});
|
||||
|
|
146
tests/unit/provideEntropy.test.js
Normal file
146
tests/unit/provideEntropy.test.js
Normal file
|
@ -0,0 +1,146 @@
|
|||
/* global it, describe */
|
||||
import assert from 'assert';
|
||||
|
||||
import { eReducer, entropyToHex, getEntropy, convertToBuffer } from '../../screen/wallets/provideEntropy';
|
||||
|
||||
describe('Entropy reducer and format', () => {
|
||||
it('handles push and pop correctly', () => {
|
||||
let state = eReducer(undefined, { type: null });
|
||||
assert.equal(entropyToHex(state), '0x');
|
||||
|
||||
state = eReducer(state, { type: 'push', value: 0, bits: 1 });
|
||||
assert.equal(entropyToHex(state), '0x0');
|
||||
|
||||
state = eReducer(state, { type: 'push', value: 0, bits: 1 });
|
||||
assert.equal(entropyToHex(state), '0x0');
|
||||
|
||||
state = eReducer(state, { type: 'push', value: 0, bits: 3 });
|
||||
assert.equal(entropyToHex(state), '0x00');
|
||||
|
||||
state = eReducer(state, { type: 'pop' });
|
||||
assert.equal(entropyToHex(state), '0x0');
|
||||
|
||||
state = eReducer(state, { type: 'pop' });
|
||||
state = eReducer(state, { type: 'pop' });
|
||||
assert.equal(entropyToHex(state), '0x');
|
||||
|
||||
state = eReducer(state, { type: 'push', value: 1, bits: 1 });
|
||||
assert.equal(entropyToHex(state), '0x1'); // 0b1
|
||||
|
||||
state = eReducer(state, { type: 'push', value: 0, bits: 1 });
|
||||
assert.equal(entropyToHex(state), '0x2'); // 0b10
|
||||
|
||||
state = eReducer(state, { type: 'push', value: 0b01, bits: 2 });
|
||||
assert.equal(entropyToHex(state), '0x9'); // 0b1001
|
||||
|
||||
state = eReducer(state, { type: 'push', value: 0b10, bits: 2 });
|
||||
assert.equal(entropyToHex(state), '0x26'); // 0b100110
|
||||
});
|
||||
|
||||
it('handles 128 bits correctly', () => {
|
||||
const state = eReducer(undefined, { type: 'push', value: 0, bits: 128 });
|
||||
assert.equal(entropyToHex(state), '0x00000000000000000000000000000000');
|
||||
});
|
||||
|
||||
it('handles 256 bits correctly', () => {
|
||||
let state = eReducer(undefined, { type: null }); // get init state
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (const i of [...Array(256)]) {
|
||||
state = eReducer(state, { type: 'push', value: 1, bits: 1 });
|
||||
}
|
||||
assert.equal(entropyToHex(state), '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
});
|
||||
|
||||
it('handles pop when empty without error', () => {
|
||||
const state = eReducer(undefined, { type: 'pop' });
|
||||
assert.equal(entropyToHex(state), '0x');
|
||||
});
|
||||
|
||||
it('handles 256 bits limit', () => {
|
||||
let state = eReducer(undefined, { type: 'push', value: 0, bits: 254 });
|
||||
state = eReducer(state, { type: 'push', value: 0b101, bits: 3 });
|
||||
assert.equal(entropyToHex(state), '0x0000000000000000000000000000000000000000000000000000000000000002');
|
||||
});
|
||||
|
||||
it('Throws error if you try to push value exceeding size in bits', () => {
|
||||
assert.throws(() => eReducer(undefined, { type: 'push', value: 8, bits: 3 }), {
|
||||
name: 'TypeError',
|
||||
message: "Can't push value exceeding size in bits",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEntropy function', () => {
|
||||
it('handles coin', () => {
|
||||
assert.deepEqual(getEntropy(0, 2), { value: 0, bits: 1 });
|
||||
assert.deepEqual(getEntropy(1, 2), { value: 1, bits: 1 });
|
||||
});
|
||||
|
||||
it('handles 6 sides dice', () => {
|
||||
assert.deepEqual(getEntropy(0, 6), { value: 0, bits: 2 });
|
||||
assert.deepEqual(getEntropy(1, 6), { value: 1, bits: 2 });
|
||||
assert.deepEqual(getEntropy(2, 6), { value: 2, bits: 2 });
|
||||
assert.deepEqual(getEntropy(3, 6), { value: 3, bits: 2 });
|
||||
assert.deepEqual(getEntropy(4, 6), { value: 0, bits: 1 });
|
||||
assert.deepEqual(getEntropy(5, 6), { value: 1, bits: 1 });
|
||||
});
|
||||
|
||||
it('handles odd numbers', () => {
|
||||
assert.deepEqual(getEntropy(0, 3), { value: 0, bits: 1 });
|
||||
assert.deepEqual(getEntropy(1, 3), { value: 1, bits: 1 });
|
||||
assert.deepEqual(getEntropy(2, 3), null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertToBuffer function', () => {
|
||||
it('zero bits', () => {
|
||||
const state = eReducer(undefined, { type: null });
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from([]));
|
||||
});
|
||||
|
||||
it('8 zero bits', () => {
|
||||
const state = eReducer(undefined, { type: 'push', value: 0, bits: 8 });
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0]));
|
||||
});
|
||||
|
||||
it('8 filled bits', () => {
|
||||
const state = eReducer(undefined, { type: 'push', value: 0b11111111, bits: 8 });
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11111111]));
|
||||
});
|
||||
|
||||
it('9 zero bits', () => {
|
||||
const state = eReducer(undefined, { type: 'push', value: 0, bits: 9 });
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0]));
|
||||
});
|
||||
|
||||
it('9 filled bits', () => {
|
||||
const state = eReducer(undefined, { type: 'push', value: 0b111111111, bits: 9 });
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11111111]));
|
||||
});
|
||||
|
||||
it('9 bits', () => {
|
||||
const state = eReducer(undefined, { type: 'push', value: 0b111100111, bits: 9 });
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from([0b11100111]));
|
||||
});
|
||||
|
||||
it('3 bytes', () => {
|
||||
let state = eReducer(undefined, { type: 'push', value: 1, bits: 8 });
|
||||
state = eReducer(state, { type: 'push', value: 2, bits: 8 });
|
||||
state = eReducer(state, { type: 'push', value: 3, bits: 8 });
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from([1, 2, 3]));
|
||||
});
|
||||
|
||||
it('256 bits or 32bytes', () => {
|
||||
let state = eReducer(undefined, { type: null }); // get init state
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (const i of [...Array(256)]) {
|
||||
state = eReducer(state, { type: 'push', value: 1, bits: 1 });
|
||||
}
|
||||
|
||||
const bytes = [...Array(32)].map(() => 255);
|
||||
|
||||
assert.deepEqual(convertToBuffer(state), Buffer.from(bytes));
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue