Local Test Network

I run a local network for testing, and there are a few limits that I remove from vaults (I haven’t used mock routing, which may be adequate for your needs).

After these changes, the new vault binary will need to be built from source.

That’s it for vaults, but then the demo app has upload limits that need to be changed if you’re using it to test.

This is quite a lot of work to get up and running, so I made a script called SafeInABox which does all this work. I confess these days I usually use it simply as a reference for where and what to change, and it probably won’t suit your needs as-is, but it’s worth a look.

Hope this helps, I’ve done a lot of work in this area (see Profiling Vault Performance) so am happy to answer any questions.

6 Likes

Thats fantastic. Exactly what I was looking for.

3 Likes

Thanks @mav, I think I will have to try this out soon so hopefully it is still up to date. I am writing my own Rust client for the SAFE Network to learn the API better so I think I will soon need a test network for that as I am already almost at my PUTs limit. (Not to mention I would like to write Elm, Javascript and Ruby versions too at some point as well.)

So just FYI team I may be back with question about using a test network soon. :slight_smile:

4 Likes

Yay for Elm! I hope to see an Elm package for the API eventually.

2 Likes

There are some additional things that recently required changing as outlined by @tfa in

1 Like

For future records, a full list of modifications required to run a local network on Test 12C (ie vaults 0.13.1)

Test 12C

The diffs below are git diffs - any line starting with a minus sign is to be removed, any line starting with a plus sign is to be added.

language versions

$ rustc --version
rustc 1.16.0 (30cf806ef 2017-03-10)
$ cargo --version
cargo-0.17.0-nightly (f9e5481 2017-03-03)
$ node --version
v6.0.0
$ npm --version
3.8.6

safe_vault

git checkout 0.13.1

src/bin/safe_vault.rs
remove linting for unused and warnings

L27
-          unknown_crate_types, warnings)]
+          unknown_crate_types)]

L33
-        unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes,
+        unknown_lints, unsafe_code, unused_allocation, unused_attributes,

src/lib.rs
remove linting for unused and warnings

L201
-          unknown_crate_types, warnings)]
+          unknown_crate_types)]

L205
-        unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes,
+        unknown_lints, unsafe_code, unused_allocation, unused_attributes,

Cargo.toml
use local copy of routing

L24
-routing = "~0.28.2"
+routing = { path = "/home/user/maidsafe/routing" }

src/personas/maid_manager.rs
remove account limits

L34
-const DEFAULT_ACCOUNT_SIZE: u64 = 500;
+const DEFAULT_ACCOUNT_SIZE: u64 = 500000000;

routing

git checkout 0.28.2

Cargo.toml
use local copy of crust

L13
-crust = "~0.22.0"
+crust = { path = "/home/user/maidsafe/crust" }

src/states/node.rs
Remove proof of work check for peers on local network

L1326
         let (difficulty, target_size) = if self.crust_service.is_peer_hard_coded(&peer_id) ||
