Skip to content

Commit

Permalink
fix plate generation
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastienGllmt committed Aug 3, 2020
1 parent 2197cbe commit 1da5860
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 58 deletions.
56 changes: 55 additions & 1 deletion app/api/ada/lib/cardanoCrypto/plate.js
Expand Up @@ -2,13 +2,15 @@

import { RustModule } from './rustLoader';
import { v2genAddressBatchFunc } from '../../restoration/byron/scan';
import type { GenerateAddressFunc } from '../../../common/lib/restoration/bip44AddressScan';
import {
HARD_DERIVATION_START,
CoinTypes,
WalletTypePurpose,
ChainDerivations,
STAKING_KEY_INDEX,
} from '../../../../config/numbersConfig';
import { legacyWalletChecksum } from '@emurgo/cip4-js';
import { walletChecksum, legacyWalletChecksum } from '@emurgo/cip4-js';
import type { PlateResponse } from '../../../common/lib/crypto/plate';

export const generateByronPlate = (
Expand Down Expand Up @@ -39,3 +41,55 @@ export const generateByronPlate = (
const addresses = generateAddressFunc([...Array(count).keys()]);
return { addresses, accountPlate };
};

export const generateShelleyPlate = (
rootPk: RustModule.WalletV4.Bip32PrivateKey,
accountIndex: number,
count: number,
chainNetworkId: number,
): PlateResponse => {
const accountKey = rootPk
.derive(WalletTypePurpose.CIP1852)
.derive(CoinTypes.CARDANO)
.derive(accountIndex + HARD_DERIVATION_START);
const accountPublic = accountKey.to_public();
const chainKey = accountPublic.derive(ChainDerivations.EXTERNAL);

const stakingKey = accountPublic
.derive(ChainDerivations.CHIMERIC_ACCOUNT)
.derive(STAKING_KEY_INDEX)
.to_raw_key();

const accountPlate = walletChecksum(
Buffer.from(accountPublic.as_bytes()).toString('hex')
);
const generateAddressFunc = genBaseAddressBatchFunc(
chainKey,
stakingKey,
chainNetworkId,
);
const addresses = generateAddressFunc([...Array(count).keys()]);
return { addresses, accountPlate };
};

export function genBaseAddressBatchFunc(
addressChain: RustModule.WalletV4.Bip32PublicKey,
stakingKey: RustModule.WalletV4.PublicKey,
chainNetworkId: number,
): GenerateAddressFunc {
return (
indices: Array<number>
) => {
const stakeKey = RustModule.WalletV4.StakeCredential.from_keyhash(stakingKey.hash());
return indices.map(i => {
const addressKey = addressChain.derive(i).to_raw_key();
const paymentKey = RustModule.WalletV4.StakeCredential.from_keyhash(addressKey.hash());
const address = RustModule.WalletV4.BaseAddress.new(
chainNetworkId,
paymentKey,
stakeKey
);
return address.to_address().to_bech32();
});
};
}
81 changes: 64 additions & 17 deletions app/api/ada/lib/storage/models/PublicDeriver/traits.js
Expand Up @@ -893,6 +893,53 @@ export const Bip44PickInternal: * = Mixin<
IPickInternal
>(Bip44PickInternalMixin);

// ==================================
// Cip1852JormungandrPickInternal
// ==================================

type Cip1852JormungandrPickInternalMixinDependencies = IPublicDeriver<> & IGetStakingKey;
const Cip1852JormungandrPickInternalMixin = (
superclass: Class<Cip1852JormungandrPickInternalMixinDependencies>,
) => (class Cip1852JormungandrPickInternal extends superclass implements IPickInternal {
rawPickInternal: (
lf$Transaction,
{|
GetPathWithSpecific: Class<GetPathWithSpecific>,
GetAddress: Class<GetAddress>,
GetDerivationSpecific: Class<GetDerivationSpecific>,
|},
IPickInternalRequest,
Map<number, string>,
) => Promise<IPickInternalResponse> = async (
tx,
deps,
body,
derivationTables,
) => {
const stakingAddressDbRow = await this.rawGetStakingKey(
tx,
deps,
undefined,
derivationTables
);

const stakingKey = Buffer.from(unwrapJormungandrStakingKey(stakingAddressDbRow.addr.Hash).as_bytes()).toString('hex');
const ourGroupAddress = body.addrs
.filter(addr => (addr.Type === CoreAddressTypes.JORMUNGANDR_GROUP))
.filter(addr => addr.Hash.includes(stakingKey));
if (ourGroupAddress.length !== 1) throw new Error(`${nameof(Cip1852JormungandrPickInternal)}::${nameof(this.rawPickInternal)} no group address found`);
return {
addr: ourGroupAddress[0],
row: body.row,
addressing: body.addressing,
};
}
});
export const Cip1852JormungandrPickInternal: * = Mixin<
Cip1852JormungandrPickInternalMixinDependencies,
IPickInternal
>(Cip1852JormungandrPickInternalMixin);

// =======================
// Cip1852PickInternal
// =======================
Expand Down Expand Up @@ -923,24 +970,17 @@ const Cip1852PickInternalMixin = (
derivationTables
);

const stakingKey = (() => {
if (isCardanoHaskell(this.getParent().getNetworkInfo())) {
return Buffer.from(unwrapCardanoStakingKey(stakingAddressDbRow.addr.Hash).to_bytes()).toString('hex');
}
if (isJormungandr(this.getParent().getNetworkInfo())) {
return Buffer.from(unwrapJormungandrStakingKey(stakingAddressDbRow.addr.Hash).as_bytes()).toString('hex');
}
throw new Error(`${nameof(Cip1852PickInternal)}::${nameof(this.rawPickInternal)} don't know how to pick for this network`);
})();
const ourGroupAddress = body.addrs
.filter(addr => (
addr.Type === CoreAddressTypes.JORMUNGANDR_GROUP ||
addr.Type === CoreAddressTypes.CARDANO_REWARD
))
const keyHash = unwrapCardanoStakingKey(stakingAddressDbRow.addr.Hash).to_keyhash();
if (keyHash == null) {
throw new Error(`${nameof(Cip1852PickInternal)}::${nameof(this.rawPickInternal)} internal address is a script`);
}
const stakingKey = Buffer.from(keyHash.to_bytes()).toString('hex');
const ourBaseAddress = body.addrs
.filter(addr => addr.Type === CoreAddressTypes.CARDANO_BASE)
.filter(addr => addr.Hash.includes(stakingKey));
if (ourGroupAddress.length !== 1) throw new Error(`${nameof(Cip1852PickInternal)}::${nameof(this.rawPickInternal)} no group address found`);
if (ourBaseAddress.length !== 1) throw new Error(`${nameof(Cip1852PickInternal)}::${nameof(this.rawPickInternal)} no base address found`);
return {
addr: ourGroupAddress[0],
addr: ourBaseAddress[0],
row: body.row,
addressing: body.addressing,
};
Expand Down Expand Up @@ -2217,7 +2257,14 @@ export async function addTraitsForCip1852Child(
if (conceptualWallet.getPublicDeriverLevel() === Bip44DerivationLevels.ACCOUNT.level) {
currClass = DisplayCutoff(currClass);

currClass = HasUtxoChains(Cip1852PickInternal(currClass));
if (isJormungandr(conceptualWallet.getNetworkInfo())) {
currClass = Cip1852JormungandrPickInternal(currClass);
} else if (isCardanoHaskell(conceptualWallet.getNetworkInfo())) {
currClass = Cip1852PickInternal(currClass);
} else {
throw new Error(`${nameof(addTraitsForCip1852Child)} don't know how to pick internal`);
}
currClass = HasUtxoChains(currClass);
if (publicKey !== null) {
currClass = GetPublicKey(currClass);
if (isJormungandr(conceptualWallet.getNetworkInfo())) {
Expand Down
44 changes: 40 additions & 4 deletions app/components/wallet/WalletRestoreVerifyDialog.js
Expand Up @@ -55,6 +55,10 @@ const messages = defineMessages({
id: 'wallet.restore.dialog.verify.accountId.shelley.label',
defaultMessage: '!!!Shelley account checksum:',
},
walletRestoreVerifyJormungandrAccountIdLabel: {
id: 'wallet.restore.dialog.verify.accountId.itn.label',
defaultMessage: '!!!ITN account checksum:',
},
walletRestoreVerifyAddressesLabel: {
id: 'wallet.restore.dialog.verify.addressesLabel',
defaultMessage: '!!!Your Wallet address[es]:',
Expand All @@ -67,9 +71,14 @@ const messages = defineMessages({
id: 'wallet.restore.dialog.verify.shelley.addressesLabel',
defaultMessage: '!!!Shelley Wallet address[es]:',
},
walletRestoreVerifyJormungandrAddressesLabel: {
id: 'wallet.restore.dialog.verify.itn.addressesLabel',
defaultMessage: '!!!ITN Wallet address[es]:',
},
});

type Props = {|
+shelleyPlate: void | PlateResponse,
+byronPlate: void | PlateResponse,
+jormungandrPlate: void | PlateResponse,
+selectedExplorer: SelectedExplorer,
Expand Down Expand Up @@ -153,6 +162,7 @@ export default class WalletRestoreVerifyDialog extends Component<Props> {
render(): Node {
const { intl } = this.context;
const {
shelleyPlate,
byronPlate,
jormungandrPlate,
error,
Expand Down Expand Up @@ -206,12 +216,19 @@ export default class WalletRestoreVerifyDialog extends Component<Props> {
byronPlate.accountPlate
);

const shelleyPlateElem = shelleyPlate == null
? undefined
: this.generatePlate(
intl.formatMessage(messages.walletRestoreVerifyShelleyAccountIdLabel),
shelleyPlate.accountPlate
);

const jormungandrPlateElem = jormungandrPlate == null
? undefined
: this.generatePlate(
byronPlate == null
? intl.formatMessage(messages.walletRestoreVerifyAccountIdLabel)
: intl.formatMessage(messages.walletRestoreVerifyShelleyAccountIdLabel),
: intl.formatMessage(messages.walletRestoreVerifyJormungandrAccountIdLabel),
jormungandrPlate.accountPlate
);

Expand All @@ -231,12 +248,26 @@ export default class WalletRestoreVerifyDialog extends Component<Props> {
: this.generateAddresses(
byronPlate == null
? intl.formatMessage(messages.walletRestoreVerifyAddressesLabel)
: intl.formatMessage(messages.walletRestoreVerifyShelleyAddressesLabel),
: intl.formatMessage(messages.walletRestoreVerifyJormungandrAddressesLabel),
jormungandrPlate.addresses,
onCopyAddressTooltip,
notification,
);

const shelleyAddressesElem = shelleyPlate == null
? undefined
: this.generateAddresses(
intl.formatMessage(messages.walletRestoreVerifyShelleyAddressesLabel),
shelleyPlate.addresses,
onCopyAddressTooltip,
notification,
);

const addressElems: Array<React$Node> = [
...(shelleyAddressesElem != null ? [shelleyAddressesElem] : []),
...(byronAddressesElem != null ? [byronAddressesElem] : []),
...(jormungandrAddressesElem != null ? [jormungandrAddressesElem] : []),
];
return (
<Dialog
title={intl.formatMessage(messages.dialogTitleVerifyWalletRestoration)}
Expand All @@ -252,14 +283,19 @@ export default class WalletRestoreVerifyDialog extends Component<Props> {

<DialogTextBlock>
<CenteredLayout>
{shelleyPlateElem}
{byronPlateElem}
{jormungandrPlateElem}
</CenteredLayout>
</DialogTextBlock>

<DialogTextBlock subclass="component-bottom">
{byronAddressesElem}<br />
{jormungandrAddressesElem}
{addressElems.map((elem, i) => {
if (i === 0) {
return <>{elem}</>;
}
return <><br />{elem}</>;
})}
</DialogTextBlock>

<div className={styles.postCopyMargin} />
Expand Down
49 changes: 29 additions & 20 deletions app/containers/transfer/YoroiPlatePage.js
Expand Up @@ -12,11 +12,11 @@ import {
generateWalletRootKey,
} from '../../api/ada/lib/cardanoCrypto/cryptoWallet';
import type { PlateResponse } from '../../api/common/lib/crypto/plate';
import { TransferKind } from '../../types/TransferTypes';
import { TransferKind, TransferSource } from '../../types/TransferTypes';
import { generatePlates } from '../../stores/toplevel/WalletRestoreStore';
import { RestoreMode } from '../../actions/common/wallet-restore-actions';
import { SelectedExplorer } from '../../domain/SelectedExplorer';
import type { TransferKindType, } from '../../types/TransferTypes';
import type { TransferKindType, TransferSourceType, } from '../../types/TransferTypes';
import type { Notification } from '../../types/notificationType';
import type { NetworkRow } from '../../api/ada/lib/storage/database/primitives/tables';

Expand All @@ -31,6 +31,7 @@ type Props = {|
type WalletRestoreDialogContainerState = {|
byronPlate: void | PlateResponse,
jormungandrPlate: void | PlateResponse,
shelleyPlate: void | PlateResponse,
|}

@observer
Expand All @@ -43,30 +44,35 @@ export default class YoroiPlatePage extends Component<Props> {
? generateLedgerWalletRootKey(yoroiTransfer.recoveryPhrase)
: generateWalletRootKey(yoroiTransfer.recoveryPhrase);

// const transferKind = () => {
// return switch(yoroiTransfer.transferKind) {
// case (TransferKind.PAPER): {

// }
// case (): {

// }

// }
// }

const { byronPlate, jormungandrPlate } = generatePlates(
const getRestoreMode = () => {
if (yoroiTransfer.transferKind === TransferKind.PAPER) {
return RestoreMode.PAPER;
}
if (yoroiTransfer.transferSource === TransferSource.BYRON) {
return RestoreMode.REGULAR_15;
}
if (yoroiTransfer.transferSource === TransferSource.JORMUNGANDR_UTXO) {
return RestoreMode.REGULAR_15;
}
if (yoroiTransfer.transferSource === TransferSource.JORMUNGANDR_CHIMERIC_ACCOUNT) {
return RestoreMode.REGULAR_15;
}
if (yoroiTransfer.transferSource === TransferSource.SHELLEY) {
return RestoreMode.REGULAR_24;
}
throw new Error(`${nameof(YoroiPlatePage)} unknown mode`);
};
const { byronPlate, shelleyPlate, jormungandrPlate } = generatePlates(
rootPk,
this.props.accountIndex,
yoroiTransfer.transferKind === TransferKind.PAPER
? RestoreMode.PAPER
: RestoreMode.REGULAR_15,
getRestoreMode(),
this.getSelectedNetwork(),
);
runInAction(() => {
this.plates = {
byronPlate,
jormungandrPlate,
shelleyPlate,
};
});
}
Expand All @@ -92,9 +98,10 @@ export default class YoroiPlatePage extends Component<Props> {
duration: config.wallets.ADDRESS_COPY_TOOLTIP_NOTIFICATION_DURATION,
message: globalMessages.copyTooltipMessage,
};
const { byronPlate, jormungandrPlate } = this.plates;
const { byronPlate, shelleyPlate, jormungandrPlate } = this.plates;
return (
<WalletRestoreVerifyDialog
shelleyPlate={shelleyPlate}
byronPlate={byronPlate}
jormungandrPlate={jormungandrPlate}
selectedExplorer={this.generated.stores.explorers.selectedExplorer
Expand Down Expand Up @@ -138,7 +145,8 @@ export default class YoroiPlatePage extends Component<Props> {
|},
yoroiTransfer: {|
recoveryPhrase: string,
transferKind: TransferKindType
transferKind: TransferKindType,
transferSource: TransferSourceType
|},
uiNotifications: {|
getTooltipActiveNotification: string => ?Notification,
Expand Down Expand Up @@ -167,6 +175,7 @@ export default class YoroiPlatePage extends Component<Props> {
},
yoroiTransfer: {
transferKind: stores.yoroiTransfer.transferKind,
transferSource: stores.yoroiTransfer.transferSource,
recoveryPhrase: stores.yoroiTransfer.recoveryPhrase,
},
},
Expand Down

0 comments on commit 1da5860

Please sign in to comment.