fix: keycard signing integration into wallet connect flows
Fixes: #15957
This commit is contained in:
parent
03e75e9532
commit
20f30a52fd
|
@ -175,7 +175,7 @@ proc newModule*(
|
|||
result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events)
|
||||
result.filter = initFilter(result.controller)
|
||||
|
||||
result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService, transactionService)
|
||||
result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService, transactionService, keycardService)
|
||||
result.walletConnectController = wc_controller.newController(result.walletConnectService, walletAccountService)
|
||||
|
||||
result.dappsConnectorService = connector_service.newService(result.events)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import NimQml
|
||||
import chronicles, times, json
|
||||
|
||||
import app/global/global_singleton
|
||||
import app_service/common/utils
|
||||
import app_service/service/wallet_connect/service as wallet_connect_service
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
|
||||
|
@ -9,6 +11,8 @@ import helpers
|
|||
logScope:
|
||||
topics = "wallet-connect-controller"
|
||||
|
||||
type
|
||||
SigningCallbackFn* = proc(topic: string, id: string, keyUid: string, address: string, signature: string)
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -27,6 +31,10 @@ QtObject:
|
|||
|
||||
result.QObject.setup
|
||||
|
||||
## signals emitted by this controller
|
||||
proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string, payload: string) {.signal.}
|
||||
proc signingResultReceived*(self: Controller, topic: string, id: string, data: string) {.signal.}
|
||||
|
||||
proc addWalletConnectSession*(self: Controller, session_json: string): bool {.slot.} =
|
||||
return self.service.addSession(session_json)
|
||||
|
||||
|
@ -41,7 +49,7 @@ QtObject:
|
|||
# Emits signal dappsListReceived with the list of dApps
|
||||
proc getDapps*(self: Controller): bool {.slot.} =
|
||||
let res = self.service.getDapps()
|
||||
if res == "":
|
||||
if res.len == 0:
|
||||
return false
|
||||
else:
|
||||
self.dappsListReceived(res)
|
||||
|
@ -61,32 +69,128 @@ QtObject:
|
|||
self.activeSessionsReceived(resultStr)
|
||||
return true
|
||||
|
||||
proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string, payload: string) {.signal.}
|
||||
|
||||
# Beware, it will fail if an authentication is already in progress
|
||||
proc authenticateUser*(self: Controller, topic: string, id: string, address: string, payload: string): bool {.slot.} =
|
||||
let acc = self.walletAccountService.getAccountByAddress(address)
|
||||
if acc.keyUid == "":
|
||||
let keypair = self.walletAccountService.getKeypairByAccountAddress(address)
|
||||
if keypair.isNil:
|
||||
return false
|
||||
|
||||
return self.service.authenticateUser(acc.keyUid, proc(password: string, pin: string, success: bool) =
|
||||
self.userAuthenticationResult(topic, id, success, password, pin, payload)
|
||||
var keyUid = singletonInstance.userProfile.getKeyUid()
|
||||
if keypair.migratedToKeycard():
|
||||
keyUid = keypair.keyUid
|
||||
return self.service.authenticateUser(keyUid, proc(receivedKeyUid: string, password: string, pin: string) =
|
||||
if receivedKeyUid.len == 0 or receivedKeyUid != keyUid or password.len == 0:
|
||||
self.userAuthenticationResult(topic, id, false, "", "", "")
|
||||
return
|
||||
self.userAuthenticationResult(topic, id, true, password, pin, payload)
|
||||
)
|
||||
|
||||
proc signMessageUnsafe*(self: Controller, address: string, password: string, message: string): string {.slot.} =
|
||||
return self.service.signMessageUnsafe(address, password, message)
|
||||
proc signOnKeycard(self: Controller, address: string): bool =
|
||||
let keypair = self.walletAccountService.getKeypairByAccountAddress(address)
|
||||
if keypair.isNil:
|
||||
raise newException(CatchableError, "cannot resolve keypair for address: " & address)
|
||||
return keypair.migratedToKeycard()
|
||||
|
||||
proc signMessage*(self: Controller, address: string, password: string, message: string): string {.slot.} =
|
||||
return self.service.signMessage(address, password, message)
|
||||
proc preparePassword(self: Controller, password: string): string =
|
||||
if singletonInstance.userProfile.getIsKeycardUser():
|
||||
return password
|
||||
return hashPassword(password)
|
||||
|
||||
proc safeSignTypedData*(self: Controller, address: string, password: string, typedDataJson: string, chainId: int, legacy: bool): string {.slot.} =
|
||||
return self.service.safeSignTypedData(address, password, typedDataJson, chainId, legacy)
|
||||
proc signMessageWithCallback(self: Controller, topic: string, id: string, address: string, message: string, password: string,
|
||||
pin: string, callback: SigningCallbackFn) =
|
||||
var res = ""
|
||||
try:
|
||||
if message.len == 0:
|
||||
raise newException(CatchableError, "message is empty")
|
||||
if self.signOnKeycard(address):
|
||||
let acc = self.walletAccountService.getAccountByAddress(address)
|
||||
if acc.isNil:
|
||||
raise newException(CatchableError, "cannot resolve account for address: " & address)
|
||||
if not self.service.runSigningOnKeycard(
|
||||
acc.keyUid,
|
||||
acc.path,
|
||||
singletonInstance.utils.removeHexPrefix(message),
|
||||
pin,
|
||||
proc(keyUid: string, signature: string) =
|
||||
if keyUid.len == 0 or keyUid != acc.keyUid or signature.len == 0:
|
||||
raise newException(CatchableError, "keycard signing failed")
|
||||
callback(topic, id, keyUid, address, signature)
|
||||
):
|
||||
raise newException(CatchableError, "runSigningOnKeycard failed")
|
||||
debug "signMessageWithCallback: signing on keycard started successfully"
|
||||
return
|
||||
let finalPassword = self.preparePassword(password)
|
||||
res = self.service.signMessage(address, finalPassword, message)
|
||||
except Exception as e:
|
||||
error "signMessageWithCallback failed: ", msg=e.msg
|
||||
callback(topic, id, "", address, res)
|
||||
|
||||
proc signTransaction*(self: Controller, address: string, chainId: int, password: string, txJson: string): string {.slot.} =
|
||||
return self.service.signTransaction(address, chainId, password, txJson)
|
||||
proc signMessage*(self: Controller, topic: string, id: string, address: string, message: string, password: string, pin: string) {.slot.} =
|
||||
var res = ""
|
||||
try:
|
||||
if message.len == 0:
|
||||
raise newException(CatchableError, "message is empty")
|
||||
let hashedMessage = self.service.hashMessageEIP191(message)
|
||||
if hashedMessage.len == 0:
|
||||
raise newException(CatchableError, "hashMessageEIP191 failed")
|
||||
self.signMessageWithCallback(topic, id, address, hashedMessage, password, pin,
|
||||
proc (topic: string, id: string, keyUid: string, address: string, signature: string) =
|
||||
self.signingResultReceived(topic, id, signature)
|
||||
)
|
||||
except Exception as e:
|
||||
error "signMessage failed: ", msg=e.msg
|
||||
self.signingResultReceived(topic, id, res)
|
||||
|
||||
proc sendTransaction*(self: Controller, address: string, chainId: int, password: string, txJson: string): string {.slot.} =
|
||||
return self.service.sendTransaction(address, chainId, password, txJson)
|
||||
proc signMessageUnsafe*(self: Controller, topic: string, id: string, address: string, message: string, password: string, pin: string) {.slot.} =
|
||||
self.signMessage(topic, id, address, message, password, pin)
|
||||
|
||||
proc safeSignTypedData*(self: Controller, topic: string, id: string, address: string, typedDataJson: string, chainId: int, legacy: bool,
|
||||
password: string, pin: string): string {.slot.} =
|
||||
var res = ""
|
||||
try:
|
||||
var dataToSign = ""
|
||||
if legacy:
|
||||
dataToSign = self.service.hashTypedData(typedDataJson)
|
||||
else:
|
||||
dataToSign = self.service.hashTypedDataV4(typedDataJson)
|
||||
if dataToSign.len == 0:
|
||||
raise newException(CatchableError, "hashTypedData failed")
|
||||
self.signMessageWithCallback(topic, id, address, dataToSign, password, pin,
|
||||
proc (topic: string, id: string, keyUid: string, address: string, signature: string) =
|
||||
self.signingResultReceived(topic, id, signature)
|
||||
)
|
||||
except Exception as e:
|
||||
error "safeSignTypedData failed: ", msg=e.msg
|
||||
self.signingResultReceived(topic, id, res)
|
||||
|
||||
proc signTransaction*(self: Controller, topic: string, id: string, address: string, chainId: int, txJson: string, password: string, pin: string) {.slot.} =
|
||||
var res = ""
|
||||
try:
|
||||
let (txHash, txData) = self.service.buildTransaction(chainId, txJson)
|
||||
if txHash.len == 0 or txData.isNil:
|
||||
raise newException(CatchableError, "building transaction failed")
|
||||
self.signMessageWithCallback(topic, id, address, txHash, password, pin,
|
||||
proc (topic: string, id: string, keyUid: string, address: string, signature: string) =
|
||||
let rawTx = self.service.buildRawTransaction(chainId, $txData, signature)
|
||||
self.signingResultReceived(topic, id, rawTx)
|
||||
)
|
||||
except Exception as e:
|
||||
error "signTransaction failed: ", msg=e.msg
|
||||
self.signingResultReceived(topic, id, res)
|
||||
|
||||
proc sendTransaction*(self: Controller, topic: string, id: string, address: string, chainId: int, txJson: string, password: string, pin: string) {.slot.} =
|
||||
var res = ""
|
||||
try:
|
||||
let (txHash, txData) = self.service.buildTransaction(chainId, txJson)
|
||||
if txHash.len == 0 or txData.isNil:
|
||||
raise newException(CatchableError, "building transaction failed")
|
||||
self.signMessageWithCallback(topic, id, address, txHash, password, pin,
|
||||
proc (topic: string, id: string, keyUid: string, address: string, signature: string) =
|
||||
let signedTxHash = self.service.sendTransactionWithSignature(chainId, $txData, signature)
|
||||
self.signingResultReceived(topic, id, signedTxHash)
|
||||
)
|
||||
except Exception as e:
|
||||
error "sendTransaction failed: ", msg=e.msg
|
||||
self.signingResultReceived(topic, id, res)
|
||||
|
||||
proc getEstimatedTime(self: Controller, chainId: int, maxFeePerGasHex: string): int {.slot.} =
|
||||
return self.service.getEstimatedTime(chainId, maxFeePerGasHex).int
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, chronicles, times, json
|
||||
import NimQml, chronicles, times, json, uuids
|
||||
import strutils
|
||||
|
||||
import backend/wallet_connect as status_go
|
||||
|
@ -8,6 +8,7 @@ import app_service/service/settings/service as settings_service
|
|||
import app_service/common/wallet_constants
|
||||
from app_service/service/transaction/dto import PendingTransactionTypeDto
|
||||
import app_service/service/transaction/service as tr
|
||||
import app_service/service/keycard/service as keycard_service
|
||||
|
||||
import app/global/global_singleton
|
||||
|
||||
|
@ -25,7 +26,8 @@ logScope:
|
|||
const UNIQUE_WALLET_CONNECT_MODULE_IDENTIFIER* = "WalletSection-WCModule"
|
||||
|
||||
type
|
||||
AuthenticationResponseFn* = proc(password: string, pin: string, success: bool)
|
||||
AuthenticationResponseFn* = proc(keyUid: string, password: string, pin: string)
|
||||
SignResponseFn* = proc(keyUid: string, signature: string)
|
||||
|
||||
QtObject:
|
||||
type Service* = ref object of QObject
|
||||
|
@ -33,8 +35,11 @@ QtObject:
|
|||
threadpool: ThreadPool
|
||||
settingsService: settings_service.Service
|
||||
transactions: tr.Service
|
||||
keycardService: keycard_service.Service
|
||||
|
||||
connectionKeycardResponse: UUID
|
||||
authenticationCallback: AuthenticationResponseFn
|
||||
signCallback: SignResponseFn
|
||||
|
||||
proc delete*(self: Service) =
|
||||
self.QObject.delete
|
||||
|
@ -44,6 +49,7 @@ QtObject:
|
|||
threadpool: ThreadPool,
|
||||
settingsService: settings_service.Service,
|
||||
transactions: tr.Service,
|
||||
keycardService: keycard_service.Service,
|
||||
): Service =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
|
@ -52,25 +58,19 @@ QtObject:
|
|||
result.threadpool = threadpool
|
||||
result.settingsService = settings_service
|
||||
result.transactions = transactions
|
||||
result.keycardService = keycardService
|
||||
|
||||
proc init*(self: Service) =
|
||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
||||
let args = SharedKeycarModuleArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_CONNECT_MODULE_IDENTIFIER:
|
||||
return
|
||||
|
||||
if self.authenticationCallback == nil:
|
||||
error "unexpected user authenticated event; no callback set"
|
||||
return
|
||||
defer:
|
||||
self.authenticationCallback = nil
|
||||
|
||||
if args.password == "" and args.pin == "":
|
||||
info "fail to authenticate user"
|
||||
self.authenticationCallback("", "", false)
|
||||
return
|
||||
|
||||
self.authenticationCallback(args.password, args.pin, true)
|
||||
self.authenticationCallback(args.keyUid, args.password, args.pin)
|
||||
|
||||
proc addSession*(self: Service, session_json: string): bool =
|
||||
# TODO #14588: call it async
|
||||
|
@ -114,105 +114,104 @@ QtObject:
|
|||
let testChains = self.settingsService.areTestNetworksEnabled()
|
||||
# TODO #14588: call it async
|
||||
return status_go.getDapps(validAtEpoch, testChains)
|
||||
|
||||
|
||||
proc getActiveSessions*(self: Service, validAtTimestamp: int64): JsonNode =
|
||||
# TODO #14588: call it async
|
||||
return status_go.getActiveSessions(validAtTimestamp)
|
||||
|
||||
|
||||
|
||||
# Will fail if another authentication is in progress
|
||||
proc authenticateUser*(self: Service, keyUid: string, callback: AuthenticationResponseFn): bool =
|
||||
if self.authenticationCallback != nil:
|
||||
return false
|
||||
self.authenticationCallback = callback
|
||||
|
||||
let data = SharedKeycarModuleAuthenticationArgs(
|
||||
uniqueIdentifier: UNIQUE_WALLET_CONNECT_MODULE_IDENTIFIER,
|
||||
keyUid: keyUid)
|
||||
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
|
||||
return true
|
||||
|
||||
proc signMessageUnsafe*(self: Service, address: string, password: string, message: string): string =
|
||||
return status_go.signMessageUnsafe(address, password, message)
|
||||
proc hashMessageEIP191*(self: Service, message: string): string =
|
||||
let hashRes = hashMessageEIP191("0x" & toHex(message))
|
||||
if not hashRes.error.isNil:
|
||||
error "hashMessageEIP191 failed: ", msg=hashRes.error.message
|
||||
return ""
|
||||
return hashRes.result.getStr()
|
||||
|
||||
proc signMessage*(self: Service, address: string, password: string, message: string): string =
|
||||
return status_go.signMessage(address, password, message)
|
||||
proc signMessage*(self: Service, address: string, hashedPassword: string, hashedMessage: string): string =
|
||||
var signMsgRes: JsonNode
|
||||
let err = wallet.signMessage(signMsgRes,
|
||||
hashedMessage,
|
||||
address,
|
||||
hashedPassword)
|
||||
if err.len > 0:
|
||||
error "status-go - wallet_signMessage failed", err=err
|
||||
return signMsgRes.getStr
|
||||
|
||||
proc safeSignTypedData*(self: Service, address: string, password: string, typedDataJson: string, chainId: int, legacy: bool): string =
|
||||
return status_go.safeSignTypedData(address, password, typedDataJson, chainId, legacy)
|
||||
|
||||
proc signTransaction*(self: Service, address: string, chainId: int, password: string, txJson: string): string =
|
||||
proc buildTransaction*(self: Service, chainId: int, txJson: string): tuple[txToSign: string, txData: JsonNode] =
|
||||
var buildTxResponse: JsonNode
|
||||
var err = wallet.buildTransaction(buildTxResponse, chainId, txJson)
|
||||
if err.len > 0:
|
||||
error "status-go - wallet_buildTransaction failed", err=err
|
||||
return ""
|
||||
return
|
||||
if buildTxResponse.isNil or buildTxResponse.kind != JsonNodeKind.JObject or
|
||||
not buildTxResponse.hasKey("txArgs") or not buildTxResponse.hasKey("messageToSign"):
|
||||
error "unexpected wallet_buildTransaction response"
|
||||
return ""
|
||||
var txToBeSigned = buildTxResponse["messageToSign"].getStr
|
||||
if txToBeSigned.len != wallet_constants.TX_HASH_LEN_WITH_PREFIX:
|
||||
return
|
||||
result.txToSign = buildTxResponse["messageToSign"].getStr
|
||||
if result.txToSign.len != wallet_constants.TX_HASH_LEN_WITH_PREFIX:
|
||||
error "unexpected tx hash length"
|
||||
return ""
|
||||
|
||||
var signMsgRes: JsonNode
|
||||
err = wallet.signMessage(signMsgRes,
|
||||
txToBeSigned,
|
||||
address,
|
||||
hashPassword(password))
|
||||
if err.len > 0:
|
||||
error "status-go - wallet_signMessage failed", err=err
|
||||
let signature = singletonInstance.utils.removeHexPrefix(signMsgRes.getStr)
|
||||
return
|
||||
result.txData = buildTxResponse["txArgs"]
|
||||
|
||||
proc buildRawTransaction*(self: Service, chainId: int, txData: string, signature: string): string =
|
||||
var txResponse: JsonNode
|
||||
err = wallet.buildRawTransaction(txResponse, chainId, $buildTxResponse["txArgs"], signature)
|
||||
var err = wallet.buildRawTransaction(txResponse, chainId, txData, signature)
|
||||
if err.len > 0:
|
||||
error "status-go - wallet_buildRawTransaction failed", err=err
|
||||
return ""
|
||||
return
|
||||
if txResponse.isNil or txResponse.kind != JsonNodeKind.JObject or not txResponse.hasKey("rawTx"):
|
||||
error "unexpected buildRawTransaction response"
|
||||
return ""
|
||||
|
||||
error "unexpected wallet_buildRawTransaction response"
|
||||
return
|
||||
return txResponse["rawTx"].getStr
|
||||
|
||||
proc sendTransaction*(self: Service, address: string, chainId: int, password: string, txJson: string): string =
|
||||
var buildTxResponse: JsonNode
|
||||
var err = wallet.buildTransaction(buildTxResponse, chainId, txJson)
|
||||
if err.len > 0:
|
||||
error "status-go - wallet_buildTransaction failed", err=err
|
||||
return ""
|
||||
if buildTxResponse.isNil or buildTxResponse.kind != JsonNodeKind.JObject or
|
||||
not buildTxResponse.hasKey("txArgs") or not buildTxResponse.hasKey("messageToSign"):
|
||||
error "unexpected wallet_buildTransaction response"
|
||||
return ""
|
||||
var txToBeSigned = buildTxResponse["messageToSign"].getStr
|
||||
if txToBeSigned.len != wallet_constants.TX_HASH_LEN_WITH_PREFIX:
|
||||
error "unexpected tx hash length"
|
||||
return ""
|
||||
|
||||
var signMsgRes: JsonNode
|
||||
err = wallet.signMessage(signMsgRes,
|
||||
txToBeSigned,
|
||||
address,
|
||||
hashPassword(password))
|
||||
if err.len > 0:
|
||||
error "status-go - wallet_signMessage failed", err=err
|
||||
let signature = singletonInstance.utils.removeHexPrefix(signMsgRes.getStr)
|
||||
|
||||
proc sendTransactionWithSignature*(self: Service, chainId: int, txData: string, signature: string): string =
|
||||
var txResponse: JsonNode
|
||||
err = wallet.sendTransactionWithSignature(txResponse, chainId,
|
||||
$PendingTransactionTypeDto.WalletConnectTransfer, $buildTxResponse["txArgs"], signature)
|
||||
let err = wallet.sendTransactionWithSignature(txResponse,
|
||||
chainId,
|
||||
$PendingTransactionTypeDto.WalletConnectTransfer,
|
||||
txData,
|
||||
singletonInstance.utils.removeHexPrefix(signature))
|
||||
if err.len > 0:
|
||||
error "status-go - sendTransactionWithSignature failed", err=err
|
||||
return ""
|
||||
if txResponse.isNil or txResponse.kind != JsonNodeKind.JString:
|
||||
error "unexpected sendTransactionWithSignature response"
|
||||
return ""
|
||||
|
||||
return txResponse.getStr
|
||||
|
||||
proc hashTypedData*(self: Service, data: string): string =
|
||||
var response: JsonNode
|
||||
let err = wallet.hashTypedData(response, data)
|
||||
if err.len > 0:
|
||||
error "status-go - hashTypedData failed", err=err
|
||||
return ""
|
||||
if response.isNil or response.kind != JsonNodeKind.JString:
|
||||
error "unexpected hashTypedData response"
|
||||
return ""
|
||||
return response.getStr
|
||||
|
||||
proc hashTypedDataV4*(self: Service, data: string): string =
|
||||
var response: JsonNode
|
||||
let err = wallet.hashTypedDataV4(response, data)
|
||||
if err.len > 0:
|
||||
error "status-go - hashTypedDataV4 failed", err=err
|
||||
return ""
|
||||
if response.isNil or response.kind != JsonNodeKind.JString:
|
||||
error "unexpected hashTypedDataV4 response"
|
||||
return ""
|
||||
return response.getStr
|
||||
|
||||
# empty maxFeePerGasHex will fetch the current chain's maxFeePerGas
|
||||
proc getEstimatedTime*(self: Service, chainId: int, maxFeePerGasHex: string): EstimatedTime =
|
||||
var maxFeePerGas: float64
|
||||
|
@ -237,4 +236,41 @@ QtObject:
|
|||
return self.transactions.getEstimatedTime(chainId, $(maxFeePerGas))
|
||||
|
||||
proc getSuggestedFees*(self: Service, chainId: int): SuggestedFeesDto =
|
||||
return self.transactions.suggestedFees(chainId)
|
||||
return self.transactions.suggestedFees(chainId)
|
||||
|
||||
proc disconnectKeycardReponseSignal(self: Service) =
|
||||
self.events.disconnect(self.connectionKeycardResponse)
|
||||
|
||||
proc connectKeycardReponseSignal(self: Service) =
|
||||
self.connectionKeycardResponse = self.events.onWithUUID(SIGNAL_KEYCARD_RESPONSE) do(e: Args):
|
||||
let args = KeycardLibArgs(e)
|
||||
self.disconnectKeycardReponseSignal()
|
||||
if self.signCallback == nil:
|
||||
error "unexpected user authenticated event; no callback set"
|
||||
return
|
||||
defer:
|
||||
self.signCallback = nil
|
||||
let currentFlow = self.keycardService.getCurrentFlow()
|
||||
if currentFlow != KCSFlowType.Sign:
|
||||
error "unexpected keycard flow type: ", currentFlow
|
||||
self.signCallback("", "")
|
||||
return
|
||||
let signature = "0x" &
|
||||
singletonInstance.utils.removeHexPrefix(args.flowEvent.txSignature.r) &
|
||||
singletonInstance.utils.removeHexPrefix(args.flowEvent.txSignature.s) &
|
||||
singletonInstance.utils.removeHexPrefix(args.flowEvent.txSignature.v)
|
||||
self.signCallback(args.flowEvent.keyUid, signature)
|
||||
|
||||
proc cancelCurrentFlow*(self: Service) =
|
||||
self.keycardService.cancelCurrentFlow()
|
||||
|
||||
proc runSigningOnKeycard*(self: Service, keyUid: string, path: string, hashedMessageToSign: string, pin: string, callback: SignResponseFn): bool =
|
||||
if pin.len == 0:
|
||||
return false
|
||||
if self.signCallback != nil:
|
||||
return false
|
||||
self.signCallback = callback
|
||||
self.cancelCurrentFlow()
|
||||
self.connectKeycardReponseSignal()
|
||||
self.keycardService.startSignFlow(path, hashedMessageToSign, pin)
|
||||
return true
|
|
@ -1,7 +1,9 @@
|
|||
import json, logging
|
||||
import json, json_serialization, logging
|
||||
import core, response_type
|
||||
from ./gen import rpc
|
||||
|
||||
import status_go
|
||||
|
||||
rpc(signMessage, "wallet"):
|
||||
message: string
|
||||
address: string
|
||||
|
@ -92,3 +94,21 @@ proc sendTransactionWithSignature*(resultOut: var JsonNode, chainId: int, txType
|
|||
except Exception as e:
|
||||
warn e.msg
|
||||
return e.msg
|
||||
|
||||
proc hashTypedData*(resultOut: var JsonNode, data: string): string =
|
||||
try:
|
||||
let rawResponse = status_go.hashTypedData(data)
|
||||
var response = Json.decode(rawResponse, RpcResponse[JsonNode])
|
||||
return prepareResponse(resultOut, response)
|
||||
except Exception as e:
|
||||
warn e.msg
|
||||
return e.msg
|
||||
|
||||
proc hashTypedDataV4*(resultOut: var JsonNode, data: string): string =
|
||||
try:
|
||||
let rawResponse = status_go.hashTypedDataV4(data)
|
||||
var response = Json.decode(rawResponse, RpcResponse[JsonNode])
|
||||
return prepareResponse(resultOut, response)
|
||||
except Exception as e:
|
||||
warn e.msg
|
||||
return e.msg
|
|
@ -1,15 +1,8 @@
|
|||
import options, logging
|
||||
import json, json_serialization
|
||||
import core, response_type
|
||||
import strutils
|
||||
|
||||
from gen import rpc
|
||||
import backend/wallet
|
||||
|
||||
import status_go
|
||||
|
||||
import app_service/service/community/dto/sign_params
|
||||
import app_service/common/utils
|
||||
|
||||
rpc(addWalletConnectSession, "wallet"):
|
||||
sessionJson: string
|
||||
|
@ -23,13 +16,6 @@ rpc(getWalletConnectActiveSessions, "wallet"):
|
|||
rpc(hashMessageEIP191, "wallet"):
|
||||
message: string
|
||||
|
||||
rpc(safeSignTypedDataForDApps, "wallet"):
|
||||
typedJson: string
|
||||
address: string
|
||||
password: string
|
||||
chainId: int
|
||||
legacy: bool
|
||||
|
||||
proc isSuccessResponse(rpcResponse: RpcResponse[JsonNode]): bool =
|
||||
return rpcResponse.error.isNil
|
||||
|
||||
|
@ -53,7 +39,7 @@ proc disconnectSession*(topic: string): bool =
|
|||
proc getActiveSessions*(validAtTimestamp: int64): JsonNode =
|
||||
try:
|
||||
let rpcRes = getWalletConnectActiveSessions(validAtTimestamp)
|
||||
|
||||
|
||||
if(not isSuccessResponse(rpcRes)):
|
||||
return nil
|
||||
|
||||
|
@ -83,47 +69,4 @@ proc getDapps*(validAtEpoch: int64, testChains: bool): string =
|
|||
return if jsonArray != "null": jsonArray else: "[]"
|
||||
except Exception as e:
|
||||
error "GetWalletConnectDapps failed: ", "msg", e.msg
|
||||
return ""
|
||||
|
||||
proc signMessageUnsafe*(address: string, password: string, message: string): string =
|
||||
try:
|
||||
let signParams = SignParamsDto(address: address, password: hashPassword(password), data: "0x" & toHex(message))
|
||||
let paramsStr = $toJson(signParams)
|
||||
let rpcResRaw = status_go.signMessage(paramsStr)
|
||||
|
||||
let rpcRes = Json.decode(rpcResRaw, RpcResponse[JsonNode])
|
||||
if(not rpcRes.error.isNil):
|
||||
return ""
|
||||
return rpcRes.result.getStr()
|
||||
except Exception as e:
|
||||
error "status_go.signMessage failed: ", "msg", e.msg
|
||||
return ""
|
||||
|
||||
proc signMessage*(address: string, password: string, message: string): string =
|
||||
try:
|
||||
let hashRes = hashMessageEIP191("0x" & toHex(message))
|
||||
if not isSuccessResponse(hashRes):
|
||||
error "wallet_hashMessageEIP191 failed: ", "msg", hashRes.error.message
|
||||
return ""
|
||||
|
||||
let safeHash = hashRes.result.getStr()
|
||||
let signRes = wallet.signMessage(safeHash, address, hashPassword(password))
|
||||
if not isSuccessResponse(signRes):
|
||||
error "wallet_signMessage failed: ", "msg", signRes.error.message
|
||||
return ""
|
||||
|
||||
return signRes.result.getStr()
|
||||
except Exception as e:
|
||||
error "signMessageForDApps failed: ", "msg", e.msg
|
||||
return ""
|
||||
|
||||
proc safeSignTypedData*(address: string, password: string, typedDataJson: string, chainId: int, legacy: bool): string =
|
||||
try:
|
||||
let rpcRes = safeSignTypedDataForDApps(typedDataJson, address, hashPassword(password), chainId, legacy)
|
||||
if not isSuccessResponse(rpcRes):
|
||||
return ""
|
||||
|
||||
return rpcRes.result.getStr()
|
||||
except Exception as e:
|
||||
error (if legacy: "wallet_safeSignTypedDataForDApps" else: "wallet_signTypedDataV4") & " failed: ", "msg", e.msg
|
||||
return ""
|
||||
return ""
|
|
@ -274,7 +274,7 @@ Item {
|
|||
compare(handler.store.authenticateUserCalls.length, 1, "expected a call to store.authenticateUser")
|
||||
|
||||
let store = handler.store
|
||||
store.userAuthenticated(td.topic, td.request.id, "password", "")
|
||||
store.userAuthenticated(td.topic, td.request.id, "hello world", "")
|
||||
compare(store.signMessageCalls.length, 1, "expected a call to store.signMessage")
|
||||
compare(store.signMessageCalls[0].message, td.request.data)
|
||||
}
|
||||
|
|
|
@ -116,6 +116,22 @@ SQUtils.QObject {
|
|||
root.displayToastMessage(qsTr("Failed to authenticate %1 from %2").arg(methodStr).arg(session.peer.metadata.url), true)
|
||||
})
|
||||
}
|
||||
|
||||
function onSigningResult(topic, id, data) {
|
||||
let isSuccessful = (data != "")
|
||||
if (isSuccessful) {
|
||||
// acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal
|
||||
sdk.acceptSessionRequest(topic, id, data)
|
||||
} else {
|
||||
console.error("signing error")
|
||||
var request = requests.findRequest(topic, id)
|
||||
if (request === null) {
|
||||
console.error("Error finding event for topic", topic, "id", id)
|
||||
return
|
||||
}
|
||||
root.sessionRequestResult(request, isSuccessful)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SQUtils.QObject {
|
||||
|
@ -334,64 +350,75 @@ SQUtils.QObject {
|
|||
return
|
||||
}
|
||||
|
||||
if (password !== "") {
|
||||
let actionResult = ""
|
||||
if (request.method === SessionRequest.methods.sign.name) {
|
||||
actionResult = store.signMessageUnsafe(request.topic, request.id,
|
||||
request.account.address, password,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data))
|
||||
} else if (request.method === SessionRequest.methods.personalSign.name) {
|
||||
actionResult = store.signMessage(request.topic, request.id,
|
||||
request.account.address, password,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data))
|
||||
} else if (request.method === SessionRequest.methods.signTypedData_v4.name ||
|
||||
request.method === SessionRequest.methods.signTypedData.name)
|
||||
{
|
||||
let legacy = request.method === SessionRequest.methods.signTypedData.name
|
||||
actionResult = store.safeSignTypedData(request.topic, request.id,
|
||||
request.account.address, password,
|
||||
if (password === "") {
|
||||
console.error("No password provided to sign message")
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === SessionRequest.methods.sign.name) {
|
||||
store.signMessageUnsafe(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data),
|
||||
password,
|
||||
pin)
|
||||
} else if (request.method === SessionRequest.methods.personalSign.name) {
|
||||
store.signMessage(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data),
|
||||
password,
|
||||
pin)
|
||||
} else if (request.method === SessionRequest.methods.signTypedData_v4.name ||
|
||||
request.method === SessionRequest.methods.signTypedData.name)
|
||||
{
|
||||
let legacy = request.method === SessionRequest.methods.signTypedData.name
|
||||
store.safeSignTypedData(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
SessionRequest.methods.signTypedData.getMessageFromData(request.data),
|
||||
request.network.chainId, legacy)
|
||||
} else if (d.isTransactionMethod(request.method)) {
|
||||
let txObj = d.getTxObject(request.method, request.data)
|
||||
if (!!payload) {
|
||||
let feesInfoJson = payload
|
||||
let hexFeesJson = root.store.convertFeesInfoToHex(feesInfoJson)
|
||||
if (!!hexFeesJson) {
|
||||
let feesInfo = JSON.parse(hexFeesJson)
|
||||
if (feesInfo.maxFeePerGas) {
|
||||
txObj.maxFeePerGas = feesInfo.maxFeePerGas
|
||||
}
|
||||
if (feesInfo.maxPriorityFeePerGas) {
|
||||
txObj.maxPriorityFeePerGas = feesInfo.maxPriorityFeePerGas
|
||||
}
|
||||
request.network.chainId,
|
||||
legacy,
|
||||
password,
|
||||
pin)
|
||||
} else if (d.isTransactionMethod(request.method)) {
|
||||
let txObj = d.getTxObject(request.method, request.data)
|
||||
if (!!payload) {
|
||||
let feesInfoJson = payload
|
||||
let hexFeesJson = root.store.convertFeesInfoToHex(feesInfoJson)
|
||||
if (!!hexFeesJson) {
|
||||
let feesInfo = JSON.parse(hexFeesJson)
|
||||
if (feesInfo.maxFeePerGas) {
|
||||
txObj.maxFeePerGas = feesInfo.maxFeePerGas
|
||||
}
|
||||
if (feesInfo.maxPriorityFeePerGas) {
|
||||
txObj.maxPriorityFeePerGas = feesInfo.maxPriorityFeePerGas
|
||||
}
|
||||
delete txObj.gasLimit
|
||||
delete txObj.gasPrice
|
||||
}
|
||||
// Remove nonce from txObj to be auto-filled by the wallet
|
||||
delete txObj.nonce
|
||||
|
||||
if (request.method === SessionRequest.methods.signTransaction.name) {
|
||||
actionResult = store.signTransaction(request.topic, request.id,
|
||||
request.account.address, request.network.chainId, password, txObj)
|
||||
} else if (request.method === SessionRequest.methods.sendTransaction.name) {
|
||||
actionResult = store.sendTransaction(request.topic, request.id,
|
||||
request.account.address, request.network.chainId, password, txObj)
|
||||
}
|
||||
delete txObj.gasLimit
|
||||
delete txObj.gasPrice
|
||||
}
|
||||
// Remove nonce from txObj to be auto-filled by the wallet
|
||||
delete txObj.nonce
|
||||
|
||||
let isSuccessful = (actionResult != "")
|
||||
if (isSuccessful) {
|
||||
// acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal
|
||||
sdk.acceptSessionRequest(request.topic, request.id, actionResult)
|
||||
} else {
|
||||
root.sessionRequestResult(request, isSuccessful)
|
||||
if (request.method === SessionRequest.methods.signTransaction.name) {
|
||||
store.signTransaction(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
request.network.chainId,
|
||||
txObj,
|
||||
password,
|
||||
pin)
|
||||
} else if (request.method === SessionRequest.methods.sendTransaction.name) {
|
||||
store.sendTransaction(
|
||||
request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
request.network.chainId,
|
||||
txObj,
|
||||
password,
|
||||
pin)
|
||||
}
|
||||
} else if (pin !== "") {
|
||||
console.debug("TODO #15097 sign message using keycard: ", request.data)
|
||||
} else {
|
||||
console.error("No password or pin provided to sign message")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -214,48 +214,59 @@ WalletConnectSDKBase {
|
|||
return
|
||||
}
|
||||
|
||||
if (password !== "") {
|
||||
var actionResult = ""
|
||||
if (request.method === SessionRequest.methods.sign.name) {
|
||||
actionResult = store.signMessageUnsafe(request.topic, request.id,
|
||||
request.account.address, password,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data))
|
||||
} else if (request.method === SessionRequest.methods.personalSign.name) {
|
||||
actionResult = store.signMessage(request.topic, request.id,
|
||||
request.account.address, password,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data))
|
||||
} else if (request.method === SessionRequest.methods.signTypedData_v4.name ||
|
||||
request.method === SessionRequest.methods.signTypedData.name)
|
||||
{
|
||||
let legacy = request.method === SessionRequest.methods.signTypedData.name
|
||||
actionResult = store.safeSignTypedData(request.topic, request.id,
|
||||
request.account.address, password,
|
||||
if (password === "") {
|
||||
console.error("No password provided to sign message")
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === SessionRequest.methods.sign.name) {
|
||||
store.signMessageUnsafe(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data),
|
||||
password,
|
||||
pin)
|
||||
} else if (request.method === SessionRequest.methods.personalSign.name) {
|
||||
store.signMessage(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
SessionRequest.methods.personalSign.getMessageFromData(request.data),
|
||||
password,
|
||||
pin)
|
||||
} else if (request.method === SessionRequest.methods.signTypedData_v4.name ||
|
||||
request.method === SessionRequest.methods.signTypedData.name)
|
||||
{
|
||||
let legacy = request.method === SessionRequest.methods.signTypedData.name
|
||||
store.safeSignTypedData(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
SessionRequest.methods.signTypedData.getMessageFromData(request.data),
|
||||
request.network.chainId, legacy)
|
||||
} else if (request.method === SessionRequest.methods.signTransaction.name) {
|
||||
let txObj = SessionRequest.methods.signTransaction.getTxObjFromData(request.data)
|
||||
actionResult = store.signTransaction(request.topic, request.id,
|
||||
request.account.address, request.network.chainId, password, txObj)
|
||||
} else if (request.method === SessionRequest.methods.sendTransaction.name) {
|
||||
let txObj = SessionRequest.methods.sendTransaction.getTxObjFromData(request.data)
|
||||
actionResult = store.sendTransaction(request.topic, request.id,
|
||||
request.account.address, request.network.chainId, password, txObj)
|
||||
}
|
||||
let isSuccessful = (actionResult != "")
|
||||
if (isSuccessful) {
|
||||
// acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal
|
||||
acceptSessionRequest(request.topic, request.method, request.id, actionResult)
|
||||
} else {
|
||||
root.sessionRequestResult(request, isSuccessful)
|
||||
}
|
||||
} else if (pin !== "") {
|
||||
console.debug("TODO #15097 sign message using keycard: ", request.data)
|
||||
} else {
|
||||
console.error("No password or pin provided to sign message")
|
||||
request.network.chainId,
|
||||
legacy,
|
||||
password,
|
||||
pin)
|
||||
} else if (request.method === SessionRequest.methods.signTransaction.name) {
|
||||
let txObj = SessionRequest.methods.signTransaction.getTxObjFromData(request.data)
|
||||
store.signTransaction(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
request.network.chainId,
|
||||
txObj,
|
||||
password,
|
||||
pin)
|
||||
} else if (request.method === SessionRequest.methods.sendTransaction.name) {
|
||||
let txObj = SessionRequest.methods.sendTransaction.getTxObjFromData(request.data)
|
||||
store.sendTransaction(request.topic,
|
||||
request.id,
|
||||
request.account.address,
|
||||
request.network.chainId,
|
||||
txObj,
|
||||
password,
|
||||
pin)
|
||||
}
|
||||
}
|
||||
|
||||
function acceptSessionRequest(topic, method, id, signature) {
|
||||
function acceptSessionRequest(topic, id, signature) {
|
||||
console.debug(`Connector DappsConnectorSDK.acceptSessionRequest; requestId: ${root.requestId}, signature: "${signature}"`)
|
||||
|
||||
sessionRequestLoader.active = false
|
||||
|
@ -322,6 +333,16 @@ WalletConnectSDKBase {
|
|||
root.displayToastMessage(qsTr("Failed to authenticate %1").arg(session.peer.metadata.url), true)
|
||||
})
|
||||
}
|
||||
|
||||
function onSigningResult(topic, id, data) {
|
||||
let isSuccessful = (data != "")
|
||||
if (isSuccessful) {
|
||||
// acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal
|
||||
d.acceptSessionRequest(topic, id, data)
|
||||
} else {
|
||||
console.error("signing error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
|
|
@ -245,10 +245,6 @@ QObject {
|
|||
|
||||
readonly property var supportedAccountsModel: SortFilterProxyModel {
|
||||
sourceModel: root.walletRootStore.nonWatchAccounts
|
||||
filters: ValueFilter {
|
||||
roleName: "keycardAccount"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
property var currentSessionProposal: null
|
||||
|
|
|
@ -12,6 +12,8 @@ QObject {
|
|||
signal userAuthenticated(string topic, string id, string password, string pin, string payload)
|
||||
signal userAuthenticationFailed(string topic, string id)
|
||||
|
||||
signal signingResult(string topic, string id, string data)
|
||||
|
||||
function addWalletConnectSession(sessionJson) {
|
||||
return controller.addWalletConnectSession(sessionJson)
|
||||
}
|
||||
|
@ -31,19 +33,16 @@ QObject {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns the hex encoded signature of the message or empty string if error
|
||||
function signMessageUnsafe(topic, id, address, password, message) {
|
||||
return controller.signMessageUnsafe(address, password, message)
|
||||
function signMessageUnsafe(topic, id, address, message, password, pin = "") {
|
||||
controller.signMessageUnsafe(topic, id, address, message, password, pin)
|
||||
}
|
||||
|
||||
// Returns the hex encoded signature of the message or empty string if error
|
||||
function signMessage(topic, id, address, password, message) {
|
||||
return controller.signMessage(address, password, message)
|
||||
function signMessage(topic, id, address, message, password, pin = "") {
|
||||
controller.signMessage(topic, id, address, message, password, pin)
|
||||
}
|
||||
|
||||
// Returns the hex encoded signature of the typedDataJson or empty string if error
|
||||
function safeSignTypedData(topic, id, address, password, typedDataJson, chainId, legacy) {
|
||||
return controller.safeSignTypedData(address, password, typedDataJson, chainId, legacy)
|
||||
function safeSignTypedData(topic, id, address, typedDataJson, chainId, legacy, password, pin = "") {
|
||||
controller.safeSignTypedData(topic, id, address, typedDataJson, chainId, legacy, password, pin)
|
||||
}
|
||||
|
||||
// Remove leading zeros from hex number as expected by status-go
|
||||
|
@ -91,16 +90,14 @@ QObject {
|
|||
return JSON.parse(controller.getSuggestedFeesJson(chainId))
|
||||
}
|
||||
|
||||
// Returns the hex encoded signature of the transaction or empty string if error
|
||||
function signTransaction(topic, id, address, chainId, password, txObj) {
|
||||
let tx = prepareTxForStatusGo(txObj)
|
||||
return controller.signTransaction(address, chainId, password, JSON.stringify(tx))
|
||||
controller.signTransaction(topic, id, address, chainId, JSON.stringify(tx), password, pin)
|
||||
}
|
||||
|
||||
// Returns the hash of the transaction or empty string if error
|
||||
function sendTransaction(topic, id, address, chainId, password, txObj) {
|
||||
function sendTransaction(topic, id, address, chainId, txObj, password, pin = "") {
|
||||
let tx = prepareTxForStatusGo(txObj)
|
||||
return controller.sendTransaction(address, chainId, password, JSON.stringify(tx))
|
||||
controller.sendTransaction(topic, id, address, chainId, JSON.stringify(tx), password, pin)
|
||||
}
|
||||
|
||||
/// \c getDapps triggers an async response to \c dappsListReceived
|
||||
|
@ -149,5 +146,9 @@ QObject {
|
|||
root.userAuthenticationFailed(topic, id)
|
||||
}
|
||||
}
|
||||
|
||||
function onSigningResultReceived(topic, id, data) {
|
||||
root.signingResult(topic, id, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 5546ffaea2b2135e7a3da58845e230d8e26fe709
|
||||
Subproject commit 8923d70ec0348c7b238ce6d5d86ebe6682577ba4
|
Loading…
Reference in New Issue