Skip to content

Commit

Permalink
CryptoProviderFeature: add BYRON so new wallets can disable support
Browse files Browse the repository at this point in the history
BYRON keypaths are automatically added with the first account to
discover BYRON addresses.

This works for all current CryptoProviders (Software, Ledger,
Trezor). The BitBox02 does not support receiving on legacy Byron
addresses and exporting m/44'/... xpubs, so we need a feature flag to
indicate that Byron should be turned off when a BitBox02 is used as
the CryptoProvider.

add BitBox02 hardware wallet support

Co-authored-by: thisconnect <[email protected]>

bitbox02: display pairing code

bitbox02: add types for the bitbox02-api dependency

They will eventually be upstreamed.

Excluded from eslint because eslint expects Javascript, not pure
Typescript (e.g. it can't parse the constructor type without a body).

bitbox02: close an existing connection before opening a new one

Same as with getLedgerTransport in the Ledger crypto provider. This
ensures logout followed by another login works.

upgrade from webpack 4 to webpack 5

We want to dynamically import the bitbox02-api package and split it
out of the main bundle. After many different attempts we only found
that it works when upgrading to webpack 5, hence this commit.

The upgrade was performed by following
https://webpack.js.org/migrate/5/.

The `require.resolve('...`) additions were upgrade fixes suggested by
webpack itself on the command line.

Other references used:

webpack/changelog-v5#10 (comment)
https://stackoverflow.com/questions/41359504/webpack-bundle-js-uncaught-referenceerror-process-is-not-defined

bitbox02: lazily load bitbox02-api and split code out of main bundle

Since the bitbox02-api package is quite heavy, we only want to load it
when actually unlocking a BitBox02.

