Add custom bootnodes

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-05-23 10:41:59 +02:00
parent 7711dc51ad
commit 33ad919508
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
34 changed files with 605 additions and 46 deletions

View File

@ -19,3 +19,4 @@ INSTABUG_SURVEYS=1
GROUP_CHATS_ENABLED=0 GROUP_CHATS_ENABLED=0
FORCE_SENT_RECEIVED_TRACKING=1 FORCE_SENT_RECEIVED_TRACKING=1
ADD_CUSTOM_MAILSERVERS_ENABLED=0 ADD_CUSTOM_MAILSERVERS_ENABLED=0
BOOTNODES_SETTINGS_ENABLED=0

View File

@ -20,3 +20,4 @@ GROUP_CHATS_ENABLED=0
FORCE_SENT_RECEIVED_TRACKING=0 FORCE_SENT_RECEIVED_TRACKING=0
USE_SYM_KEY=0 USE_SYM_KEY=0
ADD_CUSTOM_MAILSERVERS_ENABLED=0 ADD_CUSTOM_MAILSERVERS_ENABLED=0
BOOTNODES_SETTINGS_ENABLED=0

View File

@ -17,7 +17,7 @@ dependencies {
implementation 'com.github.ericwlange:AndroidJSCore:3.0.1' implementation 'com.github.ericwlange:AndroidJSCore:3.0.1'
implementation 'status-im:function:0.0.1' implementation 'status-im:function:0.0.1'
String statusGoVersion = 'develop-g5aae87ab' String statusGoVersion = 'develop-gbc14e6fa'
final String statusGoGroup = 'status-im', statusGoName = 'status-go' final String statusGoGroup = 'status-im', statusGoName = 'status-go'
// Check if the local status-go jar exists, and compile against that if it does // Check if the local status-go jar exists, and compile against that if it does

View File

@ -183,6 +183,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
} }
int testnetNetworkId = 3;
String testnetDataDir = root + "/ethereum/testnet"; String testnetDataDir = root + "/ethereum/testnet";
String oldKeystoreDir = testnetDataDir + "/keystore"; String oldKeystoreDir = testnetDataDir + "/keystore";
String newKeystoreDir = root + "/keystore"; String newKeystoreDir = root + "/keystore";
@ -204,14 +206,19 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
} }
String config = Statusgo.GenerateConfig(testnetDataDir, 3); String config;
try { try {
JSONObject customConfig = new JSONObject(defaultConfig); JSONObject customConfig = new JSONObject(defaultConfig);
String dataDir = root + customConfig.get("DataDir");
config = Statusgo.GenerateConfig(dataDir, customConfig.getInt("NetworkId"));
JSONObject jsonConfig = new JSONObject(config); JSONObject jsonConfig = new JSONObject(config);
String gethLogFilePath = prepareLogsFile(); String gethLogFilePath = prepareLogsFile();
boolean logsEnabled = (gethLogFilePath != null) && !TextUtils.isEmpty(this.logLevel); boolean logsEnabled = (gethLogFilePath != null) && !TextUtils.isEmpty(this.logLevel);
String dataDir = root + customConfig.get("DataDir");
jsonConfig.put("LogEnabled", (gethLogFilePath != null && logsEnabled)); jsonConfig.put("LogEnabled", (gethLogFilePath != null && logsEnabled));
jsonConfig.put("LogFile", gethLogFilePath); jsonConfig.put("LogFile", gethLogFilePath);
jsonConfig.put("LogLevel", TextUtils.isEmpty(this.logLevel) ? "ERROR" : this.logLevel.toUpperCase()); jsonConfig.put("LogLevel", TextUtils.isEmpty(this.logLevel) ? "ERROR" : this.logLevel.toUpperCase());
@ -236,10 +243,22 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} catch (Exception e) { } catch (Exception e) {
} }
try {
Object clusterConfig = customConfig.get("ClusterConfig");
if (clusterConfig != null) {
Log.d(TAG, "ClusterConfig is not null");
jsonConfig.put("ClusterConfig", clusterConfig);
}
} catch (Exception e) {
Log.w(TAG, "Something went wrong parsing cluster config" + e.getMessage());
}
jsonConfig.put("KeyStoreDir", newKeystoreDir); jsonConfig.put("KeyStoreDir", newKeystoreDir);
config = jsonConfig.toString(); config = jsonConfig.toString();
} catch (JSONException e) { } catch (JSONException e) {
config = Statusgo.GenerateConfig(testnetDataDir, testnetNetworkId);
Log.d(TAG, "Something went wrong " + e.getMessage()); Log.d(TAG, "Something went wrong " + e.getMessage());
Log.d(TAG, "Default configuration will be used"); Log.d(TAG, "Default configuration will be used");
} }

View File