+                                           self.crust_service.has_peers_on_lan() ||
                                            self.peer_mgr.get_joining_node(&peer_id).is_some() {
             (0, 1)
         } else {

Do not disconnect based on whitelist (@tfa this hasn’t been mentioned anywhere else yet, and was the missing piece of the puzzle for me to reliably bootstrap my pine64 network)

L398-406
-         if !self.crust_service.is_peer_whitelisted(&peer_id) {
+         if !self.crust_service.is_peer_whitelisted(&peer_id) && false {
             debug!("{:?} Received ConnectSuccess, but {:?} is not whitelisted.",
L1261
-         if !client_restriction && !self.crust_service.is_peer_whitelisted(&peer_id) {
+         if !client_restriction && !self.crust_service.is_peer_whitelisted(&peer_id) && false {
             warn!("{:?} Client is not whitelisted, so dropping connection.",

src/states/bootstrapping.rs
Fix FailedExternalReachability

L67
-                let _ = self.crust_service.start_bootstrap(HashSet::new(), CrustUser::Node);
+                let _ = self.crust_service.start_bootstrap(HashSet::new(), CrustUser::Client);

L133
-            let crust_user = if self.client_restriction {
-                CrustUser::Client
-            } else {
-                CrustUser::Node
-            };
             let _ = self.crust_service
-                .start_bootstrap(self.bootstrap_blacklist.clone(), crust_user);
+                .start_bootstrap(self.bootstrap_blacklist.clone(), CrustUser::Client);

src/node.rs
Allow peers on lan

L116
-                              self.deny_other_local_nodes && crust_service.has_peers_on_lan() {
+                              self.deny_other_local_nodes && crust_service.has_peers_on_lan() && false {

crust

In theory this shouldn’t need to be local since there are no modifications, but I found it wouldn’t work without using the local copy.

git checkout 0.22.0

src/lib.rs
remove linting for warnings and deprecated

L29-L30
-          unknown_crate_types, warnings)]
-#![deny(deprecated, improper_ctypes, missing_docs,
+          unknown_crate_types)]
+#![deny(improper_ctypes, missing_docs,

safe_client_libs

Required for safe_launcher

git checkout 0.22.3

Cargo.toml
use local copy of routing

L21
-routing = "~0.28.2"
+routing = { path = "/home/user/maidsafe/routing" }

src/lib.rs
remove linting for warnings and unused

L29
-          unknown_crate_types, warnings)]
+          unknown_crate_types)]

L33
-        unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes,
+        unknown_lints, unsafe_code, unused_allocation, unused_attributes,

safe_launcher

git checkout 0.10.2

No code modifications required.
Copy the custom binary libsafe_core generated from safe_client_libs to app/ffi/

demo_app

demo app is v0.6.2 which is in git tag 0.10.1 of safe_examples repo

git checkout 0.10.1

demo_app/config/env_[production|test|development]
Remove upload limits on filesize

L3-L4
-  "isFileUploadSizeRestricted": true,
-  "maxFileUploadSize": 25000000
+  "isFileUploadSizeRestricted": false,
+  "maxFileUploadSize": 25000000000000
6 Likes

Again, I have problems to run a local network.

It was Ok until commit ba64cf9 (included) of routing. I implemented principles defined above in my fork here.

But I tried to reimplement them on latest commit (ec585d1) but that doesn’t work anymore.

First, I came across this error:

INFO 23:21:20.552017000 [routing::states::bootstrapping bootstrapping.rs:295] Bootstrapping(506a15..) Connection failed: Proxy node needs a larger routing table to accept clients.

The workaround I found was to add the line testing the local-network feature in src/states/node.rs:

         if (peer_kind == CrustUser::Client || !self.is_first_node) &&
            !cfg!(feature = "local-network") &&
            self.routing_table().len() < self.min_section_size() - 1 {
             debug!("{:?} Client {:?} rejected: Routing table has {} entries. {} required.",
                    self,

But then the nodes fail to get relocated name:

INFO 22:58:05.075736900 [safe_vault::vault vault.rs:96] Use local-network feature
INFO 22:58:10.875088900 [routing::states::joining_node joining_node.rs:294] JoiningNode(f030ab..()) Requesting a relocated name from the network. This can take a while.
INFO 23:04:10.893171200 [routing::states::joining_node joining_node.rs:317] JoiningNode(f030ab..()) Failed to get relocated name from the network, so restarting.
WARN 23:04:10.893675800 [safe_vault::vault vault.rs:143] Restarting Vault
…

I suppose this is because the node connects as a client. But this was the way to make local network works until now. Without it I get the following error:

ERROR 23:39:38.478785200 [crust::main::bootstrap mod.rs:188] Failed to Bootstrap<UID>: (FailedExternalReachability) Bootstrappee node could not establish connection to us.

So now I am blocked. Please, could Maidsafe give some indications on making the local network works.

Note: I am pretty sure there was another thread about local networks where we were discussing the possibility to add a feature gated local network. I remember that this idea was postponed but the problem is that I don’t find this thread anymore.

Edit: I have found the thread. It is not a public topic but a PM in the other forum (initiated by @viv to @mav and me).

I still fail in running a local network. I tried some modifications but none succeeded.

Could @maidsafe_team indicates how to modify the code to make it work?

I found a way for local networks. There are modifications in 3 crates. They are available in my forks on GitHub:

To anyone: To create a local network, just clone the last one (safe_vault) and build safe_vault binary with cargo build --features "local-network" command.

@maidsafe: I didn’t make PR’s because there are probably things you wouldn’t code like I did, but please, take local networks into account in your base code. It is a pain to find how to do it each time there is a major change.

8 Likes

Thanks @tfa, this is awesome! Could you please share your vault / crust config files? I’m able to build your version of the vault but then it just shuts down after a while. Did you run all instances locally on one machine?

I don’t have any config files and I run all vaults locally on one machine.

I launch seed node with RUST_LOG=info ./target/debug/safe_vault -f in a first command window and then each following nodes with ./target/debug/safe_vault& in another command window.

3 Likes

My previous forks were not compatible with test17 client binaries. I tried to modify them but I didn’t succeed in making them work.

So, instead, I have recreated them from current master branches in hope that local networks created with new safe_vault will be compatible with next client binaries.

I am looking forward these binaries to test this possibility.

I had to add a small modification in routing to inhibit control of number of clients, but now local network work with new client binaries delivered by Maidsafe (with rate limiter). I just did a little test: created an account, added a service with a one file site and browsed to it successfully.

The procedure to create a local network is the following:

  • git clone https://github.com/Thierry61/safe_vault.git
  • cd safe_vault
  • cargo build --features “local-network”
  • launch the seed node in a first command window: RUST_LOG=info ./target/debug/safe_vault -f
  • launch other nodes in another command window: ./target/debug/safe_vault&. Do it at least 7 times and wait 10 s between each invocation

To use your local network with client binaries you have to:

  • delete or rename “SAFE Browser.crust.config” file in safe-browser directory (to connect to your local network instead of test 17)
  • pass any string for invitation
4 Likes
$ cargo build --features "local-network" --verbose
    Updating registry `https://github.com/rust-lang/crates.io-index`
    Updating git repository `https://github.com/Thierry61/routing.git`
    Updating git repository `https://github.com/Thierry61/crust.git`
error: failed to load source for a dependency on `crust`

Caused by:
  Unable to update https://github.com/Thierry61/crust.git

Caused by:
  failed to clone /home/folatt/.cargo/git/db/crust-8c8d4c67f38703cc into /home/folatt/.cargo/git/checkouts/crust-8c8d4c67f38703cc/2237fa3

Caused by:
  [5/-1] error inflating zlib stream

[update]

It builds with sudo.

I think I have the network working.

$ RUST_LOG=info ./target/debug/safe_vault -f
INFO 18:20:15.678383196 [safe_vault safe_vault.rs:75] 

Running safe_vault v0.15.0
==========================
INFO 18:20:15.679440601 [safe_vault::vault vault.rs:99] Use local-network feature
INFO 18:20:15.784839596 [routing::states::node node.rs:166] Node(cf19e1..()) Started a new network as a seed node.
INFO 18:20:57.640240576 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name 70ebe7...
INFO 18:20:57.662900650 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name 70ebe7...
INFO 18:21:01.869226236 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate 70ebe7..->7dd755..
INFO 18:21:01.878361425 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate 7dd755.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:21:01.879289634 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate 7dd755...
INFO 18:21:01.903338116 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate 7dd755...
INFO 18:21:01.918172580 [routing::states::node node.rs:1695] Node(cf19e1..()) Added 7dd755.. to routing table.
INFO 18:21:01.926605962 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:21:01.926824900 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   1 |
INFO 18:21:01.926979056 [routing::states::node node.rs:343] | Exact network size: 2                      |
INFO 18:21:01.927154104 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:21:21.397646879 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name af1858...
INFO 18:21:21.512342508 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name af1858...
INFO 18:21:25.767027764 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate af1858..->4a60d7..
INFO 18:21:25.814586827 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate 4a60d7.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:21:25.816519082 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate 4a60d7...
INFO 18:21:25.930155675 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate 4a60d7...
INFO 18:21:25.939764339 [routing::states::node node.rs:1695] Node(cf19e1..()) Added 4a60d7.. to routing table.
INFO 18:21:25.948302125 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:21:25.948526735 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   2 |
INFO 18:21:25.948679921 [routing::states::node node.rs:343] | Exact network size: 3                      |
INFO 18:21:25.948855162 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:21:38.039901254 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name d7b76e...
INFO 18:21:38.067836958 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name d7b76e...
INFO 18:21:42.278183132 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate d7b76e..->9d1cc7..
INFO 18:21:42.332504088 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate 9d1cc7.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:21:42.332960689 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate 9d1cc7...
INFO 18:21:42.380499693 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate 9d1cc7...
INFO 18:21:42.385440388 [routing::states::node node.rs:1695] Node(cf19e1..()) Added 9d1cc7.. to routing table.
INFO 18:21:42.388798616 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:21:42.388892272 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   3 |
INFO 18:21:42.388942224 [routing::states::node node.rs:343] | Exact network size: 4                      |
INFO 18:21:42.388997448 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:22:03.947704103 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name d47f1d...
INFO 18:22:03.995571071 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name d47f1d...
INFO 18:22:08.225447588 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate d47f1d..->2a94c3..
INFO 18:22:08.281262397 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate 2a94c3.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:22:08.282732898 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate 2a94c3...
INFO 18:22:08.453066469 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate 2a94c3...
INFO 18:22:08.465270495 [routing::states::node node.rs:1695] Node(cf19e1..()) Added 2a94c3.. to routing table.
INFO 18:22:08.475450524 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:22:08.475689708 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   4 |
INFO 18:22:08.475848237 [routing::states::node node.rs:343] | Exact network size: 5                      |
INFO 18:22:08.476025226 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:23:26.085317641 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name 1adb68...
INFO 18:23:26.153188782 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name 1adb68...
INFO 18:23:30.395803678 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate 1adb68..->64bd3c..
INFO 18:23:30.458348885 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate 64bd3c.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:23:30.459474805 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate 64bd3c...
INFO 18:23:30.705475200 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate 64bd3c...
INFO 18:23:30.711114159 [routing::states::node node.rs:1695] Node(cf19e1..()) Added 64bd3c.. to routing table.
INFO 18:23:30.716389071 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:23:30.718624468 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   5 |
INFO 18:23:30.719316754 [routing::states::node node.rs:343] | Exact network size: 6                      |
INFO 18:23:30.719776596 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:24:36.054680119 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name e7edaf...
INFO 18:24:36.200171460 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name e7edaf...
INFO 18:24:40.566274959 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate e7edaf..->bcc196..
INFO 18:24:40.682203903 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate bcc196.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:24:40.682784275 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate bcc196...
INFO 18:24:40.879610530 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate bcc196...
INFO 18:24:40.889102283 [routing::states::node node.rs:1695] Node(cf19e1..()) Added bcc196.. to routing table.
INFO 18:24:40.893351032 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:24:40.893444890 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   6 |
INFO 18:24:40.893495543 [routing::states::node node.rs:343] | Exact network size: 7                      |
INFO 18:24:40.893551206 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:26:05.816431744 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name 047a28...
INFO 18:26:05.882813432 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name 047a28...
INFO 18:26:10.101482888 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate 047a28..->e2916a..
INFO 18:26:10.182397231 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate e2916a.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:26:10.182949349 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate e2916a...
INFO 18:26:10.381056223 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate e2916a...
INFO 18:26:10.385271084 [routing::states::node node.rs:1695] Node(cf19e1..()) Added e2916a.. to routing table.
INFO 18:26:10.398035889 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:26:10.398158744 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   7 |
INFO 18:26:10.398207932 [routing::states::node node.rs:343] | Exact network size: 8                      |
INFO 18:26:10.398263275 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:26:41.982543064 [routing::states::node node.rs:2245] Node(cf19e1..()) Expecting candidate with old name 71d2dd...
INFO 18:26:42.096024950 [routing::states::node node.rs:2280] Node(cf19e1..()) Our section with Prefix() accepted candidate with old name 71d2dd...
INFO 18:26:46.407764199 [routing::states::node node.rs:1602] Node(cf19e1..()) Sending resource proof challenge to candidate 71d2dd..->0f8b34..
INFO 18:26:46.556718984 [routing::states::node node.rs:1389] Node(cf19e1..()) Candidate 0f8b34.. passed our challenge in 4 seconds. Sending approval to our section with Prefix().
INFO 18:26:46.557216354 [routing::states::node node.rs:2723] Node(cf19e1..()) Resource proof duration has finished. Voting to approve candidate 0f8b34...
INFO 18:26:46.727826067 [routing::states::node node.rs:1229] Node(cf19e1..()) Our section with Prefix() has approved candidate 0f8b34...
INFO 18:26:46.744930184 [routing::states::node node.rs:1695] Node(cf19e1..()) Added 0f8b34.. to routing table.
INFO 18:26:46.754546818 [routing::states::node node.rs:341]  -------------------------------------------- 
INFO 18:26:46.754709900 [routing::states::node node.rs:342] | Node(cf19e1..()) - Routing Table size:   8 |
INFO 18:26:46.754787229 [routing::states::node node.rs:343] | Exact network size: 9                      |
INFO 18:26:46.754876087 [routing::states::node node.rs:344]  -------------------------------------------- 
INFO 18:40:18.755555340 [routing::states::node node.rs:302] Node(cf19e1..()) - Connected clients: 1, cumulative: 1
INFO 18:40:18.757591846 [routing::states::node node.rs:3139] Node(cf19e1..()) Stats - Client total session traffic from V4(192.168.178.21) - 0
INFO 18:40:18.758002535 [routing::states::node node.rs:302] Node(cf19e1..()) - Connected clients: 0, cumulative: 1
INFO 18:40:19.768308038 [routing::states::node node.rs:302] Node(cf19e1..()) - Connected clients: 1, cumulative: 2
INFO 18:40:19.771381500 [routing::states::node node.rs:3139] Node(cf19e1..()) Stats - Client total session traffic from V4(127.0.0.1) - 0
INFO 18:40:19.771600090 [routing::states::node node.rs:302] Node(cf19e1..()) - Connected clients: 0, cumulative: 2

But it doesn’t change the outcome of the web_hosting app.

I don’t know if you have latest client binaries delivered by Maidsafe (with rate limiter feature). So, I have just relayed the invitation to you. You will need to download “SAFE Browser” and “Web Hosting Manager” and modify “SAFE Browser” as indicated above.

I’m a bit confused now. Does this invitation include access to the internal test network?

[update]

Hurray I’m in! :slight_smile:
Locally that is.

2 Likes

The invitation is a pointer to binaries for accessing Maidsafe test 17 network. My safe_vault repository is fork for building local networks compatible with these binaries.

We really don’t want to make that all public or it will cause us issues when we are trying to restrict the tests as we add security etc. Maybe best to keep these to yourself for the time being, otherwise we need to restart the network again if we get spammed during such tests. It’s not easy as we are trying to be as open as possible with an unfinished system. Hope you understand

6 Likes