We follow the third approach described in
https://webpack.js.org/guides/code-splitting: using `await
import(...)` to load the package on demand, which also automatically
splits the code into a new file. The `webpackChunkName: "bitbox02-api"` describes the
name of the new file in dist/js/.

The tsconfig change was needed for code splitting to kick in,
according to:
- webpack/webpack#5703 (comment)
- webpack/webpack#5703 (comment)

poolOwner: restrict to Trezor/Ledger
  • Loading branch information
benma authored and refi93 committed Nov 23, 2021
1 parent 307f636 commit 12c7a8c
Show file tree
Hide file tree
Showing 34 changed files with 1,189 additions and 1,366 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ ADALITE_NETWORK="MAINNET"
# ADALITE_NETWORK="TESTNET"

# load wallet options
ADALITE_ENABLE_BITBOX02="true"
ADALITE_ENABLE_TREZOR="true"
ADALITE_ENABLE_LEDGER="true"

# delegation by ticker
ADALITE_ENABLE_SEARCH_BY_TICKER=true
ADALITE_ENABLE_SEARCH_BY_TICKER=true

# catalyst next round start and end dates in ISO format (example: 2021-2030)
ADALITE_NEXT_VOTING_START=2021-01-01T00:00:00.000Z
Expand Down
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/
app/bitbox02-api.d.ts
app/dist/
app/tests/dist/
app/node_modules/
Expand Down
5 changes: 5 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
"react": {
"version": "16.8",
"pragma": "h"
},
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
},
"parser": "@typescript-eslint/parser",
Expand Down
105 changes: 105 additions & 0 deletions app/bitbox02-api.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
declare module 'bitbox02-api' {
export function getDevicePath(options?: {forceBridge: boolean}): string

type CardanoNetwork = 0 | 1

export const constants = {
Status: {
PairingFailed: string,
},
Product: {
BitBox02Multi: string,
BitBox02BTCOnly: string,
},
messages: {
CardanoNetwork: {
CardanoMainnet: CardanoNetwork,
CardanoTestnet: CardanoNetwork,
},
},
}

declare class Firmware {
Product(): string
}

type Keypath = number[]
type ScriptConfig = {
pkhSkh: {
keypathPayment: Keypath
keypathStake: Keypath
}
}

type CardanoInput = {
keypath: Keypath
prevOutHash: Uint8Array
prevOutIndex: number
}

type CardanoOutput = {
encodedAddress: string
value: string
scriptConfig?: ScriptConfig
}

type CardanoCertificate =
| {
stakeRegistration: {
keypath: Keypath
}
}
| {
stakeDeregistration: {
keypath: Keypath
}
}
| {
stakeDelegation: {
keypath: Keypath
poolKeyhash: Uint8Array
}
}

type CardanoWithdrawal = {
keypath: Keypath
value: string
}

type CardanoShelleyWitness = {
signature: Uint8Array
publicKey: Uint8Array
}

export declare class BitBox02API {
constructor(devicePath: string)
connect(
showPairingCb: (string) => void,
userVerify: () => Promise<void>,
handleAttestationCb: (bool) => void,
onCloseCb: () => void,
setStatusCb: (string) => void
)
close(): boolean
firmware(): Firmware
version(): string
cardanoXPubs(keypaths: Keypath[]): Promise<Uint8Array[]>
cardanoAddress(
network: CardanoNetwork,
scriptConfig: ScriptConfig,
display?: boolean
): Promise<string>
cardanoSignTransaction(params: {
network: CardanoNetwork
inputs: CardanoInput[]
outputs: CardanoOutput[]
fee: string
ttl: string
certificates: CardanoCertificate[]
withdrawals: CardanoWithdrawal[]
validityIntervalStart: string
}): Promise<{
shelleyWitnesses: CardanoShelleyWitness[]
}>
}
}
10 changes: 9 additions & 1 deletion app/frontend/actions/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const accountsIncludeStakingAddresses = (
}

export default (store: Store) => {
const {loadingAction} = loadingActions(store)
const {loadingAction, stopLoadingAction} = loadingActions(store)
const {setError} = errorActions(store)
const {getState, setState} = store

Expand Down Expand Up @@ -103,11 +103,19 @@ export default (store: Store) => {
ledgerTransportChoice === LedgerTransportChoice.DEFAULT
? await getDefaultLedgerTransportType()
: ledgerTransportChoice
const bitbox02OnPairingCode = (pairingCode: string) => {
if (pairingCode !== null) {
loadingAction(state, `BitBox02 pairing code:\n${pairingCode}`)
} else {
stopLoadingAction(state)
}
}
const config = {
...ADALITE_CONFIG,
isShelleyCompatible,
shouldExportPubKeyBulk,
ledgerTransportType,
bitbox02OnPairingCode,
}

try {
Expand Down
10 changes: 10 additions & 0 deletions app/frontend/components/common/svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
// eslint-disable-next-line no-unused-vars
import {h, ComponentFactory} from 'preact'

const BitBoxLogoWhite = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="86" height="21">
<path
d="M31.776 15.39c1.713 0 2.62-.767 2.62-2.077s-.928-2.095-2.621-2.095h-3.358v4.171zm-3.36-6.37h3.299c1.451 0 2.257-.671 2.257-1.853 0-1.183-.745-1.733-2.237-1.733H28.41zm8.801 4.515c0 2.499-1.713 4.031-4.434 4.031h-7.195V3.256h6.811c2.52 0 4.314 1.345 4.314 3.69a3.104 3.104 0 0 1-2.217 3.103c1.753.544 2.687 1.693 2.687 3.486m1.86-10.278h2.621v2.62h-2.62zm0 4.01h2.621v10.299h-2.62Zm10.891 10.158a7.073 7.073 0 0 1-2.317.383c-2.016 0-2.923-.948-2.923-2.903V9.263H42.86V7.288h1.834V4.083h2.62v3.205h2.459v2.015h-2.46v5.32c0 .706.303.948 1.029.948a5.619 5.619 0 0 0 1.29-.222zm7.807-2.037c1.712 0 2.62-.765 2.62-2.075s-.928-2.095-2.621-2.095h-3.36v4.171zm-3.36-6.367h3.305c1.452 0 2.258-.672 2.258-1.854 0-1.183-.767-1.733-2.244-1.733h-3.326zm8.827 4.514c0 2.499-1.713 4.031-4.434 4.031h-7.22V3.256h6.81c2.52 0 4.314 1.345 4.314 3.69a3.104 3.104 0 0 1-2.217 3.103c1.753.544 2.72 1.693 2.72 3.486m3.897-1.129c0 2.016 1.068 3.265 2.687 3.265 1.618 0 2.687-1.269 2.687-3.265 0-1.995-1.069-3.265-2.687-3.265-1.62 0-2.687 1.344-2.687 3.265m8.062 0c0 3.265-2.237 5.482-5.375 5.482s-5.375-2.217-5.375-5.482 2.237-5.481 5.375-5.481c3.137 0 5.374 2.216 5.374 5.481m7.477 5.16-2.318-3.527-2.337 3.527H75.14l3.707-5.402-3.366-4.896h2.843l2.015 3.022 1.975-3.022h2.814l-3.32 4.796 3.682 5.502ZM0 15.88v-4.882l3.943-1.696V1.704L7.853 0v12.463zm17.675.04v-4.872L13.73 9.343v-7.59L9.814.041v12.463Zm-8.826-1.744L1.013 17.6 8.84 21l7.861-3.417z"
style="fill:#fff;stroke-width:.0711515"
/>
</svg>
)

const LedgerLogoWhite = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="82" height="20" viewBox="0 0 82 20">
<g
Expand Down Expand Up @@ -367,6 +376,7 @@ const WarningIcon = () => (
)

export {
BitBoxLogoWhite,
LedgerLogoWhite,
TrezorLogoWhite,
AdaLiteLogo,
Expand Down
9 changes: 3 additions & 6 deletions app/frontend/components/common/welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ class Welcome extends Component<WelcomeProps, {dontShowAgainCheckbox: boolean}>
</Article>
<Article title="Consider using a hardware wallet" icon="wallet">
AdaLite allows you to access your funds using a hardware wallet. It currently
supports Trezor model T, Ledger Nano X and Ledger Nano S. This enables you to
interact with AdaLite in the safest manner possible without giving away your
supports Trezor model T, Ledger Nano X, Ledger Nano S and BitBox02. This enables you
to interact with AdaLite in the safest manner possible without giving away your
mnemonic. An attacker can’t steal your mnemonic or private key since they don’t
leave Ledger.
</Article>
Expand Down Expand Up @@ -115,7 +115,4 @@ class Welcome extends Component<WelcomeProps, {dontShowAgainCheckbox: boolean}>
}
}

export default connect(
null,
actions
)(Welcome)
export default connect(null, actions)(Welcome)
18 changes: 9 additions & 9 deletions app/frontend/components/pages/advanced/poolOwner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {State} from '../../../state'
import {PoolRegTransactionSummary} from '../../../../frontend/types'
import {saveAs} from '../../../libs/file-saver'
import {CborizedCliWitness} from '../../../wallet/shelley/types'
import {usingHwWalletSelector} from '../../../../frontend/selectors'
import {usingHwWalletWithPoolSupportSelector} from '../../../../frontend/selectors'
import {transformSignatureToCliFormat} from '../../../../frontend/wallet/shelley/helpers/stakepoolRegistrationUtils'
import * as assert from 'assert'

Expand All @@ -20,7 +20,7 @@ interface Props {
poolRegTxError: any
downloadPoolSignature: any
openPoolRegTransactionModal: any
usingHwWallet: boolean
usingHwWalletWithPoolSupport: boolean
resetPoolRegTransactionSummary: any
}

Expand All @@ -38,7 +38,7 @@ const PoolOwnerCard = ({
poolRegTransactionSummary,
poolRegTxError,
openPoolRegTransactionModal,
usingHwWallet,
usingHwWalletWithPoolSupport,
}: Props) => {
const {
plan: poolTxPlan,
Expand Down Expand Up @@ -76,7 +76,7 @@ const PoolOwnerCard = ({
}, [poolTxPlan])

const error = poolRegTxError
const hwWalletLimitation = 'Only hardware wallet users can use this feature.'
const hwWalletLimitation = 'Only Trezor and Ledger users can use this feature.'
return (
<div className="card" data-cy="PoolRegistrationCard">
<h2 className="card-title small-margin">
Expand All @@ -96,7 +96,7 @@ const PoolOwnerCard = ({
fileDescription="transaction"
acceptedFiles="*"
error
isDisabled={!usingHwWallet}
isDisabled={!usingHwWalletWithPoolSupport}
disabledReason={hwWalletLimitation}
/>

Expand All @@ -109,14 +109,14 @@ const PoolOwnerCard = ({
)}
<div className="pool-owner-content-bottom">
<button
disabled={!usingHwWallet || fileName === '' || !!error || !poolTxPlan}
disabled={!usingHwWalletWithPoolSupport || fileName === '' || !!error || !poolTxPlan}
onClick={handleTxSign}
className="button primary"
{...tooltip(
'Please insert a valid certificate\nJSON file before proceeding.',
usingHwWallet && fileName === ''
usingHwWalletWithPoolSupport && fileName === ''
)}
{...tooltip(hwWalletLimitation, !usingHwWallet)}
{...tooltip(hwWalletLimitation, !usingHwWalletWithPoolSupport)}
onKeyDown={(e) => {
e.key === 'Enter' && (e.target as HTMLButtonElement).click()
}}
Expand Down Expand Up @@ -162,7 +162,7 @@ export default connect(
(state: State) => ({
poolRegTxError: state.poolRegTxError,
poolRegTransactionSummary: state.poolRegTransactionSummary,
usingHwWallet: usingHwWalletSelector(state),
usingHwWalletWithPoolSupport: usingHwWalletWithPoolSupportSelector(state),
}),
actions
)(PoolOwnerCard)
49 changes: 44 additions & 5 deletions app/frontend/components/pages/login/hardwareAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Fragment, h} from 'preact'
import {CryptoProviderType} from '../../../wallet/types'
import {TrezorLogoWhite, LedgerLogoWhite} from '../../common/svg'
import {TrezorLogoWhite, LedgerLogoWhite, BitBoxLogoWhite} from '../../common/svg'
import {ADALITE_CONFIG} from '../../../config'
import tooltip from '../../common/tooltip'
import {useActions} from '../../../helpers/connect'
Expand All @@ -23,6 +23,12 @@ const LoadByHardwareWalletSection = () => {
setBulkExport(!enableBulkExport)
}, [enableBulkExport])

const BitBox02AffiliateLink = (title) => (
<a href="https://shiftcrypto.ch/bitbox02/" target="blank">
{title}
</a>
)

const TrezorAffiliateLink = (title) => (
<a href="https://shop.trezor.io/?offer_id=10&aff_id=1071" target="blank">
{title}
Expand Down Expand Up @@ -57,15 +63,15 @@ const LoadByHardwareWalletSection = () => {
!ADALITE_CONFIG.ADALITE_ENABLE_TREZOR
)}
{...tooltip('Not supported on mobile devices', isMobileOnly)}
className="button primary trezor thin-data-balloon"
className="button primary thin-data-balloon"
onClick={() =>
loadWallet({
cryptoProviderType: CryptoProviderType.TREZOR,
shouldExportPubKeyBulk: enableBulkExport,
})
}
>
Unlock with
<span className="authentication-button-label">Unlock with</span>
<div className="trezor-logo-container">
<TrezorLogoWhite />
</div>
Expand All @@ -91,7 +97,7 @@ const LoadByHardwareWalletSection = () => {
!ADALITE_CONFIG.ADALITE_ENABLE_LEDGER
)}
disabled={!ADALITE_CONFIG.ADALITE_ENABLE_LEDGER}
className={`button primary ledger thin-data-balloon ${styles.ledgerButton}`}
className={`button primary thin-data-balloon ${styles.ledgerButton}`}
onClick={() =>
loadWallet({
cryptoProviderType: CryptoProviderType.LEDGER,
Expand All @@ -100,7 +106,7 @@ const LoadByHardwareWalletSection = () => {
})
}
>
Unlock with
<span className="authentication-button-label">Unlock with</span>
<div className="ledger-logo-container">
<LedgerLogoWhite />
</div>
Expand All @@ -124,6 +130,39 @@ const LoadByHardwareWalletSection = () => {
</div>
</div>
</div>
<div className="authentication-wallet">
<div className="authentication-image-container">
<img className="authentication-image" src="assets/bitbox02.png" alt="BitBox02" />
</div>
<div className="authentication-paragraph">BitBox02</div>
<div className="authentication-paragraph small">
{BitBox02AffiliateLink('Support us by buying one')}
</div>
<div
className="authentication-paragraph small"
dangerouslySetInnerHTML={{__html: '&nbsp;'}}
/>
<button
disabled={!ADALITE_CONFIG.ADALITE_ENABLE_BITBOX02 || isMobileOnly}
{...tooltip(
'Support for BitBox02 is temporarily disabled',
!ADALITE_CONFIG.ADALITE_ENABLE_BITBOX02
)}
{...tooltip('Not supported on mobile devices', isMobileOnly)}
className="button primary thin-data-balloon"
onClick={() =>
loadWallet({
cryptoProviderType: CryptoProviderType.BITBOX02,
shouldExportPubKeyBulk: enableBulkExport,
})
}
>
<span className="authentication-button-label">Unlock with</span>
<div className="bitbox-logo-container">
<BitBoxLogoWhite />
</div>
</button>
</div>
</div>
<div className="authentication-hw-bulk-public-export">
<label className="checkbox">
Expand Down
2 changes: 1 addition & 1 deletion app/frontend/components/pages/login/loginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const AuthCardInitial = () => (
/>
<AuthOption
tabName={AuthMethodType.HW_WALLET}
texts={['Trezor T', 'Ledger Nano S/X', 'Android device & Ledger']}
texts={['Trezor T', 'Ledger Nano S/X', 'Android device & Ledger', 'BitBox02']}
tag={'recommended'}
/>
<AuthOption tabName={AuthMethodType.KEY_FILE} texts={['Encrypted .JSON file']} tag={''} />
Expand Down
4 changes: 2 additions & 2 deletions app/frontend/components/pages/login/loginPageSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ const WalletContent = () => (
<div className="sidebar-item">
<p className="sidebar-paragraph">
Computers might be vulnerable to attacks on program and system level. Typing your mnemonic
directly may put your wallet at risk. We currently support Trezor Model T, Ledger Nano S and
Nano X hardware wallets.
directly may put your wallet at risk. We currently support Trezor Model T, Ledger Nano S,
Ledger Nano X and BitBox02 hardware wallets.
</p>
</div>
<div className="sidebar-item">
Expand Down
Loading

0 comments on commit 12c7a8c

Please sign in to comment.