@ -15,7 +15,7 @@
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self
options:(NSJSONWritingOptions) (prettyPrint ? NSJSONWritingPrettyPrinted : 0) options:(NSJSONWritingOptions) (prettyPrint ? NSJSONWritingPrettyPrinted : 0)
error:&error]; error:&error];
if (! jsonData) { if (! jsonData) {
NSLog(@"bv_jsonStringWithPrettyPrint: error: %@", error.localizedDescription); NSLog(@"bv_jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
return @"{}"; return @"{}";
@ -35,7 +35,7 @@
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self
options:(NSJSONWritingOptions) (prettyPrint ? NSJSONWritingPrettyPrinted : 0) options:(NSJSONWritingOptions) (prettyPrint ? NSJSONWritingPrettyPrinted : 0)
error:&error]; error:&error];
if (! jsonData) { if (! jsonData) {
NSLog(@"bv_jsonStringWithPrettyPrint: error: %@", error.localizedDescription); NSLog(@"bv_jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
return @"[]"; return @"[]";
@ -90,7 +90,7 @@ RCT_EXPORT_METHOD(parseJail:(NSString *)chatId
} }
NSDictionary *result = [_jail parseJail:chatId withCode:js]; NSDictionary *result = [_jail parseJail:chatId withCode:js];
stringResult = [result bv_jsonStringWithPrettyPrint:NO]; stringResult = [result bv_jsonStringWithPrettyPrint:NO];
callback(@[stringResult]); callback(@[stringResult]);
} }
@ -103,7 +103,7 @@ RCT_EXPORT_METHOD(callJail:(NSString *)chatId
NSLog(@"CallJail() method called"); NSLog(@"CallJail() method called");
#endif #endif
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *stringResult; NSString *stringResult;
if(_jail == nil) { if(_jail == nil) {
_jail = [Jail new]; _jail = [Jail new];
@ -130,12 +130,12 @@ RCT_EXPORT_METHOD(startNode:(NSString *)configString) {
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]
lastObject]; lastObject];
NSURL *testnetFolderName = [rootUrl URLByAppendingPathComponent:@"ethereum/testnet"]; NSURL *testnetFolderName = [rootUrl URLByAppendingPathComponent:@"ethereum/testnet"];
if (![fileManager fileExistsAtPath:testnetFolderName.path]) if (![fileManager fileExistsAtPath:testnetFolderName.path])
[fileManager createDirectoryAtPath:testnetFolderName.path withIntermediateDirectories:YES attributes:nil error:&error]; [fileManager createDirectoryAtPath:testnetFolderName.path withIntermediateDirectories:YES attributes:nil error:&error];
NSURL *flagFolderUrl = [rootUrl URLByAppendingPathComponent:@"ropsten_flag"]; NSURL *flagFolderUrl = [rootUrl URLByAppendingPathComponent:@"ropsten_flag"];
if(![fileManager fileExistsAtPath:flagFolderUrl.path]){ if(![fileManager fileExistsAtPath:flagFolderUrl.path]){
NSLog(@"remove lightchaindata"); NSLog(@"remove lightchaindata");
NSURL *lightChainData = [testnetFolderName URLByAppendingPathComponent:@"StatusIM/lightchaindata"]; NSURL *lightChainData = [testnetFolderName URLByAppendingPathComponent:@"StatusIM/lightchaindata"];
@ -148,9 +148,9 @@ RCT_EXPORT_METHOD(startNode:(NSString *)configString) {
attributes:nil attributes:nil
error:&error]; error:&error];
} }
NSLog(@"after remove lightchaindata"); NSLog(@"after remove lightchaindata");
NSURL *oldKeystoreUrl = [testnetFolderName URLByAppendingPathComponent:@"keystore"]; NSURL *oldKeystoreUrl = [testnetFolderName URLByAppendingPathComponent:@"keystore"];
NSURL *newKeystoreUrl = [rootUrl URLByAppendingPathComponent:@"keystore"]; NSURL *newKeystoreUrl = [rootUrl URLByAppendingPathComponent:@"keystore"];
if([fileManager fileExistsAtPath:oldKeystoreUrl.path]){ if([fileManager fileExistsAtPath:oldKeystoreUrl.path]){
@ -158,15 +158,16 @@ RCT_EXPORT_METHOD(startNode:(NSString *)configString) {
[fileManager copyItemAtPath:oldKeystoreUrl.path toPath:newKeystoreUrl.path error:nil]; [fileManager copyItemAtPath:oldKeystoreUrl.path toPath:newKeystoreUrl.path error:nil];
[fileManager removeItemAtPath:oldKeystoreUrl.path error:nil]; [fileManager removeItemAtPath:oldKeystoreUrl.path error:nil];
} }
NSLog(@"after lightChainData"); NSLog(@"after lightChainData");
NSLog(@"preconfig: %@", configString); NSLog(@"preconfig: %@", configString);
NSData *configData = [configString dataUsingEncoding:NSUTF8StringEncoding]; NSData *configData = [configString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *configJSON = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil]; NSDictionary *configJSON = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
int networkId = [configJSON[@"NetworkId"] integerValue]; int networkId = [configJSON[@"NetworkId"] integerValue];
NSString *dataDir = [configJSON objectForKey:@"DataDir"]; NSString *dataDir = [configJSON objectForKey:@"DataDir"];
NSString *upstreamURL = [configJSON valueForKeyPath:@"UpstreamConfig.URL"]; NSString *upstreamURL = [configJSON valueForKeyPath:@"UpstreamConfig.URL"];
NSArray *bootnodes = [configJSON valueForKeyPath:@"ClusterConfig.BootNodes"];
NSString *networkDir = [rootUrl.path stringByAppendingString:dataDir]; NSString *networkDir = [rootUrl.path stringByAppendingString:dataDir];
NSString *devCluster = [ReactNativeConfig envFor:@"ETHEREUM_DEV_CLUSTER"]; NSString *devCluster = [ReactNativeConfig envFor:@"ETHEREUM_DEV_CLUSTER"];
NSString *logLevel = [[ReactNativeConfig envFor:@"LOG_LEVEL_STATUS_GO"] uppercaseString]; NSString *logLevel = [[ReactNativeConfig envFor:@"LOG_LEVEL_STATUS_GO"] uppercaseString];
@ -180,19 +181,27 @@ RCT_EXPORT_METHOD(startNode:(NSString *)configString) {
[resultingConfigJson setValue:[NSNumber numberWithBool:[logLevel length] != 0] forKey:@"LogEnabled"]; [resultingConfigJson setValue:[NSNumber numberWithBool:[logLevel length] != 0] forKey:@"LogEnabled"];
[resultingConfigJson setValue:logUrl.path forKey:@"LogFile"]; [resultingConfigJson setValue:logUrl.path forKey:@"LogFile"];
[resultingConfigJson setValue:([logLevel length] == 0 ? [NSString stringWithUTF8String: "ERROR"] : logLevel) forKey:@"LogLevel"]; [resultingConfigJson setValue:([logLevel length] == 0 ? [NSString stringWithUTF8String: "ERROR"] : logLevel) forKey:@"LogLevel"];
[resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKeyPath:@"WhisperConfig.LightClient"]; [resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKeyPath:@"WhisperConfig.LightClient"];
if(upstreamURL != nil) { if(upstreamURL != nil) {
[resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKeyPath:@"UpstreamConfig.Enabled"]; [resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKeyPath:@"UpstreamConfig.Enabled"];
[resultingConfigJson setValue:upstreamURL forKeyPath:@"UpstreamConfig.URL"]; [resultingConfigJson setValue:upstreamURL forKeyPath:@"UpstreamConfig.URL"];
} }
if(bootnodes != nil) {
[resultingConfigJson setValue:[NSNumber numberWithBool:YES] forKeyPath:@"ClusterConfig.Enabled"];
[resultingConfigJson setValue:bootnodes forKeyPath:@"ClusterConfig.BootNodes"];
}
NSString *resultingConfig = [resultingConfigJson bv_jsonStringWithPrettyPrint:NO]; NSString *resultingConfig = [resultingConfigJson bv_jsonStringWithPrettyPrint:NO];
NSLog(@"node config %@", resultingConfig); NSLog(@"node config %@", resultingConfig);
if(![fileManager fileExistsAtPath:networkDirUrl.path]) { if(![fileManager fileExistsAtPath:networkDirUrl.path]) {
[fileManager createDirectoryAtPath:networkDirUrl.path withIntermediateDirectories:YES attributes:nil error:nil]; [fileManager createDirectoryAtPath:networkDirUrl.path withIntermediateDirectories:YES attributes:nil error:nil];
} }
NSLog(@"logUrlPath %@", logUrl.path); NSLog(@"logUrlPath %@", logUrl.path);
if(![fileManager fileExistsAtPath:logUrl.path]) { if(![fileManager fileExistsAtPath:logUrl.path]) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
@ -353,7 +362,7 @@ RCT_EXPORT_METHOD(clearCookies) {
RCT_EXPORT_METHOD(clearStorageAPIs) { RCT_EXPORT_METHOD(clearStorageAPIs) {
[[NSURLCache sharedURLCache] removeAllCachedResponses]; [[NSURLCache sharedURLCache] removeAllCachedResponses];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil]; NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
for (NSString *string in array) { for (NSString *string in array) {
@ -408,7 +417,7 @@ RCT_EXPORT_METHOD(getDeviceUUID:(RCTResponseSenderBlock)callback) {
NSLog(@"getDeviceUUID() method called"); NSLog(@"getDeviceUUID() method called");
#endif #endif
NSString* Identifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; NSString* Identifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
callback(@[Identifier]); callback(@[Identifier]);
} }
@ -420,7 +429,7 @@ RCT_EXPORT_METHOD(getDeviceUUID:(RCTResponseSenderBlock)callback) {
#endif #endif
return; return;
} }
NSString *sig = [NSString stringWithUTF8String:signal]; NSString *sig = [NSString stringWithUTF8String:signal];
#if DEBUG #if DEBUG
NSLog(@"SignalEvent"); NSLog(@"SignalEvent");
@ -428,7 +437,7 @@ RCT_EXPORT_METHOD(getDeviceUUID:(RCTResponseSenderBlock)callback) {
#endif #endif
[bridge.eventDispatcher sendAppEventWithName:@"gethEvent" [bridge.eventDispatcher sendAppEventWithName:@"gethEvent"
body:@{@"jsonEvent": sig}]; body:@{@"jsonEvent": sig}];
return; return;
} }
@ -447,7 +456,7 @@ RCT_EXPORT_METHOD(getDeviceUUID:(RCTResponseSenderBlock)callback) {
#endif #endif
[bridge.eventDispatcher sendAppEventWithName:@"gethEvent" [bridge.eventDispatcher sendAppEventWithName:@"gethEvent"
body:@{@"jsonEvent": signal}]; body:@{@"jsonEvent": signal}];
return; return;
} }

View File

@ -25,7 +25,7 @@
<artifactItem> <artifactItem>
<groupId>status-im</groupId> <groupId>status-im</groupId>
<artifactId>status-go-ios-simulator</artifactId> <artifactId>status-go-ios-simulator</artifactId>
<version>develop-g5aae87ab</version> <version>develop-gbc14e6fa</version>
<type>zip</type> <type>zip</type>
<overWrite>true</overWrite> <overWrite>true</overWrite>
<outputDirectory>./</outputDirectory> <outputDirectory>./</outputDirectory>

View File

@ -10,9 +10,22 @@
(core/single-clj :account) (core/single-clj :account)
(update :settings core/deserialize))) (update :settings core/deserialize)))
(defn- deserialize-bootnodes [bootnodes]
(reduce-kv
(fn [acc id {:keys [chain] :as bootnode}]
(assoc-in acc [chain id] bootnode))
{}
bootnodes))
(defn- serialize-bootnodes [bootnodes]
(->> bootnodes
vals
(mapcat vals)))
(defn- deserialize-account [account] (defn- deserialize-account [account]
(-> account (-> account
(update :settings core/deserialize) (update :settings core/deserialize)
(update :bootnodes deserialize-bootnodes)
(update :networks (partial reduce-kv (update :networks (partial reduce-kv
(fn [acc network-id props] (fn [acc network-id props]
(assoc acc network-id (assoc acc network-id
@ -31,6 +44,7 @@
(defn- serialize-account [account] (defn- serialize-account [account]
(-> account (-> account
(update :settings core/serialize) (update :settings core/serialize)
(update :bootnodes serialize-bootnodes)
(update :networks (partial map (fn [[_ props]] (update :networks (partial map (fn [[_ props]]
(update props :config types/clj->json)))))) (update props :config types/clj->json))))))

View File

@ -1,7 +1,8 @@
(ns status-im.data-store.realm.schemas.base.core (ns status-im.data-store.realm.schemas.base.core
(:require [status-im.data-store.realm.schemas.base.v1.core :as v1] (:require [status-im.data-store.realm.schemas.base.v1.core :as v1]
[status-im.data-store.realm.schemas.base.v2.core :as v2] [status-im.data-store.realm.schemas.base.v2.core :as v2]
[status-im.data-store.realm.schemas.base.v3.core :as v3])) [status-im.data-store.realm.schemas.base.v3.core :as v3]
[status-im.data-store.realm.schemas.base.v4.core :as v4]))
;; put schemas ordered by version ;; put schemas ordered by version
(def schemas [{:schema v1/schema (def schemas [{:schema v1/schema
@ -12,4 +13,7 @@
:migration v2/migration} :migration v2/migration}
{:schema v3/schema {:schema v3/schema
:schemaVersion 3 :schemaVersion 3
:migration v3/migration}]) :migration v3/migration}
{:schema v4/schema
:schemaVersion 4
:migration v4/migration}])

View File

@ -0,0 +1,29 @@
(ns status-im.data-store.realm.schemas.base.v4.account)
(def schema {:name :account
:primaryKey :address
:properties {:address :string
:public-key :string
:name {:type :string :optional true}
:email {:type :string :optional true}
:status {:type :string :optional true}
:debug? {:type :bool :default false}
:photo-path :string
:signing-phrase {:type :string}
:mnemonic {:type :string :optional true}
:last-updated {:type :int :default 0}
:last-sign-in {:type :int :default 0}
:signed-up? {:type :bool
:default false}
:network :string
:networks {:type :list
:objectType :network}
:bootnodes {:type :list
:objectType :bootnode}
:last-request {:type :int :optional true}
:settings {:type :string}
:sharing-usage-data? {:type :bool :default false}
:dev-mode? {:type :bool :default false}
:seed-backed-up? {:type :bool :default false}
:wallet-set-up-passed? {:type :bool
:default false}}})

View File

@ -0,0 +1,8 @@
(ns status-im.data-store.realm.schemas.base.v4.bootnode)
(def schema {:name :bootnode
:primaryKey :id
:properties {:id :string
:name {:type :string}
:chain {:type :string}
:address {:type :string}}})

View File

@ -0,0 +1,12 @@
(ns status-im.data-store.realm.schemas.base.v4.core
(:require [status-im.data-store.realm.schemas.base.v1.network :as network]
[status-im.data-store.realm.schemas.base.v4.account :as account]
[status-im.data-store.realm.schemas.base.v4.bootnode :as bootnode]
[taoensso.timbre :as log]))
(def schema [network/schema
bootnode/schema
account/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating base database v4: " old-realm new-realm))

View File

@ -622,6 +622,14 @@
:close-app-button "Confirm" :close-app-button "Confirm"
:connect-wnode-content "Connect to {{name}}?" :connect-wnode-content "Connect to {{name}}?"
;; Bootnodes
:bootnodes "Bootnodes"
:bootnodes-settings "Bootnodes settings"
:bootnodes-enabled "Bootnodes enabled"
:bootnode-address "Bootnode address"
:add-bootnode "Add bootnode"
:specify-bootnode-address "Specify bootnode address"
:mainnet-warning-title "Warning!" :mainnet-warning-title "Warning!"
:mainnet-warning-text "While we highly appreciate your contribution as a tester of Status, wed like to point out the dangers. Youre switching to Mainnet mode which is still in Alpha. This means it is still in development and has not been audited yet. Some of the risks you may be exposed to include:\n\n- Accounts may be unrecoverable due to breaking changes\n- Loss of ETH and tokens\n- Failure to send or receive messages\n\nSwitching to Mainnet should be done for testing purposes only. By tapping \"I understand\", you confirm that you assume the full responsibility for all risks concerning your data and funds. " :mainnet-warning-text "While we highly appreciate your contribution as a tester of Status, wed like to point out the dangers. Youre switching to Mainnet mode which is still in Alpha. This means it is still in development and has not been audited yet. Some of the risks you may be exposed to include:\n\n- Accounts may be unrecoverable due to breaking changes\n- Loss of ETH and tokens\n- Failure to send or receive messages\n\nSwitching to Mainnet should be done for testing purposes only. By tapping \"I understand\", you confirm that you assume the full responsibility for all risks concerning your data and funds. "
:mainnet-warning-ok-text "I understand" :mainnet-warning-ok-text "I understand"

View File

@ -3,6 +3,7 @@
(:require [cljs.spec.alpha :as spec] (:require [cljs.spec.alpha :as spec]
status-im.utils.db status-im.utils.db
status-im.ui.screens.network-settings.db status-im.ui.screens.network-settings.db
status-im.ui.screens.bootnodes-settings.db
[status-im.constants :as const])) [status-im.constants :as const]))
(defn valid-length? [password] (defn valid-length? [password]
@ -24,6 +25,7 @@
(spec/def :account/status (spec/nilable string?)) (spec/def :account/status (spec/nilable string?))
(spec/def :account/network (spec/nilable string?)) (spec/def :account/network (spec/nilable string?))
(spec/def :account/networks (spec/nilable :networks/networks)) (spec/def :account/networks (spec/nilable :networks/networks))
(spec/def :account/bootnodes (spec/nilable :bootnodes/bootnodes))
(spec/def :account/wnode (spec/nilable string?)) (spec/def :account/wnode (spec/nilable string?))
(spec/def :account/settings (spec/nilable (spec/map-of keyword? any?))) (spec/def :account/settings (spec/nilable (spec/map-of keyword? any?)))
(spec/def :account/signing-phrase :global/not-empty-string) (spec/def :account/signing-phrase :global/not-empty-string)
@ -41,7 +43,8 @@
:account/networks :account/settings :account/wnode :account/networks :account/settings :account/wnode
:account/last-sign-in :account/sharing-usage-data? :account/dev-mode? :account/last-sign-in :account/sharing-usage-data? :account/dev-mode?
:account/seed-backed-up? :account/mnemonic :account/seed-backed-up? :account/mnemonic
:account/wallet-set-up-passed? :account/last-request])) :account/wallet-set-up-passed? :account/last-request
:account/bootnodes]))
(spec/def :accounts/accounts (spec/nilable (spec/map-of :account/address :accounts/account))) (spec/def :accounts/accounts (spec/nilable (spec/map-of :account/address :accounts/account)))

View File

@ -63,12 +63,32 @@
(assoc db :node/after-start nil) (assoc db :node/after-start nil)
address password))) address password)))
(defn add-custom-bootnodes [config network all-bootnodes]
(let [bootnodes (as-> all-bootnodes $
(get $ network)
(vals $)
(map :address $))]
(if (seq bootnodes)
(assoc config :ClusterConfig {:Enabled true
:BootNodes bootnodes})
config)))
(defn get-network-by-address [db address] (defn get-network-by-address [db address]
(let [accounts (get db :accounts/accounts) (let [accounts (get db :accounts/accounts)
{:keys [network networks]} (get accounts address) {:keys [network
config (get-in networks [network :config])] settings
{:network network bootnodes
:config config})) networks]} (get accounts address)
use-custom-bootnodes (get-in settings [:bootnodes network])
config (cond-> (get-in networks [network :config])
(and
config/bootnodes-settings-enabled?
use-custom-bootnodes)
(add-custom-bootnodes network bootnodes))]
{:use-custom-bootnodes use-custom-bootnodes
:network network
:config config}))
(defn wrap-with-initialize-geth-fx [db address password] (defn wrap-with-initialize-geth-fx [db address password]
(let [{:keys [network config]} (get-network-by-address db address)] (let [{:keys [network config]} (get-network-by-address db address)]
@ -87,21 +107,31 @@
{:db (assoc db :node/after-stop [::start-node address password]) {:db (assoc db :node/after-stop [::start-node address password])
::stop-node nil}) ::stop-node nil})
(defn- restart-node? [account-network network use-custom-bootnodes]
(or (not= account-network network)
(and config/bootnodes-settings-enabled?
use-custom-bootnodes)))
(defn login-account [{{:keys [network status-node-started?] :as db} :db} [_ address password]]
(let [{use-custom-bootnodes :use-custom-bootnodes
account-network :network} (get-network-by-address db address)
db' (-> db
(assoc-in [:accounts/login :processing] true))
wrap-fn (cond (not status-node-started?)
wrap-with-initialize-geth-fx
(not (restart-node? account-network
network
use-custom-bootnodes))
wrap-with-login-account-fx
:else
wrap-with-stop-node-fx)]
(wrap-fn db' address password)))
(register-handler-fx (register-handler-fx
:login-account :login-account
(fn [{{:keys [network status-node-started?] :as db} :db} [_ address password]] login-account)
(let [{account-network :network} (get-network-by-address db address)
db' (-> db
(assoc-in [:accounts/login :processing] true))
wrap-fn (cond (not status-node-started?)
wrap-with-initialize-geth-fx
(= account-network network)
wrap-with-login-account-fx
:else
wrap-with-stop-node-fx)]
(wrap-fn db' address password))))
(register-handler-fx (register-handler-fx
:login-handler :login-handler

View File

@ -0,0 +1,18 @@
(ns status-im.ui.screens.bootnodes-settings.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require
[clojure.string :as string]
[cljs.spec.alpha :as spec]))
(spec/def ::not-blank-string (complement string/blank?))
(spec/def :bootnode/address ::not-blank-string)
(spec/def :bootnode/name ::not-blank-string)
(spec/def :bootnode/id ::not-blank-string)
(spec/def :bootnode/chain ::not-blank-string)
(spec/def :bootnode/bootnode (allowed-keys :req-un [:bootnode/chain
:bootnode/address
:bootnode/name
:bootnode/id]))
(spec/def :bootnodes/bootnodes (spec/nilable (spec/map-of :bootnode/id (spec/map-of :bootnode/id :bootnode/bootnode))))

View File

@ -0,0 +1,51 @@
(ns status-im.ui.screens.bootnodes-settings.edit-bootnode.events
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.utils.handlers :refer [register-handler] :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.ui.screens.accounts.utils :as accounts.utils]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.types :as types]
[status-im.utils.inbox :as utils.inbox]))
(defn- new-bootnode [id bootnode-name address chain]
{:address address
:chain chain
:id (string/replace id "-" "")
:name bootnode-name})
(defn save-new-bootnode [{{:bootnodes/keys [manage] :account/keys [account] :as db} :db :as cofx} _]
(let [{:keys [name url]} manage
network (:network db)
bootnode (new-bootnode
(:random-id cofx)
(:value name)
(:value url)
network)
new-bootnodes (assoc-in (:bootnodes account) [network (:id bootnode)] bootnode)]
(handlers-macro/merge-fx cofx
{:db (dissoc db :bootnodes/manage)
:dispatch [:navigate-back]}
(accounts.utils/account-update {:bootnodes new-bootnodes}))))
(handlers/register-handler-fx
:save-new-bootnode
[(re-frame/inject-cofx :random-id)]
save-new-bootnode)
(handlers/register-handler-fx
:bootnode-set-input
(fn [{db :db} [_ input-key value]]
{:db (update db :bootnodes/manage assoc input-key {:value value
:error (if (= input-key :name)
(string/blank? value)
(not (utils.inbox/valid-enode-address? value)))})}))
(handlers/register-handler-fx
:edit-bootnode
(fn [{db :db} _]
{:db (update-in db [:bootnodes/manage] assoc
:name {:error true}
:url {:error true})
:dispatch [:navigate-to :edit-bootnode]}))

View File

@ -0,0 +1,15 @@
(ns status-im.ui.screens.bootnodes-settings.edit-bootnode.styles
(:require-macros [status-im.utils.styles :refer [defstyle]]))
(def edit-bootnode-view
{:flex 1
:margin-horizontal 16
:margin-vertical 15})
(def input-container
{:margin-bottom 15})
(def bottom-container
{:flex-direction :row
:margin-horizontal 12
:margin-vertical 15})

View File

@ -0,0 +1,14 @@
(ns status-im.ui.screens.bootnodes-settings.edit-bootnode.subs
(:require [re-frame.core :refer [reg-sub]]))
(reg-sub
:get-manage-bootnode
:<- [:get :bootnodes/manage]
(fn [manage]
manage))
(reg-sub
:manage-bootnode-valid?
:<- [:get-manage-bootnode]
(fn [manage]
(not-any? :error (vals manage))))

View File

@ -0,0 +1,42 @@
(ns status-im.ui.screens.bootnodes-settings.edit-bootnode.views
(:require-macros [status-im.utils.views :as views])
(:require
[re-frame.core :as re-frame]
[status-im.ui.components.react :as react]
[status-im.i18n :as i18n]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.text-input.view :as text-input]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.styles :as styles]))
(views/defview edit-bootnode []
(views/letsubs [manage-bootnode [:get-manage-bootnode]
is-valid? [:manage-bootnode-valid?]]
[react/view components.styles/flex
[status-bar/status-bar]
[react/keyboard-avoiding-view components.styles/flex
[toolbar/simple-toolbar (i18n/label :t/add-bootnode)]
[react/scroll-view
[react/view styles/edit-bootnode-view
[text-input/text-input-with-label
{:label (i18n/label :t/name)
:placeholder (i18n/label :t/specify-name)
:container styles/input-container
:default-value (get-in manage-bootnode [:name :value])
:on-change-text #(re-frame/dispatch [:bootnode-set-input :name %])
:auto-focus true}]
[text-input/text-input-with-label
{:label (i18n/label :t/bootnode-address)
:placeholder (i18n/label :t/specify-bootnode-address)
:container styles/input-container
:default-value (get-in manage-bootnode [:url :value])
:on-change-text #(re-frame/dispatch [:bootnode-set-input :url %])}]]]
[react/view styles/bottom-container
[react/view components.styles/flex]
[components.common/bottom-button
{:forward? true
:label (i18n/label :t/save)
:disabled? (not is-valid?)
:on-press #(re-frame/dispatch [:save-new-bootnode])}]]]]))

View File

@ -0,0 +1,22 @@
(ns status-im.ui.screens.bootnodes-settings.events
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.ui.screens.accounts.events :as accounts-events]
[status-im.i18n :as i18n]
[status-im.transport.core :as transport]
status-im.ui.screens.bootnodes-settings.edit-bootnode.events
[status-im.utils.ethereum.core :as ethereum]))
(defn toggle-custom-bootnodes [value {:keys [db] :as cofx}]
(let [network (get-in db [:account/account :network])
settings (get-in db [:account/account :settings])]
(handlers-macro/merge-fx cofx
(accounts-events/update-settings
(assoc-in settings [:bootnodes network] value)
[:logout]))))
(handlers/register-handler-fx
:toggle-custom-bootnodes
(fn [cofx [_ value]]
(toggle-custom-bootnodes value cofx)))

View File

@ -0,0 +1,30 @@
(ns status-im.ui.screens.bootnodes-settings.styles
(:require [status-im.ui.components.colors :as colors])
(:require-macros [status-im.utils.styles :refer [defstyle]]))
(def wrapper
{:flex 1
:background-color :white})
(def bootnode-item-inner
{:padding-horizontal 16})
(defstyle bootnode-item
{:flex-direction :row
:background-color :white
:align-items :center
:padding-horizontal 16
:ios {:height 64}
:android {:height 56}})
(defstyle bootnode-item-name-text
{:color colors/black
:ios {:font-size 17
:letter-spacing -0.2
:line-height 20}
:android {:font-size 16}})
(defstyle switch-container
{:height 50
:background-color colors/white
:padding-left 15})

View File

@ -0,0 +1,15 @@
(ns status-im.ui.screens.bootnodes-settings.subs
(:require [re-frame.core :as re-frame]
status-im.ui.screens.bootnodes-settings.edit-bootnode.subs
[status-im.utils.ethereum.core :as ethereum]))
(re-frame/reg-sub :settings/bootnodes-enabled
:<- [:get :account/account]
(fn [account]
(let [{:keys [network settings]} account]
(get-in settings [:bootnodes network]))))
(re-frame/reg-sub :settings/network-bootnodes
:<- [:get :account/account]
(fn [account]
(get-in account [:bootnodes (:network account)])))

View File

@ -0,0 +1,46 @@
(ns status-im.ui.screens.bootnodes-settings.views
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.utils.config :as config]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.toolbar.actions :as toolbar.actions]
[status-im.ui.screens.profile.components.views :as profile.components]
[status-im.ui.screens.bootnodes-settings.styles :as styles]))
(defn navigate-to-add-bootnode []
(re-frame/dispatch [:edit-bootnode]))
(defn render-row [{:keys [name id]}]
[react/view
{:accessibility-label :bootnode-item}
[react/view styles/bootnode-item
[react/view styles/bootnode-item-inner
[react/text {:style styles/bootnode-item-name-text}
name]]]])
(views/defview bootnodes-settings []
(views/letsubs [bootnodes-enabled [:settings/bootnodes-enabled]
bootnodes [:settings/network-bootnodes]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (i18n/label :t/bootnodes-settings)]
[toolbar/actions
[(toolbar.actions/add false navigate-to-add-bootnode)]]]
[react/view styles/switch-container
[profile.components/settings-switch-item
{:label-kw :t/bootnodes-enabled
:value bootnodes-enabled
:action-fn #(re-frame/dispatch [:toggle-custom-bootnodes %])}]]
[react/view styles/wrapper
[list/flat-list {:data (vals bootnodes)
:default-separator? false
:key-fn :id
:render-fn render-row}]]]))

View File

@ -170,6 +170,7 @@
:networks/networks :networks/networks
:networks/manage :networks/manage
:mailservers/manage :mailservers/manage
:bootnodes/manage
:node/after-start :node/after-start
:node/after-stop :node/after-stop
:inbox/wnodes :inbox/wnodes

View File

@ -24,6 +24,7 @@
status-im.ui.screens.wallet.choose-recipient.events status-im.ui.screens.wallet.choose-recipient.events
status-im.ui.screens.browser.events status-im.ui.screens.browser.events
status-im.ui.screens.offline-messaging-settings.events status-im.ui.screens.offline-messaging-settings.events
status-im.ui.screens.bootnodes-settings.events
status-im.ui.screens.currency-settings.events status-im.ui.screens.currency-settings.events
status-im.ui.screens.usage-data.events status-im.ui.screens.usage-data.events
[re-frame.core :as re-frame] [re-frame.core :as re-frame]

View File

@ -2,7 +2,6 @@
(:require [re-frame.core :refer [dispatch dispatch-sync after] :as re-frame] (:require [re-frame.core :refer [dispatch dispatch-sync after] :as re-frame]
[status-im.utils.handlers :refer [register-handler] :as handlers] [status-im.utils.handlers :refer [register-handler] :as handlers]
status-im.ui.screens.network-settings.edit-network.events status-im.ui.screens.network-settings.edit-network.events
status-im.ui.screens.offline-messaging-settings.edit-mailserver.events
[status-im.utils.handlers-macro :as handlers-macro] [status-im.utils.handlers-macro :as handlers-macro]
[status-im.ui.screens.accounts.utils :as accounts.utils] [status-im.ui.screens.accounts.utils :as accounts.utils]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]

View File

@ -5,6 +5,7 @@
[status-im.ui.screens.accounts.events :as accounts-events] [status-im.ui.screens.accounts.events :as accounts-events]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.transport.core :as transport] [status-im.transport.core :as transport]
status-im.ui.screens.offline-messaging-settings.edit-mailserver.events
[status-im.utils.ethereum.core :as ethereum])) [status-im.utils.ethereum.core :as ethereum]))
(handlers/register-handler-fx (handlers/register-handler-fx

View File

@ -152,6 +152,11 @@
{:label-kw :t/offline-messaging {:label-kw :t/offline-messaging
:action-fn #(re-frame/dispatch [:navigate-to :offline-messaging-settings]) :action-fn #(re-frame/dispatch [:navigate-to :offline-messaging-settings])
:accessibility-label :offline-messages-settings-button}]) :accessibility-label :offline-messages-settings-button}])
(when config/bootnodes-settings-enabled?
[profile.components/settings-item
{:label-kw :t/bootnodes
:action-fn #(re-frame/dispatch [:navigate-to :bootnodes-settings])
:accessibility-label :bootnodes-settings-button}])
[profile.components/settings-item-separator] [profile.components/settings-item-separator]
[profile.components/settings-item [profile.components/settings-item
{:label-kw :t/help-improve? {:label-kw :t/help-improve?

View File

@ -13,6 +13,7 @@
status-im.ui.screens.wallet.transactions.subs status-im.ui.screens.wallet.transactions.subs
status-im.ui.screens.network-settings.subs status-im.ui.screens.network-settings.subs
status-im.ui.screens.offline-messaging-settings.subs status-im.ui.screens.offline-messaging-settings.subs
status-im.ui.screens.bootnodes-settings.subs
status-im.ui.screens.currency-settings.subs status-im.ui.screens.currency-settings.subs
status-im.ui.screens.browser.subs status-im.ui.screens.browser.subs
status-im.bots.subs status-im.bots.subs

View File

@ -39,6 +39,8 @@
[status-im.ui.screens.network-settings.edit-network.views :refer [edit-network]] [status-im.ui.screens.network-settings.edit-network.views :refer [edit-network]]
[status-im.ui.screens.offline-messaging-settings.views :refer [offline-messaging-settings]] [status-im.ui.screens.offline-messaging-settings.views :refer [offline-messaging-settings]]
[status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :refer [edit-mailserver]] [status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :refer [edit-mailserver]]
[status-im.ui.screens.bootnodes-settings.views :refer [bootnodes-settings]]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.views :refer [edit-bootnode]]
[status-im.ui.screens.currency-settings.views :refer [currency-settings]] [status-im.ui.screens.currency-settings.views :refer [currency-settings]]
[status-im.ui.screens.browser.views :refer [browser]] [status-im.ui.screens.browser.views :refer [browser]]
[status-im.ui.screens.add-new.open-dapp.views :refer [open-dapp dapp-description]] [status-im.ui.screens.add-new.open-dapp.views :refer [open-dapp dapp-description]]
@ -85,6 +87,8 @@
:edit-network edit-network :edit-network edit-network
:offline-messaging-settings offline-messaging-settings :offline-messaging-settings offline-messaging-settings
:edit-mailserver edit-mailserver :edit-mailserver edit-mailserver
:bootnodes-settings bootnodes-settings
:edit-bootnode edit-bootnode
:currency-settings currency-settings :currency-settings currency-settings
:recent-recipients recent-recipients :recent-recipients recent-recipients
:recipient-qr-code recipient-qr-code :recipient-qr-code recipient-qr-code

View File

@ -22,6 +22,7 @@
(def stub-status-go? (enabled? (get-config :STUB_STATUS_GO 0))) (def stub-status-go? (enabled? (get-config :STUB_STATUS_GO 0)))
(def mainnet-warning-enabled? (enabled? (get-config :MAINNET_WARNING_ENABLED 0))) (def mainnet-warning-enabled? (enabled? (get-config :MAINNET_WARNING_ENABLED 0)))
(def offline-inbox-enabled? (enabled? (get-config :OFFLINE_INBOX_ENABLED "1"))) (def offline-inbox-enabled? (enabled? (get-config :OFFLINE_INBOX_ENABLED "1")))
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
(def log-level (def log-level
(-> (get-config :LOG_LEVEL "error") (-> (get-config :LOG_LEVEL "error")
string/lower-case string/lower-case

View File

@ -35,7 +35,9 @@
[status-im.test.utils.datetime] [status-im.test.utils.datetime]
[status-im.test.utils.mixpanel] [status-im.test.utils.mixpanel]
[status-im.test.utils.prices] [status-im.test.utils.prices]
[status-im.test.ui.screens.network-settings.edit-network.events])) [status-im.test.ui.screens.network-settings.edit-network.events]
[status-im.test.ui.screens.bootnodes-settings.edit-bootnode.events]
[status-im.test.ui.screens.accounts.login.events]))
(enable-console-print!) (enable-console-print!)
@ -81,4 +83,6 @@
'status-im.test.utils.datetime 'status-im.test.utils.datetime
'status-im.test.utils.mixpanel 'status-im.test.utils.mixpanel
'status-im.test.utils.prices 'status-im.test.utils.prices
'status-im.test.ui.screens.network-settings.edit-network.events) 'status-im.test.ui.screens.network-settings.edit-network.events
'status-im.test.ui.screens.bootnodes-settings.edit-bootnode.events
'status-im.test.ui.screens.accounts.login.events)

View File

@ -0,0 +1,132 @@
(ns status-im.test.ui.screens.accounts.login.events
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.config :as config]
[status-im.ui.screens.accounts.login.events :as events]))
(deftest login-account
(let [mainnet-account {:network "mainnet_rpc"
:networks {"mainnet_rpc" {:config {:NetworkId 1}}}}
testnet-account {:network "testnet_rpc"
:networks {"testnet_rpc" {:config {:NetworkId 3}}}}
accounts {"mainnet" mainnet-account
"testnet" testnet-account}
initial-db {:db {:network "mainnet_rpc"
:accounts/accounts accounts}}]
(testing "status-go has not started"
(let [actual (events/login-account initial-db [nil "testnet" "password"])]
(testing "it starts status-node if it has not started"
(is (= {:NetworkId 3}
(:initialize-geth-fx
actual))))
(testing "it logins the user after the node started"
(is (= [::events/login-account "testnet" "password"] (get-in actual [:db :node/after-start]))))))
(testing "status-go has started & the user is on mainnet"
(let [db (assoc-in initial-db [:db :status-node-started?] true)
actual (events/login-account
db
[nil "mainnet" "password"])]
(testing "it does not start status-node if it has already started"
(is (not (:initialize-geth-fx actual))))
(testing "it logs in the user"
(is (= ["mainnet" "password"] (::events/login actual))))))
(testing "the user has selected a different network"
(testing "status-go has started"
(let [db (assoc-in initial-db [:db :status-node-started?] true)
actual (events/login-account
db
[nil "testnet" "password"])]
(testing "it dispatches start-node"
(is (get-in actual [:db :node/after-stop] [::events/start-node "testnet" "password"])))
(testing "it stops status-node"
(is (contains? actual ::events/stop-node)))))
(testing "status-go has not started"
(let [actual (events/login-account
initial-db
[nil "testnet" "password"])]
(testing "it starts status-node"
(is (= {:NetworkId 3} (:initialize-geth-fx actual))))
(testing "it logins the user after the node started"
(is (= [::events/login-account "testnet" "password"] (get-in actual [:db :node/after-start])))))))
(testing "custom bootnodes"
(let [custom-bootnodes {"a" {:id "a"
:name "name-a"
:address "address-a"}
"b" {:id "b"
:name "name-b"
:address "address-b"}}
bootnodes-db (assoc-in
initial-db
[:db :accounts/accounts "mainnet" :bootnodes]
{"mainnet_rpc" custom-bootnodes})]
(testing "custom bootnodes enabled"
(let [bootnodes-enabled-db (assoc-in
bootnodes-db
[:db :accounts/accounts "mainnet" :settings]
{:bootnodes {"mainnet_rpc" true}})
actual (events/login-account
bootnodes-enabled-db
[nil "mainnet" "password"])]
(testing "status-node has started"
(let [db (assoc-in bootnodes-enabled-db [:db :status-node-started?] true)
actual (events/login-account
db
[nil "mainnet" "password"])]
(testing "it dispatches start-node"
(is (get-in actual [:db :node/after-stop] [::events/start-node "testnet" "password"])))
(testing "it stops status-node"
(is (contains? actual ::events/stop-node)))))
(testing "status-node has not started"
(let [actual (events/login-account
bootnodes-enabled-db
[nil "mainnet" "password"])]
(testing "it adds bootnodes to the config"
(is (= {:ClusterConfig {:Enabled true
:BootNodes ["address-a" "address-b"]}
:NetworkId 1} (:initialize-geth-fx actual))))
(testing "it logins the user after the node started"
(is (= [::events/login-account "mainnet" "password"] (get-in actual [:db :node/after-start]))))))))
(testing "custom bootnodes not enabled"
(testing "status-node has started"
(let [db (assoc-in bootnodes-db [:db :status-node-started?] true)
actual (events/login-account
db
[nil "mainnet" "password"])]
(testing "it does not start status-node if it has already started"
(is (not (:initialize-geth-fx actual))))
(testing "it logs in the user"
(is (= ["mainnet" "password"] (::events/login actual))))))
(testing "status-node has not started"
(let [actual (events/login-account
bootnodes-db
[nil "mainnet" "password"])]
(testing "it starts status-node without custom bootnodes"
(is (= {:NetworkId 1} (:initialize-geth-fx actual))))
(testing "it logins the user after the node started"
(is (= [::events/login-account "mainnet" "password"] (get-in actual [:db :node/after-start])))))))))))
(deftest restart-node?
(testing "custom bootnodes is toggled off"
(with-redefs [config/bootnodes-settings-enabled? false]
(testing "it returns true when the network is different"
(is (events/restart-node? "mainnet_rpc" "mainnet" true)))
(testing "it returns false when the network is the same"
(is (not (events/restart-node? "mainnet" "mainnet" true))))))
(testing "custom bootnodes is toggled on"
(with-redefs [config/bootnodes-settings-enabled? true]
(testing "the user is not using custom bootnodes"
(testing "it returns true when the network is different"
(is (events/restart-node? "mainnet_rpc" "mainnet" false)))
(testing "it returns false when the network is the same"
(is (not (events/restart-node? "mainnet" "mainnet" false)))))
(testing "the user is using custom bootnodes"
(testing "it returns true when the network is different"
(is (events/restart-node? "mainnet" "mainnet" true)))
(testing "it returns true when the network is the same"
(is (events/restart-node? "mainnet_rpc" "mainnet" true)))))))

View File

@ -0,0 +1,19 @@
(ns status-im.test.ui.screens.bootnodes-settings.edit-bootnode.events
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.events :as events]))
(deftest add-new-bootnode
(testing "adding a bootnode"
(let [new-bootnode {:name {:value "name"}
:url {:value "url"}}
expected {"mainnet_rpc" {"someid" {:name "name"
:address "url"
:chain "mainnet_rpc"
:id "someid"}}}
actual (events/save-new-bootnode
{:random-id "some-id"
:db {:bootnodes/manage new-bootnode
:network "mainnet_rpc"
:account/account {}}}
nil)]
(is (= expected (get-in actual [:db :account/account :bootnodes]))))))