feature(@desktop/keycard): generating addresses on a Keycard

Generating addresses was done on the status-go side, but now since `ExportPublic`
flow from the keycard library supports deriving addresses for list of derivation paths
we're using it in the desktop app for `SetupNewKeycardNewSeedPhrase` flow.
This commit is contained in:
Sale Djenic 2022-12-22 15:46:10 +01:00 committed by saledjenic
parent 0e65f2a7ff
commit 8600ef35b3
5 changed files with 79 additions and 44 deletions

View File

@ -420,12 +420,18 @@ proc runStoreMetadataFlow*(self: Controller, cardName: string, pin: string, wall
self.cancelCurrentFlow()
self.keycardService.startStoreMetadataFlow(cardName, pin, walletPaths)
proc runDeriveAccountFlow*(self: Controller, bip44Path = "", pin = "") =
proc runDeriveAccountFlow*(self: Controller, bip44Path: string, pin: string) =
if not serviceApplicable(self.keycardService):
return
self.cancelCurrentFlow()
self.keycardService.startExportPublicFlow(bip44Path, exportMasterAddr=true, exportPrivateAddr=false, pin)
proc runDeriveAccountFlow*(self: Controller, bip44Paths: seq[string], pin: string) =
if not serviceApplicable(self.keycardService):
return
self.cancelCurrentFlow()
self.keycardService.startExportPublicFlow(bip44Paths, exportMasterAddr=true, exportPrivateAddr=false, pin)
proc runAuthenticationFlow*(self: Controller, keyUid = "") =
## For signing a transaction we need to provide a key uid of a keypair that an account we want to sign a transaction
## for belongs to. If we're just doing an authentication for a logged in user, then default key uid is always the key

View File

@ -2,18 +2,10 @@ type
CreatingAccountNewSeedPhraseState* = ref object of State
paths: seq[string]
addresses: seq[string]
addingAccountsToWalletDone: bool
addingAccountsToWalletOk: bool
addingMigratedKeypairDone: bool
addingMigratedKeypairOk: bool
proc newCreatingAccountNewSeedPhraseState*(flowType: FlowType, backState: State): CreatingAccountNewSeedPhraseState =
result = CreatingAccountNewSeedPhraseState()
result.setup(flowType, StateType.CreatingAccountNewSeedPhrase, backState)
result.addingAccountsToWalletDone = false
result.addingAccountsToWalletOk = false
result.addingMigratedKeypairDone = false
result.addingMigratedKeypairOk = false
proc delete*(self: CreatingAccountNewSeedPhraseState) =
self.State.delete
@ -26,17 +18,28 @@ proc resolvePaths(self: CreatingAccountNewSeedPhraseState, controller: Controlle
self.paths.add(account.getPath())
i.inc
proc resolveAddressesForPaths(self: CreatingAccountNewSeedPhraseState, controller: Controller) =
proc findIndexForPath(self: CreatingAccountNewSeedPhraseState, path: string): int =
var ind = -1
for p in self.paths:
ind.inc
if p == path:
return ind
return ind
proc resolveAddresses(self: CreatingAccountNewSeedPhraseState, controller: Controller, keycardEvent: KeycardEvent): bool =
if keycardEvent.generatedWalletAccounts.len != self.paths.len:
return false
let kpForPRocessing = controller.getKeyPairForProcessing()
let generatedAccount = controller.generateAccountsFromSeedPhrase(controller.getSeedPhrase(), self.paths)
for account in kpForPRocessing.getAccountsModel().getItems():
if not generatedAccount.derivedAccounts.derivations.hasKey(account.getPath()):
return
kpForPRocessing.setDerivedFrom(generatedAccount.address)
let accDetails = generatedAccount.derivedAccounts.derivations[account.getPath()]
account.setAddress(accDetails.address)
account.setPubKey(accDetails.publicKey)
self.addresses.add(accDetails.address)
let index = self.findIndexForPath(account.getPath())
if index == -1:
## should never be here
return false
kpForPRocessing.setDerivedFrom(keycardEvent.masterKeyAddress)
account.setAddress(keycardEvent.generatedWalletAccounts[index].address)
account.setPubKey(keycardEvent.generatedWalletAccounts[index].publicKey)
self.addresses.add(keycardEvent.generatedWalletAccounts[index].address)
return true
proc addAccountsToWallet(self: CreatingAccountNewSeedPhraseState, controller: Controller): bool =
let kpForPRocessing = controller.getKeyPairForProcessing()
@ -68,32 +71,18 @@ proc runStoreMetadataFlow(self: CreatingAccountNewSeedPhraseState, controller: C
method executePrePrimaryStateCommand*(self: CreatingAccountNewSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycardNewSeedPhrase:
if not self.addingAccountsToWalletDone:
self.addingAccountsToWalletDone = true
self.resolvePaths(controller)
self.resolveAddressesForPaths(controller)
if self.paths.len != self.addresses.len:
return
self.addingAccountsToWalletOk = self.addAccountsToWallet(controller)
if self.addingAccountsToWalletOk:
self.doMigration(controller)
self.resolvePaths(controller)
controller.runDeriveAccountFlow(bip44Paths = self.paths, controller.getPin())
method executePreSecondaryStateCommand*(self: CreatingAccountNewSeedPhraseState, controller: Controller) =
## Secondary action is called after each async action during migration process.
if not self.addingMigratedKeypairDone:
self.addingMigratedKeypairDone = true
self.addingMigratedKeypairOk = controller.getAddingMigratedKeypairSuccess()
if self.addingMigratedKeypairOk:
self.runStoreMetadataFlow(controller)
method getNextPrimaryState*(self: CreatingAccountNewSeedPhraseState, controller: Controller): State =
## Secondary action is called after each async action during migration process, in this case after `addMigratedKeyPair`.
if self.flowType == FlowType.SetupNewKeycardNewSeedPhrase:
if self.addingAccountsToWalletDone and not self.addingAccountsToWalletOk:
return createState(StateType.CreatingAccountNewSeedPhraseFailure, self.flowType, nil)
if controller.getAddingMigratedKeypairSuccess():
self.runStoreMetadataFlow(controller)
method getNextSecondaryState*(self: CreatingAccountNewSeedPhraseState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycardNewSeedPhrase:
if self.addingMigratedKeypairDone and not self.addingMigratedKeypairOk:
if not controller.getAddingMigratedKeypairSuccess():
return createState(StateType.CreatingAccountNewSeedPhraseFailure, self.flowType, nil)
method resolveKeycardNextState*(self: CreatingAccountNewSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent,
@ -102,6 +91,17 @@ method resolveKeycardNextState*(self: CreatingAccountNewSeedPhraseState, keycard
if not state.isNil:
return state
if self.flowType == FlowType.SetupNewKeycardNewSeedPhrase:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
return createState(StateType.CreatingAccountNewSeedPhraseSuccess, self.flowType, nil)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.ExportPublic:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
if not self.resolveAddresses(controller, keycardEvent):
return createState(StateType.CreatingAccountNewSeedPhraseFailure, self.flowType, nil)
if not self.addAccountsToWallet(controller):
return createState(StateType.CreatingAccountNewSeedPhraseFailure, self.flowType, nil)
self.doMigration(controller)
return nil # returning nil, cause we need to remain in this state
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.StoreMetadata:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
return createState(StateType.CreatingAccountNewSeedPhraseSuccess, self.flowType, nil)
return createState(StateType.CreatingAccountNewSeedPhraseSuccess, self.flowType, nil)

View File

@ -42,6 +42,7 @@ type
pukRetries*: int
cardMetadata*: CardMetadata
generatedWalletAccount*: GeneratedWalletAccount
generatedWalletAccounts*: seq[GeneratedWalletAccount]
txSignature*: TransactionSignature
eip1581Key*: KeyDetails
encryptionKey*: KeyDetails
@ -127,8 +128,12 @@ proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent =
if(jsonObj.getProp(ResponseParamCardMeta, obj)):
result.cardMetadata = toCardMetadata(obj)
if(jsonObj.getProp(ResponseParamExportedKey, obj)):
result.generatedWalletAccount = toGeneratedWalletAccount(obj)
if jsonObj.getProp(ResponseParamExportedKey, obj):
if obj.kind == JArray:
for o in obj:
result.generatedWalletAccounts.add(toGeneratedWalletAccount(o))
else:
result.generatedWalletAccount = toGeneratedWalletAccount(obj)
if(jsonObj.getProp(ResponseParamTXSignature, obj)):
result.txSignature = toTransactionSignature(obj)

View File

@ -242,6 +242,8 @@ QtObject:
self.startFlow(payload)
proc startExportPublicFlow*(self: Service, path: string, exportMasterAddr = false, exportPrivateAddr = false, pin = "") =
## Exports addresses for passed `path`. Result of this flow sets instance of `GeneratedWalletAccount` under
## `generatedWalletAccount` property in `KeycardEvent`.
if exportPrivateAddr and not path.startsWith(DefaultEIP1581Path):
error "in order to export private address path must not be outside of eip1581 tree"
return
@ -258,6 +260,28 @@ QtObject:
self.currentFlow = KCSFlowType.ExportPublic
self.startFlow(payload)
proc startExportPublicFlow*(self: Service, paths: seq[string], exportMasterAddr = false, exportPrivateAddr = false, pin = "") =
## Exports addresses for passed `path`. Result of this flow sets array of `GeneratedWalletAccount` under
## `generatedWalletAccounts` property in `KeycardEvent`. The order of keys set in `generatedWalletAccounts` array
## mathch the order of `paths` sent to this flow.
if exportPrivateAddr:
for p in paths:
if not p.startsWith(DefaultEIP1581Path):
error "one of paths in the list refers to a private address path which is not in eip1581 tree"
return
var payload = %* {
RequestParamBIP44Path: DefaultBIP44Path,
RequestParamExportMasterAddress: exportMasterAddr,
RequestParamExportPrivate: exportPrivateAddr
}
if paths.len > 0:
payload[RequestParamBIP44Path] = %* paths
if pin.len > 0:
payload[RequestParamPIN] = %* pin
self.currentFlow = KCSFlowType.ExportPublic
self.startFlow(payload)
proc startStoreMetadataFlow*(self: Service, cardName: string, pin: string, walletPaths: seq[string]) =
var name = cardName
if cardName.len > CardNameLength:

@ -1 +1 @@
Subproject commit e4fef0fb362ca5e48292d916c83c8aaefa7ac6fa
Subproject commit b50cfe22ac3802d508e34b0561095c7100f6efa8