Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions docs/key-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,60 @@ Not all wallets can operate with just public keys. Monero, in particular, has tr

Since Edge doesn't have this feature yet, the public key format is "work in progress". We *do* cache some element of these keys on disk for faster startup times, so the format needs to at least be semi-functional.

## Methods

```typescript
type KeysJson = object

interface EdgeCurrencyTools {
createPrivateKey(
opts?: EdgeCreatePrivateKeyOptions
): Promise<KeysJson>

readonly derivePublicKey?: (walletInfo: EdgeWalletInfo) => Promise<Object>

readonly importKey?: (
keyText: string,
opts?: EdgeCreatePrivateKeyOptions
) => Promise<KeysJson>

readonly keyCanSpend?: (walletInfo: EdgeWalletInfo) => Promise<boolean>

readonly listSplittableTypes?: (
walletInfo: EdgeWalletInfo
) => Promise<Array<string>>

readonly splitKey?: (
newWalletType: string,
walletInfo: EdgeWalletInfo
) => Promise<KeysJson>
}
```

### createPrivateKey

Create a new private key. The wallet will treat the returned object as the new wallet's `EdgeWalletInfo.keys` property. This method can use `EdgeIo.random` as a source of entropy (passed at plugin creation time).

### derivePublicKey

Creates an key that can see funds, but not spend. Depending on the input data, this may involve some mixture of deriving public fields (like addresses) and removing private fields (like spending keys). The wallet will treat the returned object as the new wallet's `EdgeWalletInfo.keys` property, and will potentially store it in clear-text on the device.

### importKey

Creates a new private or public key from some user-supplied text. The wallet will treat the returned object as the new wallet's `EdgeWalletInfo.keys` property.

### isPrivateKey

Returns true if an `EdgeWalletInfo` contains spending-capable keys.

### listSplittableTypes

Given an `EdgeWalletInfo`, return a list of wallet types that are possible to split into. This should examine all potential compatibility concerns, such as segwit wallets not being able to split to non-segwit chains (like BCH).

### splitKey

Creates a new private or public key from a different coin's `EdgeWalletInfo`. The wallet will treat the returned object as the new wallet's `EdgeWalletInfo.keys` property. The main requirement is that doing a round-trip between two different coin types should produce the *exact* same keys the wallet started with. Starting with a public (read-only) key should produce another public key, and starting with a private key should produce another private key.

# Detailed key formats

