Skip to content

Commit

Permalink
Enable "Chain Specific App" setting (#1445)
Browse files Browse the repository at this point in the history
* Allow for use of chain specific app

* Cleanup ordering of metadata check

* Update ui to 3.9.1

* Add error for when chain is null

* Add setbusy
  • Loading branch information
TarikGul authored Aug 13, 2024
1 parent c5d0fc1 commit 61060c6
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 100 deletions.
4 changes: 2 additions & 2 deletions packages/extension-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"@polkadot/phishing": "^0.23.3",
"@polkadot/rpc-provider": "^12.3.1",
"@polkadot/types": "^12.3.1",
"@polkadot/ui-keyring": "^3.8.3",
"@polkadot/ui-settings": "^3.8.3",
"@polkadot/ui-keyring": "^3.9.1",
"@polkadot/ui-settings": "^3.9.1",
"@polkadot/util": "^13.0.2",
"@polkadot/util-crypto": "^13.0.2",
"eventemitter3": "^5.0.1",
Expand Down
8 changes: 4 additions & 4 deletions packages/extension-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
"@polkadot/hw-ledger": "^13.0.2",
"@polkadot/keyring": "^13.0.2",
"@polkadot/networks": "^13.0.2",
"@polkadot/react-identicon": "^3.8.3",
"@polkadot/react-qr": "^3.8.3",
"@polkadot/react-identicon": "^3.9.1",
"@polkadot/react-qr": "^3.9.1",
"@polkadot/types": "^12.3.1",
"@polkadot/types-augment": "^12.3.1",
"@polkadot/ui-keyring": "^3.8.3",
"@polkadot/ui-settings": "^3.8.3",
"@polkadot/ui-keyring": "^3.9.1",
"@polkadot/ui-settings": "^3.9.1",
"@polkadot/util": "^13.0.2",
"@polkadot/util-crypto": "^13.0.2",
"file-saver": "^2.0.5",
Expand Down
91 changes: 57 additions & 34 deletions packages/extension-ui/src/Popup/Signing/LedgerSign.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Copyright 2019-2024 @polkadot/extension-ui authors & contributors
// SPDX-License-Identifier: Apache-2.0

/* eslint-disable deprecation/deprecation */

import type { Chain } from '@polkadot/extension-chains/types';
import type { Ledger, LedgerGeneric } from '@polkadot/hw-ledger';
import type { ExtrinsicPayload } from '@polkadot/types/interfaces';
import type { SignerPayloadJSON } from '@polkadot/types/types';
import type { HexString } from '@polkadot/util/types';

Expand All @@ -23,8 +27,9 @@ interface Props {
className?: string;
error: string | null;
genesisHash?: string;
onSignature?: ({ signature }: { signature: HexString }, signedTransaction: HexString) => void;
payload?: SignerPayloadJSON;
onSignature?: ({ signature }: { signature: HexString }, signedTransaction?: HexString) => void;
payloadJson?: SignerPayloadJSON;
payloadExt?: ExtrinsicPayload
setError: (value: string | null) => void;
}

Expand All @@ -50,7 +55,7 @@ function getMetadataProof (chain: Chain, payload: SignerPayloadJSON) {
};
}

