From 76e867f1d848380de669c89eeb8ddff5d42b54fa Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Wed, 27 Oct 2021 14:44:35 +0200 Subject: [PATCH] Refactor/wallet part 2 (#91) * refactor: add save account * refactor: add account generation * refactor: save settings * refactor: add update account in new be * add getTransfersByAddress (#93) Co-authored-by: Jonathan Rainville --- status/statusgo_backend_new/accounts.nim | 108 ++++++++++++++++++- status/statusgo_backend_new/conversions.nim | 35 ++++++ status/statusgo_backend_new/eth.nim | 3 + status/statusgo_backend_new/settings.nim | 5 +- status/statusgo_backend_new/transactions.nim | 16 ++- 5 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 status/statusgo_backend_new/conversions.nim diff --git a/status/statusgo_backend_new/accounts.nim b/status/statusgo_backend_new/accounts.nim index 82e4f72..4516ec2 100644 --- a/status/statusgo_backend_new/accounts.nim +++ b/status/statusgo_backend_new/accounts.nim @@ -1,4 +1,4 @@ -import json, json_serialization, chronicles +import json, json_serialization, chronicles, nimcrypto import ./core, ./utils import ./response_type @@ -12,9 +12,29 @@ logScope: const NUMBER_OF_ADDRESSES_TO_GENERATE = 5 const MNEMONIC_PHRASE_LENGTH = 12 +const GENERATED* = "generated" +const SEED* = "seed" +const KEY* = "key" +const WATCH* = "watch" + proc getAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} = return core.callPrivateRPC("accounts_getAccounts") +proc deleteAccount*(address: string): RpcResponse[JsonNode] {.raises: [Exception].} = + return core.callPrivateRPC("accounts_deleteAccount", %* [address]) + +proc updateAccount*(name, address, publicKey, walletType, color: string) {.raises: [Exception].} = + discard core.callPrivateRPC("accounts_saveAccounts", %* [ + [{ + "color": color, + "name": name, + "address": address, + "public-key": publicKey, + "type": walletType, + "path": "m/44'/60'/0'/0/1" # <--- TODO: fix this. Derivation path is not supposed to change + }] + ]) + proc generateAddresses*(paths: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} = let payload = %* { "n": NUMBER_OF_ADDRESSES_TO_GENERATE, @@ -102,6 +122,69 @@ proc storeDerivedAccounts*(id, hashedPassword: string, paths: seq[string]): error "error doing rpc request", methodName = "storeDerivedAccounts", exception=e.msg raise newException(RpcException, e.msg) +proc storeAccounts*(id, hashedPassword: string): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* { + "accountID": id, + "password": hashedPassword + } + + try: + let response = status_go.multiAccountStoreAccount($payload) + result.result = Json.decode(response, JsonNode) + + except RpcException as e: + error "error doing rpc request", methodName = "storeAccounts", exception=e.msg + raise newException(RpcException, e.msg) + +proc hashPassword*(password: string): string = + result = "0x" & $keccak_256.digest(password) + +proc saveAccount*( + address: string, + name: string, + password: string, + color: string, + accountType: string, + isADerivedAccount = true, + walletIndex: int = 0, + id: string = "", + publicKey: string = "", +) {.raises: [Exception].} = + var derivationPath = "m/44'/60'/0'/0/0" + let hashedPassword = hashPassword(password) + + if (isADerivedAccount): + let derivationPath = (if accountType == GENERATED: "m/" else: "m/44'/60'/0'/0/") & $walletIndex + discard storeDerivedAccounts(id, hashedPassword, @[derivationPath]) + elif accountType == KEY: + discard storeAccounts(id, hashedPassword) + + discard callPrivateRPC("accounts_saveAccounts", %* [ + [{ + "color": color, + "name": name, + "address": address, + "public-key": publicKey, + "type": accountType, + "path": derivationPath + }] + ]) + +proc loadAccount*(address: string, password: string): RpcResponse[JsonNode] {.raises: [Exception].} = + let hashedPassword = hashPassword(password) + let payload = %* { + "address": address, + "password": hashedPassword + } + + try: + let response = status_go.multiAccountLoadAccount($payload) + result.result = Json.decode(response, JsonNode) + + except RpcException as e: + error "error doing rpc request", methodName = "storeAccounts", exception=e.msg + raise newException(RpcException, e.msg) + proc addPeer*(peer: string): RpcResponse[JsonNode] {.raises: [Exception].} = try: let response = status_go.addPeer(peer) @@ -142,3 +225,26 @@ proc login*(name, keyUid, hashedPassword, identicon, thumbnail, large: string): except RpcException as e: error "error doing rpc request", methodName = "login", exception=e.msg raise newException(RpcException, e.msg) + +proc multiAccountImportPrivateKey*(privateKey: string): RpcResponse[JsonNode] = + let payload = %* { + "privateKey": privateKey + } + try: + let response = status_go.multiAccountImportPrivateKey($payload) + result.result = Json.decode(response, JsonNode) + + except RpcException as e: + error "error doing rpc request", methodName = "multiAccountImportPrivateKey", exception=e.msg + raise newException(RpcException, e.msg) + +proc verifyAccountPassword*(address: string, password: string, keystoreDir: string): bool = + let hashedPassword = hashPassword(password) + let verifyResult = status_go.verifyAccountPassword(keystoreDir, address, hashedPassword) + let error = parseJson(verifyResult)["error"].getStr + + if error == "": + return true + + return false + \ No newline at end of file diff --git a/status/statusgo_backend_new/conversions.nim b/status/statusgo_backend_new/conversions.nim new file mode 100644 index 0000000..b941cd1 --- /dev/null +++ b/status/statusgo_backend_new/conversions.nim @@ -0,0 +1,35 @@ +import + json, options, strutils + +import + web3/[conversions, ethtypes], stint + +import ../types/transaction + +# TODO: make this public in nim-web3 lib +template stripLeadingZeros*(value: string): string = + var cidx = 0 + # ignore the last character so we retain '0' on zero value + while cidx < value.len - 1 and value[cidx] == '0': + cidx.inc + value[cidx .. ^1] + +proc `%`*(x: TransactionData): JsonNode = + result = newJobject() + result["from"] = %x.source + result["type"] = %x.txType + if x.to.isSome: + result["to"] = %x.to.unsafeGet + if x.gas.isSome: + result["gas"] = %x.gas.unsafeGet + if x.gasPrice.isSome: + result["gasPrice"] = %("0x" & x.gasPrice.unsafeGet.toHex.stripLeadingZeros) + if x.maxFeePerGas.isSome: + result["maxFeePerGas"] = %("0x" & x.maxFeePerGas.unsafeGet.toHex) + if x.maxPriorityFeePerGas.isSome: + result["maxPriorityFeePerGas"] = %("0x" & x.maxPriorityFeePerGas.unsafeGet.toHex) + if x.value.isSome: + result["value"] = %("0x" & x.value.unsafeGet.toHex) + result["data"] = %x.data + if x.nonce.isSome: + result["nonce"] = %x.nonce.unsafeGet \ No newline at end of file diff --git a/status/statusgo_backend_new/eth.nim b/status/statusgo_backend_new/eth.nim index e72264b..e56e4d5 100644 --- a/status/statusgo_backend_new/eth.nim +++ b/status/statusgo_backend_new/eth.nim @@ -3,6 +3,9 @@ import ./core, ./response_type export response_type +proc getAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} = + return core.callPrivateRPC("eth_accounts") + proc getBlockByNumber*(blockNumber: string): RpcResponse[JsonNode] {.raises: [Exception].} = let payload = %* [blockNumber, false] return core.callPrivateRPC("eth_getBlockByNumber", payload) diff --git a/status/statusgo_backend_new/settings.nim b/status/statusgo_backend_new/settings.nim index b9d1a2d..7a11b7e 100644 --- a/status/statusgo_backend_new/settings.nim +++ b/status/statusgo_backend_new/settings.nim @@ -4,4 +4,7 @@ import ./core, ./response_type export response_type proc getSettings*(): RpcResponse[JsonNode] {.raises: [Exception].} = - return core.callPrivateRPC("settings_getSettings") \ No newline at end of file + return core.callPrivateRPC("settings_getSettings") + +proc saveSettings*(key: string, value: string | JsonNode | bool | int) {.raises: [Exception].} = + discard core.callPrivateRPC("settings_saveSetting", %* [key, value]) \ No newline at end of file diff --git a/status/statusgo_backend_new/transactions.nim b/status/statusgo_backend_new/transactions.nim index 0c3bb12..db3f241 100644 --- a/status/statusgo_backend_new/transactions.nim +++ b/status/statusgo_backend_new/transactions.nim @@ -1,7 +1,17 @@ -import json +import json, stint, chronicles, strutils, conversions -import ./core + +import ../types/transaction +import ./core as core proc checkRecentHistory*(addresses: seq[string]) {.raises: [Exception].} = let payload = %* [addresses] - discard callPrivateRPC("wallet_checkRecentHistory", payload) \ No newline at end of file + discard callPrivateRPC("wallet_checkRecentHistory", payload) + +proc getTransfersByAddress*(address: string, toBlock: Uint256, limit: int, loadMore: bool = false): RpcResponse[JsonNode] {.raises: [Exception].} = + let + toBlockParsed = if not loadMore: newJNull() else: %("0x" & stint.toHex(toBlock)) + limitParsed = "0x" & limit.toHex.stripLeadingZeros + + callPrivateRPC("wallet_getTransfersByAddress", %* [address, toBlockParsed, limitParsed, loadMore]) + \ No newline at end of file