## Storage keys
Expand Down
9 changes: 4 additions & 5 deletions src/core/account/account-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ import { base58 } from '../../util/encoding.js'
import { makeExchangeCache } from '../exchange/exchange-api.js'
import {
createCurrencyWallet,
listSplittableWalletTypes,
makeKeysKit,
makeStorageKeyInfo,
splitWalletInfo
makeStorageKeyInfo
} from '../login/keys.js'
import { applyKit } from '../login/login.js'
import { cancelOtpReset, disableOtp, enableOtp } from '../login/otp.js'
Expand All @@ -41,6 +39,7 @@ import {
} from '../login/password.js'
import { changePin, checkPin2, deletePin } from '../login/pin2.js'
import { changeRecovery, deleteRecovery } from '../login/recovery2.js'
import { listSplittableWalletTypes, splitWallet } from '../login/split.js'
import { getCurrencyTools } from '../plugins/plugins-selectors.js'
import { type ApiInput } from '../root-pixie.js'
import { makeStorageWalletApi } from '../storage/storage-api.js'
Expand Down Expand Up @@ -267,7 +266,7 @@ export function makeAccountApi (ai: ApiInput, accountId: string): EdgeAccount {
if (keys == null) {
// Use the currency plugin to create the keys:
const tools = await getCurrencyTools(ai, walletType)
keys = await tools.createPrivateKey(walletType)
keys = await tools.createPrivateKey()
}

const walletInfo = makeStorageKeyInfo(ai, walletType, keys)
Expand All @@ -281,7 +280,7 @@ export function makeAccountApi (ai: ApiInput, accountId: string): EdgeAccount {
walletId: string,
newWalletType: string
): Promise<string> {
return splitWalletInfo(ai, accountId, walletId, newWalletType)
return splitWallet(ai, accountId, walletId, newWalletType)
},
async listSplittableWalletTypes (walletId: string): Promise<Array<string>> {
return listSplittableWalletTypes(ai, accountId, walletId)
Expand Down
1 change: 1 addition & 0 deletions src/core/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export type RootAction =
// Called when a currency engine returns the display private & public seeds.
type: 'CURRENCY_ENGINE_CHANGED_SEEDS',
payload: {
canSpend: boolean,
displayPublicSeed: string | null,
displayPrivateSeed: string | null,
walletId: string
Expand Down
5 changes: 4 additions & 1 deletion src/core/currency/wallet/currency-wallet-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ export function makeCurrencyWalletApi (
},

// Wallet keys:
get canSpend (): boolean {
return input.props.selfState.canSpend
},
get displayPrivateSeed (): string | null {
lockdown()
return input.props.selfState.displayPrivateSeed
Expand Down Expand Up @@ -377,7 +380,7 @@ export function makeCurrencyWalletApi (
},

async signTx (tx: EdgeTransaction): Promise<EdgeTransaction> {
return engine.signTx(tx)
return engine.signTx(tx, walletInfo)
},

async broadcastTx (tx: EdgeTransaction): Promise<EdgeTransaction> {
Expand Down
7 changes: 6 additions & 1 deletion src/core/currency/wallet/currency-wallet-pixie.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ export const walletPixie: TamePixie<CurrencyWalletProps> = combinePixies({
type: 'CURRENCY_WALLET_PUBLIC_INFO',
payload: { walletInfo: publicWalletInfo, walletId: input.props.id }
})
const canSpend: boolean =
tools.keyCanSpend != null ? await tools.keyCanSpend(walletInfo) : true

// Start the engine:
const engine = await plugin.makeCurrencyEngine(mergedWalletInfo, {
Expand All @@ -119,6 +121,7 @@ export const walletPixie: TamePixie<CurrencyWalletProps> = combinePixies({
type: 'CURRENCY_ENGINE_CHANGED_SEEDS',
payload: {
walletId: walletInfo.id,
canSpend,
displayPrivateSeed: engine.getDisplayPrivateSeed(),
displayPublicSeed: engine.getDisplayPublicSeed()
}
Expand Down Expand Up @@ -296,7 +299,9 @@ async function getPublicWalletInfo (
// Derive the public keys:
let publicKeys = {}
try {
publicKeys = await tools.derivePublicKey(walletInfo)
if (tools.derivePublicKey != null) {
publicKeys = await tools.derivePublicKey(walletInfo)
}
} catch (e) {}
const publicWalletInfo = {
id: walletInfo.id,
Expand Down
7 changes: 7 additions & 0 deletions src/core/currency/wallet/currency-wallet-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export type CurrencyWalletState = {
+pluginName: string,

+currencyInfo: EdgeCurrencyInfo,
+canSpend: boolean,
+displayPrivateSeed: string | null,
+displayPublicSeed: string | null,
+engineFailure: Error | null,
Expand Down Expand Up @@ -105,6 +106,12 @@ const currencyWallet = buildReducer({
return getCurrencyPlugin(next.root, next.self.walletInfo.type).currencyInfo
},

canSpend (state = false, action: RootAction): boolean {
return action.type === 'CURRENCY_ENGINE_CHANGED_SEEDS'
? action.payload.canSpend
: state
},

displayPrivateSeed (state = null, action: RootAction): string | null {
return action.type === 'CURRENCY_ENGINE_CHANGED_SEEDS'
? action.payload.displayPrivateSeed
Expand Down
Loading