Skip to content

Commit

Permalink
Refactor and test getting account xpubs
Browse files Browse the repository at this point in the history
  • Loading branch information
PeterBenc committed Jan 28, 2021
1 parent 62ddd51 commit 639512a
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 97 deletions.
37 changes: 15 additions & 22 deletions app/frontend/components/pages/advanced/keys.tsx
Expand Up @@ -5,7 +5,8 @@ import {State, getActiveAccountInfo} from '../../../state'
import {parsePath} from '../../../helpers/pathParser'
import {LinkIconToKey} from '../delegations/common'
import tooltip from '../../common/tooltip'
import {HexString, _PubKeyCbor, _XPubKey} from '../../../types'
import {_Address, _XPubKey} from '../../../types'
import {bechAddressToHex, xpubHexToCborPubHex} from '../../../wallet/shelley/helpers/addresses'

const DownloadKey = ({cborHex}) => {
const fileContents = {
Expand All @@ -22,20 +23,13 @@ const DownloadKey = ({cborHex}) => {
}

type Props = {
byronAccountXpub: _XPubKey
shelleyAccountXpub: _XPubKey
stakingAddressHex: HexString
stakingKeyCborHex: _PubKeyCbor
stakingAddress: string
byronAccountXpub: _XPubKey
stakingAddress: _Address
stakingXpub: _XPubKey
}

const Keys = ({
byronAccountXpub,
shelleyAccountXpub,
stakingAddressHex,
stakingKeyCborHex,
stakingAddress,
}: Props) => {
const Keys = ({byronAccountXpub, shelleyAccountXpub, stakingAddress, stakingXpub}: Props) => {
return (
<div className="card">
<h2 className="card-title small-margin">Keys</h2>
Expand All @@ -52,8 +46,8 @@ const Keys = ({
</div>
<div className="advanced-value">{shelleyAccountXpub.xpubHex}</div>
<div className="advanced-label">
Staking key CBOR hex ({parsePath(stakingKeyCborHex.path)})
<DownloadKey cborHex={stakingKeyCborHex.cborHex} />
Staking key CBOR hex ({parsePath(stakingXpub.path)})
<DownloadKey cborHex={xpubHexToCborPubHex(stakingXpub.xpubHex)} />
<a
{...tooltip(
'Staking key is needed for creating the stake pool ownership certificate.',
Expand All @@ -63,27 +57,26 @@ const Keys = ({
<span className="show-info">{''}</span>
</a>
</div>
<div className="advanced-value">{stakingKeyCborHex.cborHex}</div>
<div className="advanced-value">{xpubHexToCborPubHex(stakingXpub.xpubHex)}</div>
{}
<div className="advanced-label">
Reward address <LinkIconToKey stakeKey={stakingAddress} />
</div>
<div className="advanced-value">{stakingAddress}</div>
<div className="advanced-label">
Staking key hex <LinkIconToKey stakeKey={stakingAddressHex.slice(2)} />
Staking key hex <LinkIconToKey stakeKey={bechAddressToHex(stakingAddress)} />
</div>
<div className="advanced-value">{stakingAddressHex.slice(2)}</div>
<div className="advanced-value">{bechAddressToHex(stakingAddress)}</div>
</div>
)
}

export default connect(
(state: State) => ({
shelleyAccountXpub: getActiveAccountInfo(state).keys.shelleyAccountXpub,
byronAccountXpub: getActiveAccountInfo(state).keys.byronAccountXpub,
stakingAddressHex: getActiveAccountInfo(state).keys.stakingAddressHex,
stakingKeyCborHex: getActiveAccountInfo(state).keys.stakingKeyCborHex,
stakingAddress: getActiveAccountInfo(state).keys.stakingAddress,
shelleyAccountXpub: getActiveAccountInfo(state).accountXpubs.shelleyAccountXpub,
byronAccountXpub: getActiveAccountInfo(state).accountXpubs.byronAccountXpub,
stakingAddress: getActiveAccountInfo(state).stakingAddress,
stakingXpub: getActiveAccountInfo(state).stakingXpub,
}),
actions
)(Keys)
7 changes: 3 additions & 4 deletions app/frontend/state.ts
Expand Up @@ -212,13 +212,12 @@ const initialState: State = {
isBigDelegator: false,
accountsInfo: [
{
keys: {
accountXpubs: {
shelleyAccountXpub: null,
byronAccountXpub: null,
stakingKeyCborHex: null,
stakingAddress: '',
stakingAddressHex: '',
},
stakingXpub: null,
stakingAddress: null,
balance: 0,
shelleyBalances: {
stakingBalance: 0,
Expand Down
22 changes: 3 additions & 19 deletions app/frontend/types.ts
Expand Up @@ -46,33 +46,17 @@ export type _XPubKey = {
xpubHex: HexString
}

export type _PubKey = {
path: number[]
pubHex: HexString
}

export type _PubKeyCbor = {
path: number[]
cborHex: HexString
}

export type _Address = {
path: number[]
address: string
}

export type AuthMethod = '' | 'hw-wallet' | 'mnemonic' // TODO
export type Ada = number & {__typeAda: any}
export type Lovelace = number & {__typeLovelace: any}

export type AccountInfo = {
keys: {
accountXpubs: {
shelleyAccountXpub: _XPubKey
byronAccountXpub: _XPubKey
stakingKeyCborHex: _PubKeyCbor
stakingAddress: string
stakingAddressHex: HexString
}
stakingXpub: _XPubKey
stakingAddress: _Address
balance: number
shelleyBalances: {
stakingBalance?: number
Expand Down
41 changes: 22 additions & 19 deletions app/frontend/wallet/account.ts
Expand Up @@ -4,11 +4,10 @@ import {MAX_INT32} from './constants'
import NamedError from '../helpers/NamedError'
import {CryptoProvider, Lovelace} from '../types'
import {
getStakingAddressHex,
getAccountXpub as getAccoutXpubShelley,
ShelleyStakingAccountProvider,
ShelleyBaseAddressProvider,
getStakingKeyCborHex,
getStakingXpub,
} from './shelley/shelley-address-provider'

import {
Expand Down Expand Up @@ -151,6 +150,10 @@ const MyAddresses = ({
)
}

async function getStakingAddress() {
return await accountAddrManager._deriveAddress(accountIndex)
}

return {
getAddressToAbsPathMapper,
fixedPathMapper,
Expand All @@ -160,6 +163,7 @@ const MyAddresses = ({
accountAddrManager,
legacyExtManager,
areAddressesUsed,
getStakingAddress,
}
}

Expand Down Expand Up @@ -273,7 +277,7 @@ const Account = ({
}

const getTxPlan = async (args: utxoArgs) => {
const accountAddress = await myAddresses.accountAddrManager._deriveAddress(accountIndex)
const stakingAddress = await myAddresses.getStakingAddress()
const {address, coins, donationAmount, poolHash, stakingKeyRegistered, txType, rewards} = args
const changeAddress = await getChangeAddress()
const availableUtxos = await getUtxos()
Expand All @@ -294,7 +298,7 @@ const Account = ({
coins,
donationAmount,
changeAddress,
accountAddress,
stakingAddress,
poolHash,
!stakingKeyRegistered,
rewards
Expand All @@ -312,7 +316,9 @@ const Account = ({
}

async function getAccountInfo(validStakepools) {
const keys = await getKeys()
const accountXpubs = await getAccountXpubs()
const stakingXpub = await getStakingXpub(cryptoProvider, accountIndex)
const stakingAddress = await myAddresses.getStakingAddress()
const {baseAddressBalance, nonStakingBalance, balance} = await getBalance()
const shelleyAccountInfo = await getStakingInfo(validStakepools)
const stakingHistory = await getStakingHistory(validStakepools)
Expand All @@ -325,7 +331,9 @@ const Account = ({
const isUsed = await isAccountUsed()

return {
keys,
accountXpubs,
stakingXpub,
stakingAddress,
balance,
shelleyBalances: {
nonStakingBalance,
Expand Down Expand Up @@ -360,27 +368,21 @@ const Account = ({
}

async function getStakingHistory(validStakepools): Promise<StakingHistoryObject[]> {
const stakingAddressHex = await getStakingAddressHex(cryptoProvider, accountIndex)
return blockchainExplorer.getStakingHistory(stakingAddressHex, validStakepools)
const stakingAddress = await myAddresses.getStakingAddress()
return blockchainExplorer.getStakingHistory(bechAddressToHex(stakingAddress), validStakepools)
}

async function getKeys() {
async function getAccountXpubs() {
const shelleyAccountXpub = await getAccoutXpubShelley(cryptoProvider, accountIndex)
const byronAccountXpub = await getAccoutXpubByron(cryptoProvider, accountIndex)
const stakingKeyCborHex = await getStakingKeyCborHex(cryptoProvider, accountIndex)
const stakingAddress = await myAddresses.accountAddrManager._deriveAddress(accountIndex)
const stakingAddressHex = await getStakingAddressHex(cryptoProvider, accountIndex)
return {
shelleyAccountXpub,
byronAccountXpub,
stakingKeyCborHex,
stakingAddress,
stakingAddressHex,
}
}

async function getStakingInfo(validStakepools) {
const stakingAddressHex = await getStakingAddressHex(cryptoProvider, accountIndex)
const stakingAddressHex = bechAddressToHex(await myAddresses.getStakingAddress())
const {nextRewardDetails, ...accountInfo} = await blockchainExplorer.getStakingInfo(
stakingAddressHex
)
Expand Down Expand Up @@ -467,10 +469,10 @@ const Account = ({
}

async function getPoolOwnerCredentials() {
const accountAddress = await myAddresses.accountAddrManager._deriveAddress(accountIndex)
const path = myAddresses.fixedPathMapper()(accountAddress)
const stakingAddress = await myAddresses.getStakingAddress()
const path = myAddresses.fixedPathMapper()(stakingAddress)
// TODO: this is not pubkeyHex, its staking address hex
const pubKeyHex = await getStakingAddressHex(cryptoProvider, accountIndex)
const pubKeyHex = bechAddressToHex(stakingAddress)
return {
pubKeyHex: Buffer.from(pubKeyHex, 'hex')
.slice(1)
Expand Down Expand Up @@ -500,6 +502,7 @@ const Account = ({
getPoolRecommendation,
isAccountUsed,
ensureXpubIsExported,
_getAccountXpubs: getAccountXpubs,
}
}

Expand Down
4 changes: 4 additions & 0 deletions app/frontend/wallet/shelley/helpers/addresses.ts
Expand Up @@ -9,6 +9,7 @@ import {
getPubKeyBlake2b224Hash,
} from 'cardano-crypto.js'
import {HARDENED_THRESHOLD} from '../../constants'
import {encode} from 'borc'

const xpub2pub = (xpub: Buffer) => xpub.slice(0, 32)

Expand All @@ -21,6 +22,9 @@ export const isShelleyPath = (path) => path[0] - HARDENED_THRESHOLD === 1852
// TODO: do this properly with cardano-crypto unpackAddress
export const isV1Address = (address: string) => address.startsWith('D')

export const xpubHexToCborPubHex = (xpubHex: HexString) =>
encode(Buffer.from(xpubHex, 'hex').slice(0, 32)).toString('hex')

export const bechAddressToHex = (address: string): HexString => {
const parsed = bech32.decode(address)
if (parsed.prefix !== 'addr' && parsed.prefix !== 'stake') throw Error('Invalid address')
Expand Down
33 changes: 9 additions & 24 deletions app/frontend/wallet/shelley/shelley-address-provider.ts
@@ -1,11 +1,6 @@
import {CryptoProvider, HexString, _PubKeyCbor, _XPubKey} from '../../types'
import {HARDENED_THRESHOLD} from '../constants'
import {
stakingAddressFromXpub,
baseAddressFromXpub,
stakingAddressHexFromXpub,
} from './helpers/addresses'
import {encode} from 'borc'
import {stakingAddressFromXpub, baseAddressFromXpub} from './helpers/addresses'

const shelleyPath = (account: number, isChange: boolean, addrIdx: number) => {
return [
Expand All @@ -27,13 +22,16 @@ const shelleyStakeAccountPath = (account: number) => {
]
}

export const getStakingAddressHex = async (
export const getStakingXpub = async (
cryptoProvider: CryptoProvider,
accountIndex: number
): Promise<HexString> => {
const pathStake = shelleyStakeAccountPath(accountIndex)
const stakeXpub = await cryptoProvider.deriveXpub(pathStake)
return stakingAddressHexFromXpub(stakeXpub, cryptoProvider.network.networkId)
): Promise<_XPubKey> => {
const path = shelleyStakeAccountPath(accountIndex)
const xpubHex = (await cryptoProvider.deriveXpub(path)).toString('hex')
return {
path,
xpubHex,
}
}

export const getAccountXpub = async (
Expand All @@ -49,19 +47,6 @@ export const getAccountXpub = async (
}
}

export const getStakingKeyCborHex = async (
cryptoProvider: CryptoProvider,
accountIndex: number
): Promise<_PubKeyCbor> => {
const path = shelleyStakeAccountPath(accountIndex)
const pubKey: Buffer = (await cryptoProvider.deriveXpub(path)).slice(0, 32)
const cborHex: HexString = encode(pubKey).toString('hex')
return {
path,
cborHex,
}
}

export const ShelleyStakingAccountProvider = (
cryptoProvider: CryptoProvider,
accountIndex: number
Expand Down
2 changes: 1 addition & 1 deletion app/tests/src/actions/transaction-actions.js
Expand Up @@ -30,7 +30,7 @@ it('Calculate fee - shelley', async () => {
mockNet.mockPoolRecommendation()

await action.loadWallet(state, {
cryptoProviderType: CRYPTO_PROVIDER_TYPES.WALLET_SECRET,
cryptoProviderType: CryptoProviderType.WALLET_SECRET,
walletSecretDef: await mnemonicToWalletSecretDef(walletSettings[0].secret),
})

Expand Down
2 changes: 1 addition & 1 deletion app/tests/src/actions/wallet-actions.js
Expand Up @@ -65,7 +65,7 @@ it('Should properly load shelley wallet', async () => {
mockNet.mockPoolRecommendation()

await action.loadWallet(state, {
cryptoProviderType: CRYPTO_PROVIDER_TYPES.WALLET_SECRET,
cryptoProviderType: CryptoProviderType.WALLET_SECRET,
walletSecretDef: await mnemonicToWalletSecretDef(walletSettings[0].secret),
shouldExportPubKeyBulk: true,
})
Expand Down
31 changes: 31 additions & 0 deletions app/tests/src/common/account-settings.js
Expand Up @@ -52,6 +52,23 @@ export const accountSettings = [
'addr1q98wuwmvcm2e608tvnjjawqrewhfh42cyt9hjpdw3d9elsc2s3ps2plp2rc2qcgfmsa8kx2kk7s9s6hfq799tmcwpvps5etmj7',
'addr1qx39zdl4lym6hjmz6yzq5y4jw02jfyvjkwpwve49may288c2s3ps2plp2rc2qcgfmsa8kx2kk7s9s6hfq799tmcwpvpsjrny7r',
],
accountXpubs: {
shelleyAccountXpub: {
path: [2147485500, 2147485463, 2147483648],
xpubHex:
'014db2d19cb1c54b57faedea5429107e59b0893a9940fd30a634e014d52bacf2d9dc81b2c64d78b7bf1d7c0c48ea5cc21f30172a578a3be9e17a6c1c5417e5f3',
},
byronAccountXpub: {
path: [2147483692, 2147485463, 2147483648],
xpubHex:
'767490f02a28e221377ef930b062b367a531c4d1b8fc3d7937e16f316eac07a7579eaf58fdc64a021494d3cad65a3676c73ab59eeedf6effa125d2c934f65a5a',
},
},
stakingXpub: {
path: [2147485500, 2147485463, 2147483648, 2, 0],
xpubHex: '58202ef8d7c9e19bb688860a900123e5bbe2eff7187336590b3928d43a830110cd62',
},
stakingAddress: 'stake1uy9ggsc9qls4pu9qvyyacwnmr9tt0gzcdt5s0zj4au8qkqc65geks',
...accountManagerSettings[0],
},
{
Expand Down Expand Up @@ -103,6 +120,20 @@ export const accountSettings = [
'addr1q9hsg6rjwpwj0d8hu9grcs2ta8u79qw6h0ezslzqtrv0s4h4nns4m7jjdmrc6qh6dae4yt4aqm8j9v29ccvz7ph5ve8qx094f2',
'addr1q9k6d04pff2mtccuyge6537042md4249u5gl9majlpk2de84nns4m7jjdmrc6qh6dae4yt4aqm8j9v29ccvz7ph5ve8qa6np3t',
],
accountXpubs: {
shelleyAccountXpub: {
path: [2147485500, 2147485463, 2147483649],
xpubHex:
'6d391177f3a5c7f1df994240058c8bc618b9015c1fec3b6d4c0d68d93f54bcd662563a8231105c71282afdd7f65fd49b338aa8aec484a402b2e017af741e417c',
},
byronAccountXpub: null,
},
stakingXpub: {
path: [2147485500, 2147485463, 2147483649, 2, 0],
xpubHex:
'767490f02a28e221377ef930b062b367a531c4d1b8fc3d7937e16f316eac07a7579eaf58fdc64a021494d3cad65a3676c73ab59eeedf6effa125d2c934f65a5a',
},
stakingAddress: 'stake1u86eec2alffxa3udqtax7u6j967sdnezk9zuvxp0qm6xvns252x9a',
...accountManagerSettings[0],
},
]

0 comments on commit 639512a

Please sign in to comment.