function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payload, setError }: Props): React.ReactElement<Props> {
function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payloadExt, payloadJson, setError }: Props): React.ReactElement<Props> {
const [isBusy, setIsBusy] = useState(false);
const { t } = useTranslation();
const chain = useMetadata(genesisHash);
Expand All @@ -69,43 +74,61 @@ function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHas

const _onSignLedger = useCallback(
(): void => {
if (!ledger || !payload || !onSignature || !chain) {
return;
}
if (!ledger || !payloadJson || !onSignature || !chain || !payloadExt) {
if (!chain) {
setError('No chain information found. You may need to update/upload the metadata.');
setIsBusy(false);
}

if (!chain?.definition.rawMetadata) {
setError('No metadata found for this chain. You must upload the metadata to the extension in order to use Ledger.');
return;
}

const { raw, txMetadata } = getMetadataProof(chain, payload);

const metaBuff = Buffer.from(txMetadata);

setError(null);
setIsBusy(true);
ledger.signWithMetadata(raw.toU8a(true), accountIndex, addressOffset, { metadata: metaBuff })
.then((signature) => {
const extrinsic = chain.registry.createType(
'Extrinsic',
{ method: raw.method },
{ version: 4 }
);

ledger.getAddress(chain.ss58Format, false, accountIndex, addressOffset)
.then(({ address }) => {
extrinsic.addSignature(address, signature.signature, raw.toHex());
onSignature(signature, extrinsic.toHex());
})
.catch((e: Error) => {
setError(e.message);
setIsBusy(false);
});
}).catch((e: Error) => {
setError(e.message);
setIsBusy(false);
});

const currApp = settings.get().ledgerApp;

if (currApp === 'generic' || currApp === 'migration') {
if (!chain?.definition.rawMetadata) {
setError('No metadata found for this chain. You must upload the metadata to the extension in order to use Ledger.');
}

const { raw, txMetadata } = getMetadataProof(chain, payloadJson);

const metaBuff = Buffer.from(txMetadata);

(ledger as LedgerGeneric).signWithMetadata(raw.toU8a(true), accountIndex, addressOffset, { metadata: metaBuff })
.then((signature) => {
const extrinsic = chain.registry.createType(
'Extrinsic',
{ method: raw.method },
{ version: 4 }
);

(ledger as LedgerGeneric).getAddress(chain.ss58Format, false, accountIndex, addressOffset)
.then(({ address }) => {
extrinsic.addSignature(address, signature.signature, raw.toHex());
onSignature(signature, extrinsic.toHex());
})
.catch((e: Error) => {
setError(e.message);
setIsBusy(false);
});
}).catch((e: Error) => {
setError(e.message);
setIsBusy(false);
});
} else if (currApp === 'chainSpecific') {
(ledger as Ledger).sign(payloadExt.toU8a(true), accountIndex, addressOffset)
.then((signature) => {
onSignature(signature);
}).catch((e: Error) => {
setError(e.message);
setIsBusy(false);
});
}
},
[accountIndex, addressOffset, chain, ledger, onSignature, payload, setError]
[accountIndex, addressOffset, chain, ledger, onSignature, payloadJson, payloadExt, setError]
);

return (
Expand Down
3 changes: 2 additions & 1 deletion packages/extension-ui/src/Popup/Signing/Request/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ export default function Request ({ account: { accountIndex, addressOffset, genes
error={error}
genesisHash={json.genesisHash}
onSignature={_onSignature}
payload={json}
payloadExt={payload}
payloadJson={json}
setError={setError}
/>
)}
Expand Down
76 changes: 47 additions & 29 deletions packages/extension-ui/src/hooks/useLedger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { HexString } from '@polkadot/util/types';

import { useCallback, useEffect, useMemo, useState } from 'react';

import { LedgerGeneric } from '@polkadot/hw-ledger';
import { Ledger, LedgerGeneric } from '@polkadot/hw-ledger';
import { knownLedger } from '@polkadot/networks/defaults';
import { settings } from '@polkadot/ui-settings';
import { assert } from '@polkadot/util';
Expand All @@ -29,7 +29,7 @@ interface State extends StateBase {
error: string | null;
isLoading: boolean;
isLocked: boolean;
ledger: LedgerGeneric | null;
ledger: LedgerGeneric | Ledger | null;
refresh: () => void;
warning: string | null;
}
Expand All @@ -47,8 +47,8 @@ function getState (): StateBase {
};
}

function retrieveLedger (genesis: string): LedgerGeneric {
let ledger: LedgerGeneric | null = null;
function retrieveLedger (genesis: string): LedgerGeneric | Ledger {
let ledger: LedgerGeneric | Ledger | null = null;

const currApp = settings.get().ledgerApp;

Expand All @@ -72,6 +72,8 @@ function retrieveLedger (genesis: string): LedgerGeneric {
ledger = new LedgerGeneric('webusb', def.network, knownLedger['polkadot']);
} else if (currApp === 'migration') {
ledger = new LedgerGeneric('webusb', def.network, knownLedger[def.network]);
} else if (currApp === 'chainSpecific') {
ledger = new Ledger('webusb', def.network);
} else {
// This will never get touched since it will always hit the above two. This satisfies the compiler.
ledger = new LedgerGeneric('webusb', def.network, knownLedger['polkadot']);
Expand All @@ -89,6 +91,28 @@ export default function useLedger (genesis?: string | null, accountIndex = 0, ad
const [address, setAddress] = useState<string | null>(null);
const { t } = useTranslation();

const handleGetAddressError = (e: Error, genesis: string) => {
setIsLoading(false);
const { network } = getNetwork(genesis) || { network: 'unknown network' };

const warningMessage = e.message.includes('Code: 26628')
? t('Is your ledger locked?')
: null;

const errorMessage = e.message.includes('App does not seem to be open')
? t('App "{{network}}" does not seem to be open', { replace: { network } })
: e.message;

setIsLocked(true);
setWarning(warningMessage);
setError(t(
'Ledger error: {{errorMessage}}',
{ replace: { errorMessage } }
));
console.error(e);
setAddress(null);
};

const ledger = useMemo(() => {
setError(null);
setIsLocked(false);
Expand Down Expand Up @@ -131,31 +155,25 @@ export default function useLedger (genesis?: string | null, accountIndex = 0, ad
// Just in case, but this shouldn't be triggered
assert(chosenNetwork, t('This network is not available, please report an issue to update the known chains'));

ledger.getAddress(chosenNetwork.ss58Format, false, accountIndex, addressOffset)
.then((res) => {
setIsLoading(false);
setAddress(res.address);
}).catch((e: Error) => {
setIsLoading(false);
const { network } = getNetwork(genesis) || { network: 'unknown network' };

const warningMessage = e.message.includes('Code: 26628')
? t('Is your ledger locked?')
: null;

const errorMessage = e.message.includes('App does not seem to be open')
? t('App "{{network}}" does not seem to be open', { replace: { network } })
: e.message;

setIsLocked(true);
setWarning(warningMessage);
setError(t(
'Ledger error: {{errorMessage}}',
{ replace: { errorMessage } }
));
console.error(e);
setAddress(null);
});
const currApp = settings.get().ledgerApp;

if (currApp === 'generic' || currApp === 'migration') {
(ledger as LedgerGeneric).getAddress(chosenNetwork.ss58Format, false, accountIndex, addressOffset)
.then((res) => {
setIsLoading(false);
setAddress(res.address);
}).catch((e: Error) => {
handleGetAddressError(e, genesis);
});
} else if (currApp === 'chainSpecific') {
(ledger as Ledger).getAddress(false, accountIndex, addressOffset)
.then((res) => {
setIsLoading(false);
setAddress(res.address);
}).catch((e: Error) => {
handleGetAddressError(e, genesis);
});
}
// If the dependency array is exhaustive, with t, the translation function, it
// triggers a useless re-render when ledger device is connected.
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
Loading

0 comments on commit 61060c6

Please sign in to comment.