261 lines
11 KiB
Clojure
261 lines
11 KiB
Clojure
(ns status-im.network.core
|
|
(:require [clojure.string :as string]
|
|
[re-frame.core :as re-frame]
|
|
[status-im.accounts.update.core :as accounts.update]
|
|
[status-im.i18n :as i18n]
|
|
[status-im.native-module.core :as status]
|
|
[status-im.network.net-info :as net-info]
|
|
[status-im.mailserver.core :as mailserver]
|
|
[status-im.utils.ethereum.core :as ethereum]
|
|
[status-im.ui.screens.navigation :as navigation]
|
|
[status-im.fleet.core :as fleet-core]
|
|
[status-im.utils.fx :as fx]
|
|
[status-im.utils.handlers :as handlers]
|
|
[status-im.utils.http :as http]
|
|
[status-im.utils.types :as types]))
|
|
|
|
(def url-regex
|
|
#"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)")
|
|
|
|
(defn valid-rpc-url? [url]
|
|
(boolean (re-matches url-regex (str url))))
|
|
|
|
(def default-manage
|
|
{:name {:value ""}
|
|
:url {:value ""}
|
|
:chain {:value :mainnet}})
|
|
|
|
(defn validate-string [{:keys [value]}]
|
|
{:value value
|
|
:error (string/blank? value)})
|
|
|
|
(defn validate-network-id [{:keys [value]}]
|
|
{:value value
|
|
:error (and (not (string/blank? value))
|
|
(= (int value) 0))})
|
|
|
|
(defn validate-url [{:keys [value]}]
|
|
{:value value
|
|
:error (not (valid-rpc-url? value))})
|
|
|
|
(defn validate-manage [manage]
|
|
(-> manage
|
|
(update :url validate-url)
|
|
(update :name validate-string)
|
|
(update :chain validate-string)
|
|
(update :network-id validate-network-id)))
|
|
|
|
(defn valid-manage? [manage]
|
|
(->> (validate-manage manage)
|
|
vals
|
|
(map :error)
|
|
(not-any? identity)))
|
|
|
|
(defn new-network [random-id network-name upstream-url type network-id]
|
|
(let [data-dir (str "/ethereum/" (name type) "_rpc")
|
|
config {:NetworkId (or (when network-id (int network-id))
|
|
(ethereum/chain-keyword->chain-id type))
|
|
:DataDir data-dir
|
|
:UpstreamConfig {:Enabled true
|
|
:URL upstream-url}}]
|
|
{:id (string/replace random-id "-" "")
|
|
:name network-name
|
|
:config config}))
|
|
|
|
(defn get-chain [{:keys [db]}]
|
|
(let [network (get (:networks (:account/account db)) (:network db))]
|
|
(ethereum/network->chain-keyword network)))
|
|
|
|
(fx/defn set-input
|
|
[{:keys [db]} input-key value]
|
|
{:db (-> db
|
|
(update-in [:networks/manage input-key] assoc :value value)
|
|
(update-in [:networks/manage] validate-manage))})
|
|
|
|
(defn- action-handler
|
|
([handler]
|
|
(action-handler handler nil nil))
|
|
([handler data cofx]
|
|
(when handler
|
|
(handler data cofx))))
|
|
|
|
(fx/defn save
|
|
[{{:network/keys [manage] :account/keys [account] :as db} :db
|
|
random-id-generator :random-id-generator :as cofx}
|
|
{:keys [data success-event on-success on-failure]}]
|
|
(let [data (or data manage)]
|
|
(if (valid-manage? data)
|
|
(let [{:keys [name url chain network-id]} data
|
|
network (new-network (random-id-generator)
|
|
(:value name)
|
|
(:value url)
|
|
(:value chain)
|
|
(:value network-id))
|
|
new-networks (merge {(:id network) network} (:networks account))]
|
|
(fx/merge cofx
|
|
{:db (dissoc db :networks/manage)}
|
|
#(action-handler on-success (:id network) %)
|
|
(accounts.update/account-update
|
|
{:networks new-networks}
|
|
{:success-event success-event})))
|
|
(action-handler on-failure))))
|
|
|
|
;; No edit functionality actually implemented
|
|
(fx/defn edit
|
|
[{db :db}]
|
|
{:db (assoc db :networks/manage (validate-manage default-manage))
|
|
:dispatch [:navigate-to :edit-network]})
|
|
|
|
(fx/defn connect-success [{:keys [db now] :as cofx} {:keys [network-id on-success client-version]}]
|
|
(let [current-network (get-in db [:account/account :networks (:network db)])]
|
|
(if (ethereum/network-with-upstream-rpc? current-network)
|
|
(fx/merge cofx
|
|
#(action-handler on-success {:network-id network-id :client-version client-version} %)
|
|
(accounts.update/account-update
|
|
{:network network-id
|
|
:last-updated now}
|
|
{:success-event [:accounts.update.callback/save-settings-success]}))
|
|
(fx/merge cofx
|
|
{:ui/show-confirmation {:title (i18n/label :t/close-app-title)
|
|
:content (i18n/label :t/close-app-content)
|
|
:confirm-button-text (i18n/label :t/close-app-button)
|
|
:on-accept #(re-frame/dispatch [:network.ui/save-non-rpc-network-pressed network-id])
|
|
:on-cancel nil}}
|
|
#(action-handler on-success {:network-id network-id :client-version client-version} %)))))
|
|
|
|
(defn connect-failure [{:keys [network-id on-failure reason]}]
|
|
(action-handler on-failure
|
|
{:network-id network-id :reason reason}
|
|
nil))
|
|
|
|
(fx/defn connect [{:keys [db] :as cofx} {:keys [network-id on-success on-failure]}]
|
|
(if-let [config (get-in db [:account/account :networks network-id :config])]
|
|
(if-let [upstream-url (get-in config [:UpstreamConfig :URL])]
|
|
{:http-post {:url upstream-url
|
|
:data (types/clj->json {:jsonrpc "2.0"
|
|
:method "web3_clientVersion"
|
|
:id 1})
|
|
:opts {:headers {"Content-Type" "application/json"}}
|
|
:success-event-creator (fn [{:keys [response-body]}]
|
|
(if-let [client-version (:result (http/parse-payload response-body))]
|
|
[::connect-success {:network-id network-id
|
|
:on-success on-success
|
|
:client-version client-version}]
|
|
[::connect-failure {:network-id network-id
|
|
:on-failure on-failure
|
|
:reason (i18n/label :t/network-invalid-url)}]))
|
|
:failure-event-creator (fn [{:keys [response-body status-code]}]
|
|
(let [reason (if status-code
|
|
(i18n/label :t/network-invalid-status-code {:code status-code})
|
|
(str response-body))]
|
|
[::connect-failure {:network-id network-id
|
|
:on-failure on-failure
|
|
:reason reason}]))}}
|
|
(connect-success cofx {:network-id network-id
|
|
:on-success on-success
|
|
:client-version ""}))
|
|
(connect-failure {:network-id network-id
|
|
:on-failure on-failure
|
|
:reason "A network with the specified id doesn't exist"})))
|
|
|
|
(handlers/register-handler-fx
|
|
::connect-success
|
|
(fn [cofx [_ data]]
|
|
(connect-success cofx data)))
|
|
|
|
(handlers/register-handler-fx
|
|
::connect-failure
|
|
(fn [_ [_ data]]
|
|
(connect-failure data)))
|
|
|
|
(fx/defn delete
|
|
[{{:account/keys [account]} :db :as cofx} {:keys [network on-success on-failure]}]
|
|
(let [current-network? (= (:network account) network)]
|
|
(if (or current-network?
|
|
(not (get-in account [:networks network])))
|
|
(fx/merge cofx
|
|
{:ui/show-error (i18n/label :t/delete-network-error)}
|
|
#(action-handler on-failure network %))
|
|
(fx/merge cofx
|
|
{:ui/show-confirmation {:title (i18n/label :t/delete-network-title)
|
|
:content (i18n/label :t/delete-network-confirmation)
|
|
:confirm-button-text (i18n/label :t/delete)
|
|
:on-accept #(re-frame/dispatch [:network.ui/remove-network-confirmed network])
|
|
:on-cancel nil}}
|
|
#(action-handler on-success network %)))))
|
|
|
|
(fx/defn save-non-rpc-network
|
|
[{:keys [db now] :as cofx} network]
|
|
(accounts.update/account-update cofx
|
|
{:network network
|
|
:last-updated now}
|
|
{:success-event [:network.callback/non-rpc-network-saved]}))
|
|
|
|
(fx/defn remove-network
|
|
[{:keys [db now] :as cofx} network]
|
|
(let [networks (dissoc (get-in db [:account/account :networks]) network)]
|
|
(accounts.update/account-update cofx
|
|
{:networks networks
|
|
:last-updated now}
|
|
{:success-event [:navigate-back]})))
|
|
|
|
(fx/defn save-network
|
|
[cofx]
|
|
(save cofx
|
|
{:data (get-in cofx [:db :networks/manage])
|
|
:success-event [:navigate-back]}))
|
|
|
|
(fx/defn handle-connection-status-change
|
|
[{:keys [db] :as cofx} is-connected?]
|
|
(fx/merge cofx
|
|
{:db (assoc db :network-status (if is-connected? :online :offline))}
|
|
(mailserver/network-connection-status-changed is-connected?)))
|
|
|
|
(defn- navigate-to-network-details
|
|
[cofx network show-warning?]
|
|
(fx/merge cofx
|
|
(when show-warning?
|
|
{:utils/show-popup {:title "LES support is experimental!"
|
|
:content "Use at your own risk!"}})
|
|
(navigation/navigate-to-cofx :network-details {:networks/selected-network network})))
|
|
|
|
(defn- not-supported-warning [fleet]
|
|
(str (name fleet) " does not support LES!\n"
|
|
"Please, select one of the supported fleets:"
|
|
(map name fleet-core/fleets-with-les)))
|
|
|
|
(fx/defn open-network-details
|
|
[cofx network]
|
|
(let [db (:db cofx)
|
|
rpc-network? (get-in network [:config :UpstreamConfig :Enabled] false)
|
|
fleet (fleet-core/current-fleet db nil)
|
|
fleet-supports-les? (fleet-core/fleet-supports-les? fleet)]
|
|
(if (or rpc-network? fleet-supports-les?)
|
|
(navigate-to-network-details cofx network (not rpc-network?))
|
|
;; Otherwise, we show an explanation dialog to a user if the current fleet does not suport LES
|
|
{:utils/show-popup {:title "LES not supported"
|
|
:content (not-supported-warning fleet)}})))
|
|
|
|
(fx/defn handle-network-status-change
|
|
[cofx data]
|
|
{:network/notify-status-go data})
|
|
|
|
(re-frame/reg-fx
|
|
:network/listen-to-network-status
|
|
(fn []
|
|
(let [callback-event #(re-frame/dispatch [:network/network-status-changed %])]
|
|
(net-info/net-info callback-event)
|
|
(net-info/add-net-info-listener callback-event))))
|
|
|
|
(re-frame/reg-fx
|
|
:network/listen-to-connection-status
|
|
(fn []
|
|
(let [callback-event #(re-frame/dispatch [:network/connection-status-changed %])]
|
|
(net-info/is-connected? callback-event)
|
|
(net-info/add-connection-listener callback-event))))
|
|
|
|
(re-frame/reg-fx
|
|
:network/notify-status-go
|
|
(fn [data]
|
|
(status/connection-change data)))
|