move 'models' to status lib for clarity

This commit is contained in:
Iuri Matias 2020-05-29 15:54:35 -04:00
parent f16de508ed
commit 73069255cd
42 changed files with 495 additions and 496 deletions

View File

@ -1,8 +1,8 @@
import NimQml
import json, eventemitter
import ../../models/chat as chat_model
import ../../status/chat as chat_model
import ../../signals/types
import ../../status/types as status_types
import ../../status/libstatus/types as status_types
import views/channels_list
import view
import chronicles

View File

@ -3,7 +3,7 @@ import Tables
import views/channels_list
import views/message_list
import ../../signals/types
import ../../models/chat
import ../../status/chat
QtObject:
type

View File

@ -3,7 +3,7 @@ import Tables
import strformat
import random
import ../../../models/chat
import ../../../status/chat
const accountColors* = [
"#9B832F",

View File

@ -1,5 +1,5 @@
import NimQml, Tables
import ../../../models/chat
import ../../../status/chat
type
ChatMessageRoles {.pure.} = enum

View File

@ -1,9 +1,9 @@
import NimQml
import ../../status/types as status_types
import ../../status/libstatus/types as status_types
import ../../signals/types
import eventemitter
import view
import ../../models/accounts as AccountModel
import ../../status/accounts as AccountModel
import chronicles
import options
import std/wrapnils

View File

@ -3,12 +3,12 @@ import Tables
import json
import nimcrypto
import ../../signals/types
import ../../status/types as status_types
import ../../status/accounts as status_accounts
import ../../status/libstatus/types as status_types
import ../../status/libstatus/accounts as status_accounts
import strformat
import json_serialization
import core
import ../../models/accounts as AccountModel
import ../../status/accounts as AccountModel
type
AccountRoles {.pure.} = enum

View File

@ -1,9 +1,8 @@
import NimQml
import chronicles
import eventemitter
import "../../status/core" as status
import ../../signals/types
import ../../models/node
import ../../status/node
import view
logScope:

View File

@ -1,5 +1,5 @@
import NimQml
import ../../models/node
import ../../status/node
QtObject:
type NodeView* = ref object of QObject

View File

@ -1,7 +1,7 @@
import NimQml
import ../../status/types as status_types
import ../../status/accounts as status_accounts
import ../../models/accounts as AccountModel
import ../../status/libstatus/types as status_types
import ../../status/libstatus/accounts as status_accounts
import ../../status/accounts as AccountModel
import eventemitter
import view
import chronicles

View File

@ -2,11 +2,11 @@ import NimQml
import Tables
import json
import nimcrypto
import ../../status/types as status_types
import ../../status/libstatus/types as status_types
import ../../signals/types
import strformat
import json_serialization
import ../../models/accounts as AccountModel
import ../../status/accounts as AccountModel
type
AccountRoles {.pure.} = enum

View File

@ -2,12 +2,12 @@ import NimQml
import eventemitter
import strformat
import json
import "../../status/core" as status
import ../../status/mailservers as status_mailservers
import "../../status/libstatus/core" as status
import ../../status/libstatus/mailservers as status_mailservers
import ../../signals/types
import view
import "../../status/types" as status_types
import ../../models/profile
import "../../status/libstatus/types" as status_types
import ../../status/profile
type ProfileController* = object
view*: ProfileView

View File

@ -2,7 +2,7 @@ import NimQml
import views/mailservers_list
import views/contact_list
import views/profile_info
import ../../models/profile
import ../../status/profile
QtObject:
type ProfileView* = ref object of QObject

View File

@ -1,7 +1,7 @@
import NimQml
import Tables
import strformat
import ../../../models/profile
import ../../../status/profile
type
ContactRoles {.pure.} = enum

View File

@ -1,6 +1,6 @@
import NimQml
import Tables
import ../../../models/profile
import ../../../status/profile
type
MailServerRoles {.pure.} = enum

View File

@ -1,5 +1,5 @@
import NimQml
import ../../../models/profile
import ../../../status/profile
QtObject:
type ProfileInfoView* = ref object of QObject

View File

@ -5,8 +5,8 @@ import strutils
import chronicles
import view
import ../../status/wallet as status_wallet
import ../../models/wallet
import ../../status/libstatus/wallet as status_wallet
import ../../status/wallet
import ../../signals/types
type WalletController* = ref object of SignalSubscriber

View File

@ -1,7 +1,7 @@
import NimQml
import Tables
import views/asset_list
import ../../models/wallet
import ../../status/wallet
QtObject:
type

View File

@ -1,7 +1,7 @@
import NimQml
import Tables
import strformat
import ../../../models/wallet
import ../../../status/wallet
type
AssetRoles {.pure.} = enum

View File

@ -1,31 +0,0 @@
import ../status/accounts as status_accounts
import ../status/types
import options
type
AccountModel* = ref object
generatedAddresses*: seq[GeneratedAccount]
nodeAccounts*: seq[NodeAccount]
currentAccount*: Account
proc newAccountModel*(): AccountModel =
result = AccountModel()
result.currentAccount = nil
proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
var accounts = status_accounts.generateAddresses()
for account in accounts.mitems:
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
account.photoPath = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
self.generatedAddresses.add(account)
self.generatedAddresses
proc login*(self: AccountModel, selectedAccountIndex: int, password: string): NodeAccount =
let currentNodeAccount = self.nodeAccounts[selectedAccountIndex]
self.currentAccount = currentNodeAccount.toAccount
result = status_accounts.login(currentNodeAccount, password)
proc storeAccountAndLogin*(self: AccountModel, selectedAccountIndex: int, password: string): Account =
let generatedAccount: GeneratedAccount = self.generatedAddresses[selectedAccountIndex]
result = status_accounts.setupAccount(generatedAccount, password)
self.currentAccount = generatedAccount.toAccount

View File

@ -1,103 +0,0 @@
import eventemitter, sets, json, strutils
import sequtils
import ../status/utils
import ../status/chat as status_chat
import chronicles
import ../signals/types
import chat/chat_item
import chat/chat_message
import tables
export chat_item
export chat_message
type MsgArgs* = ref object of Args
message*: string
chatId*: string
payload*: JsonNode
type ChannelArgs* = ref object of Args
channel*: string
chatTypeInt*: ChatType
type ChatArgs* = ref object of Args
chats*: seq[Chat]
type
ChatModel* = ref object
events*: EventEmitter
channels*: HashSet[string]
filters*: Table[string, string]
proc newChatModel*(): ChatModel =
result = ChatModel()
result.events = createEventEmitter()
result.channels = initHashSet[string]()
result.filters = initTable[string, string]()
proc delete*(self: ChatModel) =
discard
proc hasChannel*(self: ChatModel, chatId: string): bool =
result = self.channels.contains(chatId)
proc getActiveChannel*(self: ChatModel): string =
if (self.channels.len == 0): "" else: self.channels.toSeq[self.channels.len - 1]
proc join*(self: ChatModel, chatId: string, chatTypeInt: ChatType, isNewChat: bool = true) =
if self.hasChannel(chatId): return
self.channels.incl chatId
let generatedSymKey = status_chat.generateSymKeyFromPassword()
# TODO get this from the connection or something
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
let oneToOne = isOneToOneChat(chatId)
if isNewChat: status_chat.saveChat(chatId, oneToOne)
let filterResult = status_chat.loadFilters(chatId = chatId, oneToOne = oneToOne)
status_chat.chatMessages(chatId)
let parsedResult = parseJson(filterResult)["result"]
var topics = newSeq[string](0)
for topicObj in parsedResult:
if (($topicObj["chatId"]).strip(chars = {'"'}) == chatId):
topics.add(($topicObj["topic"]).strip(chars = {'"'}))
if(not self.filters.hasKey(chatId)): self.filters[chatId] = topicObj["filterId"].getStr
if (topics.len == 0):
warn "No topic found for the chat. Cannot load past messages"
else:
status_chat.requestMessages(topics, generatedSymKey, peer, 20)
self.events.emit("channelJoined", ChannelArgs(channel: chatId, chatTypeInt: chatTypeInt))
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
proc load*(self: ChatModel) =
let chatList = status_chat.loadChats()
for chat in chatList:
# TODO: use correct type of chat instead of hardcoded 2 (assumes it's only public chats)
self.join(chat.id, ChatType.Public, false)
self.events.emit("chatsLoaded", ChatArgs(chats: chatList))
proc leave*(self: ChatModel, chatId: string) =
status_chat.removeFilters(chatId, self.filters[chatId])
status_chat.deactivateChat(chatId)
# TODO: REMOVE MAILSERVER TOPIC
# TODO: REMOVE HISTORY
self.filters.del(chatId)
self.channels.excl(chatId)
self.events.emit("channelLeft", ChannelArgs(channel: chatId))
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
proc sendMessage*(self: ChatModel, chatId: string, msg: string): string =
var sentMessage = status_chat.sendChatMessage(chatId, msg)
var parsedMessage = parseJson(sentMessage)["result"]["chats"][0]["lastMessage"]
self.events.emit("messageSent", MsgArgs(message: msg, chatId: chatId, payload: parsedMessage))
sentMessage

View File

@ -1,40 +0,0 @@
import eventemitter
import json
import strformat
import strutils
import ../status/wallet as status_wallet
type Asset* = ref object
name*, symbol*, value*, fiatValue*, image*: string
type WalletModel* = ref object
events*: EventEmitter
proc newWalletModel*(): WalletModel =
result = WalletModel()
result.events = createEventEmitter()
proc delete*(self: WalletModel) =
discard
proc sendTransaction*(self: WalletModel, from_value: string, to: string, value: string, password: string): string =
status_wallet.sendTransaction(from_value, to, value, password)
proc getEthBalance*(self: WalletModel, address: string): string =
var balance = status_wallet.getBalance(address)
echo(fmt"balance in hex: {balance}")
# 2. convert balance to eth
var eth_value = status_wallet.hex2Eth(balance)
echo(fmt"balance in eth: {eth_value}")
eth_value
proc getFiatValue*(self: WalletModel, eth_balance: string, symbol: string, fiat_symbol: string): float =
# 3. get usd price of 1 eth
var usd_eth_price = status_wallet.getPrice("ETH", "USD")
echo(fmt"usd_price: {usd_eth_price}")
# 4. convert balance to usd
var usd_balance = parseFloat(eth_balance) * parseFloat(usd_eth_price)
echo(fmt"balance in usd: {usd_balance}")
usd_balance

View File

@ -8,12 +8,12 @@ import signals/core as signals
import app/onboarding/core as onboarding
import app/login/core as login
import state
import status/accounts as status_accounts
import status/core as status_core
import status/types as types
import status/libstatus
import status/libstatus/accounts as status_accounts
import status/libstatus/core as status_core
import status/libstatus/types as types
import status/libstatus/libstatus
import status/libstatus/types
import state
import status/types
import eventemitter
import json_serialization

View File

@ -1,5 +1,5 @@
import NimQml
import ../status/types as status_types
import ../status/libstatus/types as status_types
import tables
import json
import types

View File

@ -1,6 +1,6 @@
import json
import chronicles
import ../status/types
import ../status/libstatus/types
import json_serialization
type SignalSubscriber* = ref object of RootObj

View File

@ -1,161 +1,31 @@
import libstatus
import core
import json
import utils
import accounts/constants
import nimcrypto
import os
import uuids
import types
import json_serialization
import chronicles
import libstatus/accounts as status_accounts
import libstatus/types
import options
proc queryAccounts*(): string =
var response = callPrivateRPC("eth_accounts")
result = parseJson(response)["result"][0].getStr()
type
AccountModel* = ref object
generatedAddresses*: seq[GeneratedAccount]
nodeAccounts*: seq[NodeAccount]
currentAccount*: Account
proc generateAddresses*(): seq[GeneratedAccount] =
let multiAccountConfig = %* {
"n": 5,
"mnemonicPhraseLength": 12,
"bip39Passphrase": "",
"paths": [PATH_WHISPER, PATH_WALLET_ROOT, PATH_DEFAULT_WALLET]
}
result = Json.decode($libstatus.multiAccountGenerateAndDeriveAddresses($multiAccountConfig), seq[GeneratedAccount])
proc newAccountModel*(): AccountModel =
result = AccountModel()
result.currentAccount = nil
proc generateAlias*(publicKey: string): string =
result = $libstatus.generateAlias(publicKey.toGoString)
proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
var accounts = status_accounts.generateAddresses()
for account in accounts.mitems:
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
account.photoPath = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
self.generatedAddresses.add(account)
self.generatedAddresses
proc generateIdenticon*(publicKey: string): string =
result = $libstatus.identicon(publicKey.toGoString)
proc login*(self: AccountModel, selectedAccountIndex: int, password: string): NodeAccount =
let currentNodeAccount = self.nodeAccounts[selectedAccountIndex]
self.currentAccount = currentNodeAccount.toAccount
result = status_accounts.login(currentNodeAccount, password)
proc ensureDir(dirname: string) =
if not existsDir(dirname):
# removeDir(dirname)
createDir(dirname)
proc initNodeAccounts*(): seq[NodeAccount] =
const datadir = "./data/"
const keystoredir = "./data/keystore/"
const nobackupdir = "./noBackup/"
ensureDir(datadir)
ensureDir(keystoredir)
ensureDir(nobackupdir)
discard $libstatus.initKeystore(keystoredir);
let strNodeAccounts = $libstatus.openAccounts(datadir);
result = Json.decode(strNodeAccounts, seq[NodeAccount])
proc saveAccountAndLogin*(
multiAccounts: MultiAccounts,
alias: string,
identicon: string,
accountData: string,
password: string,
configJSON: string,
settingsJSON: string): Account =
let hashedPassword = "0x" & $keccak_256.digest(password)
let subaccountData = %* [
{
"public-key": multiAccounts.defaultWallet.publicKey,
"address": multiAccounts.defaultWallet.address,
"color": "#4360df",
"wallet": true,
"path": constants.PATH_DEFAULT_WALLET,
"name": "Status account"
},
{
"public-key": multiAccounts.whisper.publicKey,
"address": multiAccounts.whisper.address,
"name": alias,
"photo-path": identicon,
"path": constants.PATH_WHISPER,
"chat": true
}
]
var savedResult = $libstatus.saveAccountAndLogin(accountData, hashedPassword, settingsJSON, configJSON, $subaccountData)
let parsedSavedResult = savedResult.parseJson
let error = parsedSavedResult["error"].getStr
if error == "":
debug "Account saved succesfully"
result = Account(name: alias, photoPath: identicon)
return
raise newException(LoginError, "Error saving account and logging in: " & error)
proc generateMultiAccounts*(account: GeneratedAccount, password: string): MultiAccounts =
let hashedPassword = "0x" & $keccak_256.digest(password)
let multiAccount = %* {
"accountID": account.id,
"paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET],
"password": hashedPassword
}
var response = $libstatus.multiAccountStoreDerivedAccounts($multiAccount);
result = Json.decode($response, MultiAccounts)
proc getAccountData*(account: GeneratedAccount, alias: string, identicon: string): JsonNode =
result = %* {
"name": alias,
"address": account.address,
"photo-path": identicon,
"key-uid": account.keyUid,
"keycard-pairing": nil
}
proc getAccountSettings*(account: GeneratedAccount, alias: string, identicon: string, multiAccounts: MultiAccounts, defaultNetworks: JsonNode): JsonNode =
result = %* {
"key-uid": account.keyUid,
"mnemonic": account.mnemonic,
"public-key": multiAccounts.whisper.publicKey,
"name": alias,
"address": account.address,
"eip1581-address": multiAccounts.eip1581.address,
"dapps-address": multiAccounts.defaultWallet.address,
"wallet-root-address": multiAccounts.walletRoot.address,
"preview-privacy?": true,
"signing-phrase": generateSigningPhrase(3),
"log-level": "INFO",
"latest-derived-path": 0,
"networks/networks": defaultNetworks,
"currency": "usd",
"photo-path": identicon,
"waku-enabled": true,
"wallet/visible-tokens": {
"mainnet": ["SNT"]
},
"appearance": 0,
"networks/current-network": "mainnet_rpc",
"installation-id": $genUUID()
}
proc setupAccount*(account: GeneratedAccount, password: string): Account =
let multiAccounts = generateMultiAccounts(account, password)
let whisperPubKey = account.derived.whisper.publicKey
let alias = generateAlias(whisperPubKey)
let identicon =generateIdenticon(whisperPubKey)
let accountData = getAccountData(account, alias, identicon)
var settingsJSON = getAccountSettings(account, alias, identicon, multiAccounts, constants.DEFAULT_NETWORKS)
result = saveAccountAndLogin(multiAccounts, alias, identicon, $accountData, password, $constants.NODE_CONFIG, $settingsJSON)
# TODO this is needed for now for the retrieving of past messages. We'll either move or remove it later
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
discard libstatus.addPeer(peer)
proc login*(nodeAccount: NodeAccount, password: string): NodeAccount =
let hashedPassword = "0x" & $keccak_256.digest(password)
let account = nodeAccount.toAccount
let loginResult = $libstatus.login($toJson(account), hashedPassword)
let error = parseJson(loginResult)["error"].getStr
if error == "":
debug "Login requested", user=nodeAccount.name
result = nodeAccount
return
raise newException(LoginError, "Error logging in: " & error)
proc storeAccountAndLogin*(self: AccountModel, selectedAccountIndex: int, password: string): Account =
let generatedAccount: GeneratedAccount = self.generatedAddresses[selectedAccountIndex]
result = status_accounts.setupAccount(generatedAccount, password)
self.currentAccount = generatedAccount.toAccount

View File

@ -1,106 +1,103 @@
import core
import json
import utils
import times
import strutils
import eventemitter, sets, json, strutils
import sequtils
import libstatus/utils
import libstatus/chat as status_chat
import chronicles
import ../signals/types
import ../signals/messages
import chat/chat_item
import chat/chat_message
import tables
export chat_item
export chat_message
proc loadFilters*(chatId: string, filterId: string = "", symKeyId: string = "", oneToOne: bool = false, identity: string = "", topic: string = "", discovery: bool = false, negotiated: bool = false, listen: bool = true): string =
result = callPrivateRPC("loadFilters".prefix, %* [
[{
"ChatID": chatId, # identifier of the chat
"FilterID": filterId, # whisper filter id generated
"SymKeyID": symKeyId, # symmetric key id used for symmetric filters
"OneToOne": oneToOne, # if asymmetric encryption is used for this chat
"Identity": identity, # public key of the other recipient for non-public filters.
# FIXME: passing empty string to the topic makes it error
# "Topic": topic, # whisper topic
"Discovery": discovery,
"Negotiated": negotiated,
"Listen": listen # whether we are actually listening for messages on this chat, or the filter is only created in order to be able to post on the topic
}]
])
type MsgArgs* = ref object of Args
message*: string
chatId*: string
payload*: JsonNode
proc removeFilters*(chatId: string, filterId: string) =
discard callPrivateRPC("removeFilters".prefix, %* [
[{
"ChatID": chatId,
"FilterID": filterId
}]
])
type ChannelArgs* = ref object of Args
channel*: string
chatTypeInt*: ChatType
proc saveChat*(chatId: string, oneToOne: bool = false, active: bool = true) =
discard callPrivateRPC("saveChat".prefix, %* [
{
"lastClockValue": 0, # TODO:
"color": "#51d0f0", # TODO:
"name": chatId,
"lastMessage": nil, # TODO:
"active": active,
"id": chatId,
"unviewedMessagesCount": 0, # TODO:
# TODO use constants for those too or use the Date
"chatType": if oneToOne: 1 else: 2, # TODO: use constants
"timestamp": 1588940692659 # TODO:
}
])
type ChatArgs* = ref object of Args
chats*: seq[Chat]
proc deactivateChat*(chatId: string) =
discard callPrivateRPC("saveChat".prefix, %* [
{
"lastClockValue": 0,
"color": "",
"name": chatId,
"lastMessage": nil,
"active": false,
"id": chatId,
"unviewedMessagesCount": 0,
"timestamp": 0
}
])
type
ChatModel* = ref object
events*: EventEmitter
channels*: HashSet[string]
filters*: Table[string, string]
proc loadChats*(): seq[Chat] =
result = @[]
let jsonResponse = parseJson($callPrivateRPC("chats".prefix))
if jsonResponse["result"].kind != JNull:
for jsonChat in jsonResponse{"result"}:
let chat = jsonChat.toChat
if chat.active: result.add(jsonChat.toChat)
proc newChatModel*(): ChatModel =
result = ChatModel()
result.events = createEventEmitter()
result.channels = initHashSet[string]()
result.filters = initTable[string, string]()
proc chatMessages*(chatId: string) =
discard callPrivateRPC("chatMessages".prefix, %* [chatId, nil, 20])
proc delete*(self: ChatModel) =
discard
# TODO this probably belongs in another file
proc generateSymKeyFromPassword*(): string =
result = ($parseJson(callPrivateRPC("waku_generateSymKeyFromPassword", %* [
# TODO unhardcode this for non-status mailservers
"status-offline-inbox"
]))["result"]).strip(chars = {'"'})
proc hasChannel*(self: ChatModel, chatId: string): bool =
result = self.channels.contains(chatId)
proc requestMessages*(topics: seq[string], symKeyID: string, peer: string, numberOfMessages: int) =
discard callPrivateRPC("requestMessages".prefix, %* [
{
"topics": topics,
"mailServerPeer": peer,
"symKeyID": symKeyID,
"timeout": 30,
"limit": numberOfMessages,
"cursor": nil,
"from": times.toUnix(times.getTime()) - 30000 # Unhardcode this. Need to keep the last fetch in a DB
}
])
proc getActiveChannel*(self: ChatModel): string =
if (self.channels.len == 0): "" else: self.channels.toSeq[self.channels.len - 1]
proc join*(self: ChatModel, chatId: string, chatTypeInt: ChatType, isNewChat: bool = true) =
if self.hasChannel(chatId): return
proc sendChatMessage*(chatId: string, msg: string): string =
callPrivateRPC("sendChatMessage".prefix, %* [
{
"chatId": chatId,
"text": msg,
"responseTo": nil,
"ensName": nil,
"sticker": nil,
"contentType": 1
}
])
self.channels.incl chatId
let generatedSymKey = status_chat.generateSymKeyFromPassword()
# TODO get this from the connection or something
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
let oneToOne = isOneToOneChat(chatId)
if isNewChat: status_chat.saveChat(chatId, oneToOne)
let filterResult = status_chat.loadFilters(chatId = chatId, oneToOne = oneToOne)
status_chat.chatMessages(chatId)
let parsedResult = parseJson(filterResult)["result"]
var topics = newSeq[string](0)
for topicObj in parsedResult:
if (($topicObj["chatId"]).strip(chars = {'"'}) == chatId):
topics.add(($topicObj["topic"]).strip(chars = {'"'}))
if(not self.filters.hasKey(chatId)): self.filters[chatId] = topicObj["filterId"].getStr
if (topics.len == 0):
warn "No topic found for the chat. Cannot load past messages"
else:
status_chat.requestMessages(topics, generatedSymKey, peer, 20)
self.events.emit("channelJoined", ChannelArgs(channel: chatId, chatTypeInt: chatTypeInt))
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
proc load*(self: ChatModel) =
let chatList = status_chat.loadChats()
for chat in chatList:
# TODO: use correct type of chat instead of hardcoded 2 (assumes it's only public chats)
self.join(chat.id, ChatType.Public, false)
self.events.emit("chatsLoaded", ChatArgs(chats: chatList))
proc leave*(self: ChatModel, chatId: string) =
status_chat.removeFilters(chatId, self.filters[chatId])
status_chat.deactivateChat(chatId)
# TODO: REMOVE MAILSERVER TOPIC
# TODO: REMOVE HISTORY
self.filters.del(chatId)
self.channels.excl(chatId)
self.events.emit("channelLeft", ChannelArgs(channel: chatId))
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
proc sendMessage*(self: ChatModel, chatId: string, msg: string): string =
var sentMessage = status_chat.sendChatMessage(chatId, msg)
var parsedMessage = parseJson(sentMessage)["result"]["chats"][0]["lastMessage"]
self.events.emit("messageSent", MsgArgs(message: msg, chatId: chatId, payload: parsedMessage))
sentMessage

View File

@ -1,5 +1,5 @@
import ../../signals/types
import ../../status/accounts as status_accounts
import ../libstatus/accounts as status_accounts
type ChatItem* = ref object
id*: string

View File

@ -0,0 +1,161 @@
import libstatus
import core
import json
import utils
import accounts/constants
import nimcrypto
import os
import uuids
import types
import json_serialization
import chronicles
proc queryAccounts*(): string =
var response = callPrivateRPC("eth_accounts")
result = parseJson(response)["result"][0].getStr()
proc generateAddresses*(): seq[GeneratedAccount] =
let multiAccountConfig = %* {
"n": 5,
"mnemonicPhraseLength": 12,
"bip39Passphrase": "",
"paths": [PATH_WHISPER, PATH_WALLET_ROOT, PATH_DEFAULT_WALLET]
}
result = Json.decode($libstatus.multiAccountGenerateAndDeriveAddresses($multiAccountConfig), seq[GeneratedAccount])
proc generateAlias*(publicKey: string): string =
result = $libstatus.generateAlias(publicKey.toGoString)
proc generateIdenticon*(publicKey: string): string =
result = $libstatus.identicon(publicKey.toGoString)
proc ensureDir(dirname: string) =
if not existsDir(dirname):
# removeDir(dirname)
createDir(dirname)
proc initNodeAccounts*(): seq[NodeAccount] =
const datadir = "./data/"
const keystoredir = "./data/keystore/"
const nobackupdir = "./noBackup/"
ensureDir(datadir)
ensureDir(keystoredir)
ensureDir(nobackupdir)
discard $libstatus.initKeystore(keystoredir);
let strNodeAccounts = $libstatus.openAccounts(datadir);
result = Json.decode(strNodeAccounts, seq[NodeAccount])
proc saveAccountAndLogin*(
multiAccounts: MultiAccounts,
alias: string,
identicon: string,
accountData: string,
password: string,
configJSON: string,
settingsJSON: string): Account =
let hashedPassword = "0x" & $keccak_256.digest(password)
let subaccountData = %* [
{
"public-key": multiAccounts.defaultWallet.publicKey,
"address": multiAccounts.defaultWallet.address,
"color": "#4360df",
"wallet": true,
"path": constants.PATH_DEFAULT_WALLET,
"name": "Status account"
},
{
"public-key": multiAccounts.whisper.publicKey,
"address": multiAccounts.whisper.address,
"name": alias,
"photo-path": identicon,
"path": constants.PATH_WHISPER,
"chat": true
}
]
var savedResult = $libstatus.saveAccountAndLogin(accountData, hashedPassword, settingsJSON, configJSON, $subaccountData)
let parsedSavedResult = savedResult.parseJson
let error = parsedSavedResult["error"].getStr
if error == "":
debug "Account saved succesfully"
result = Account(name: alias, photoPath: identicon)
return
raise newException(LoginError, "Error saving account and logging in: " & error)
proc generateMultiAccounts*(account: GeneratedAccount, password: string): MultiAccounts =
let hashedPassword = "0x" & $keccak_256.digest(password)
let multiAccount = %* {
"accountID": account.id,
"paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET],
"password": hashedPassword
}
var response = $libstatus.multiAccountStoreDerivedAccounts($multiAccount);
result = Json.decode($response, MultiAccounts)
proc getAccountData*(account: GeneratedAccount, alias: string, identicon: string): JsonNode =
result = %* {
"name": alias,
"address": account.address,
"photo-path": identicon,
"key-uid": account.keyUid,
"keycard-pairing": nil
}
proc getAccountSettings*(account: GeneratedAccount, alias: string, identicon: string, multiAccounts: MultiAccounts, defaultNetworks: JsonNode): JsonNode =
result = %* {
"key-uid": account.keyUid,
"mnemonic": account.mnemonic,
"public-key": multiAccounts.whisper.publicKey,
"name": alias,
"address": account.address,
"eip1581-address": multiAccounts.eip1581.address,
"dapps-address": multiAccounts.defaultWallet.address,
"wallet-root-address": multiAccounts.walletRoot.address,
"preview-privacy?": true,
"signing-phrase": generateSigningPhrase(3),
"log-level": "INFO",
"latest-derived-path": 0,
"networks/networks": defaultNetworks,
"currency": "usd",
"photo-path": identicon,
"waku-enabled": true,
"wallet/visible-tokens": {
"mainnet": ["SNT"]
},
"appearance": 0,
"networks/current-network": "mainnet_rpc",
"installation-id": $genUUID()
}
proc setupAccount*(account: GeneratedAccount, password: string): Account =
let multiAccounts = generateMultiAccounts(account, password)
let whisperPubKey = account.derived.whisper.publicKey
let alias = generateAlias(whisperPubKey)
let identicon =generateIdenticon(whisperPubKey)
let accountData = getAccountData(account, alias, identicon)
var settingsJSON = getAccountSettings(account, alias, identicon, multiAccounts, constants.DEFAULT_NETWORKS)
result = saveAccountAndLogin(multiAccounts, alias, identicon, $accountData, password, $constants.NODE_CONFIG, $settingsJSON)
# TODO this is needed for now for the retrieving of past messages. We'll either move or remove it later
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
discard libstatus.addPeer(peer)
proc login*(nodeAccount: NodeAccount, password: string): NodeAccount =
let hashedPassword = "0x" & $keccak_256.digest(password)
let account = nodeAccount.toAccount
let loginResult = $libstatus.login($toJson(account), hashedPassword)
let error = parseJson(loginResult)["error"].getStr
if error == "":
debug "Login requested", user=nodeAccount.name
result = nodeAccount
return
raise newException(LoginError, "Error logging in: " & error)

View File

@ -0,0 +1,106 @@
import core
import json
import utils
import times
import strutils
import chronicles
import ../../signals/types
import ../../signals/messages
proc loadFilters*(chatId: string, filterId: string = "", symKeyId: string = "", oneToOne: bool = false, identity: string = "", topic: string = "", discovery: bool = false, negotiated: bool = false, listen: bool = true): string =
result = callPrivateRPC("loadFilters".prefix, %* [
[{
"ChatID": chatId, # identifier of the chat
"FilterID": filterId, # whisper filter id generated
"SymKeyID": symKeyId, # symmetric key id used for symmetric filters
"OneToOne": oneToOne, # if asymmetric encryption is used for this chat
"Identity": identity, # public key of the other recipient for non-public filters.
# FIXME: passing empty string to the topic makes it error
# "Topic": topic, # whisper topic
"Discovery": discovery,
"Negotiated": negotiated,
"Listen": listen # whether we are actually listening for messages on this chat, or the filter is only created in order to be able to post on the topic
}]
])
proc removeFilters*(chatId: string, filterId: string) =
discard callPrivateRPC("removeFilters".prefix, %* [
[{
"ChatID": chatId,
"FilterID": filterId
}]
])
proc saveChat*(chatId: string, oneToOne: bool = false, active: bool = true) =
discard callPrivateRPC("saveChat".prefix, %* [
{
"lastClockValue": 0, # TODO:
"color": "#51d0f0", # TODO:
"name": chatId,
"lastMessage": nil, # TODO:
"active": active,
"id": chatId,
"unviewedMessagesCount": 0, # TODO:
# TODO use constants for those too or use the Date
"chatType": if oneToOne: 1 else: 2, # TODO: use constants
"timestamp": 1588940692659 # TODO:
}
])
proc deactivateChat*(chatId: string) =
discard callPrivateRPC("saveChat".prefix, %* [
{
"lastClockValue": 0,
"color": "",
"name": chatId,
"lastMessage": nil,
"active": false,
"id": chatId,
"unviewedMessagesCount": 0,
"timestamp": 0
}
])
proc loadChats*(): seq[Chat] =
result = @[]
let jsonResponse = parseJson($callPrivateRPC("chats".prefix))
if jsonResponse["result"].kind != JNull:
for jsonChat in jsonResponse{"result"}:
let chat = jsonChat.toChat
if chat.active: result.add(jsonChat.toChat)
proc chatMessages*(chatId: string) =
discard callPrivateRPC("chatMessages".prefix, %* [chatId, nil, 20])
# TODO this probably belongs in another file
proc generateSymKeyFromPassword*(): string =
result = ($parseJson(callPrivateRPC("waku_generateSymKeyFromPassword", %* [
# TODO unhardcode this for non-status mailservers
"status-offline-inbox"
]))["result"]).strip(chars = {'"'})
proc requestMessages*(topics: seq[string], symKeyID: string, peer: string, numberOfMessages: int) =
discard callPrivateRPC("requestMessages".prefix, %* [
{
"topics": topics,
"mailServerPeer": peer,
"symKeyID": symKeyID,
"timeout": 30,
"limit": numberOfMessages,
"cursor": nil,
"from": times.toUnix(times.getTime()) - 30000 # Unhardcode this. Need to keep the last fetch in a DB
}
])
proc sendChatMessage*(chatId: string, msg: string): string =
callPrivateRPC("sendChatMessage".prefix, %* [
{
"chatId": chatId,
"text": msg,
"responseTo": nil,
"ensName": nil,
"sticker": nil,
"contentType": 1
}
])

View File

@ -0,0 +1,43 @@
import core as status
import json
# import utils
import httpclient, json
import strformat
import stint
import strutils
proc getAccounts*(): seq[string] =
var response = callPrivateRPC("eth_accounts")
result = parseJson(response)["result"].to(seq[string])
proc getAccount*(): string =
var accounts = getAccounts()
result = accounts[0]
proc sendTransaction*(from_address: string, to: string, value: string, password: string): string =
var args = %* {
"value": fmt"0x{toHex(value)}",
"from": from_address,
"to": to
}
var response = status.sendTransaction($args, password)
result = response
proc getPrice*(crypto: string, fiat: string): string =
var url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
let client = newHttpClient()
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
let response = client.request(url)
$parseJson(response.body)["USD"]
proc getBalance*(address: string): string =
let payload = %* [address, "latest"]
parseJson(status.callPrivateRPC("eth_getBalance", payload))["result"].str
proc hex2Eth*(input: string): string =
var value = fromHex(Stuint[256], input)
var one_eth = fromHex(Stuint[256], "DE0B6B3A7640000")
var (eth, remainder) = divmod(value, one_eth)
fmt"{eth}.{remainder}"

View File

@ -2,7 +2,7 @@ import eventemitter
# import json
# import strformat
# import strutils
import "../status/core" as status
import libstatus/core as status
type NodeModel* = ref object
events*: EventEmitter

View File

@ -1,5 +1,5 @@
import eventemitter
import ../status/types
import libstatus/types
type
MailServer* = ref object

View File

@ -1,43 +1,40 @@
import core as status
import eventemitter
import json
# import utils
import httpclient, json
import strformat
import stint
import strutils
import libstatus/wallet as status_wallet
proc getAccounts*(): seq[string] =
var response = callPrivateRPC("eth_accounts")
result = parseJson(response)["result"].to(seq[string])
type Asset* = ref object
name*, symbol*, value*, fiatValue*, image*: string
proc getAccount*(): string =
var accounts = getAccounts()
result = accounts[0]
type WalletModel* = ref object
events*: EventEmitter
proc sendTransaction*(from_address: string, to: string, value: string, password: string): string =
var args = %* {
"value": fmt"0x{toHex(value)}",
"from": from_address,
"to": to
}
var response = status.sendTransaction($args, password)
result = response
proc newWalletModel*(): WalletModel =
result = WalletModel()
result.events = createEventEmitter()
proc getPrice*(crypto: string, fiat: string): string =
var url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
let client = newHttpClient()
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
proc delete*(self: WalletModel) =
discard
let response = client.request(url)
$parseJson(response.body)["USD"]
proc sendTransaction*(self: WalletModel, from_value: string, to: string, value: string, password: string): string =
status_wallet.sendTransaction(from_value, to, value, password)
proc getBalance*(address: string): string =
let payload = %* [address, "latest"]
parseJson(status.callPrivateRPC("eth_getBalance", payload))["result"].str
proc getEthBalance*(self: WalletModel, address: string): string =
var balance = status_wallet.getBalance(address)
echo(fmt"balance in hex: {balance}")
proc hex2Eth*(input: string): string =
var value = fromHex(Stuint[256], input)
var one_eth = fromHex(Stuint[256], "DE0B6B3A7640000")
# 2. convert balance to eth
var eth_value = status_wallet.hex2Eth(balance)
echo(fmt"balance in eth: {eth_value}")
eth_value
var (eth, remainder) = divmod(value, one_eth)
fmt"{eth}.{remainder}"
proc getFiatValue*(self: WalletModel, eth_balance: string, symbol: string, fiat_symbol: string): float =
# 3. get usd price of 1 eth
var usd_eth_price = status_wallet.getPrice("ETH", "USD")
echo(fmt"usd_price: {usd_eth_price}")
# 4. convert balance to usd
var usd_balance = parseFloat(eth_balance) * parseFloat(usd_eth_price)
echo(fmt"balance in usd: {usd_balance}")
usd_balance