95 Commits

Author SHA1 Message Date
36930be8e4 added write config support 2025-06-28 12:03:56 +01:00
be1f3308ca Merge branch 'master' of github.com:Akilan1999/p2p-rendering-computation 2025-06-07 14:07:53 +01:00
b3fcc10cd8 fixed p2prc docs 2025-06-07 14:06:55 +01:00
Akilan Selvacoumar
301a87e758 Update README.md 2025-06-06 20:43:47 +01:00
c9f4c8b5c7 added p2prc haskell logo 2025-06-06 17:00:19 +01:00
10b3c6bae3 fixed folder copied inside 2025-06-06 16:16:46 +01:00
d4e73e9d34 added manual installation documentation 2025-06-06 15:59:37 +01:00
4c7deb948f added haskell build instruction 2025-06-06 15:43:48 +01:00
54a675e0c0 changed command line argument for testing 2025-06-06 14:24:26 +01:00
3c3cfac5e2 fixed pass through custom information 2025-06-04 15:51:07 +01:00
6a24da74ef removed index.org~ from main directory 2025-06-03 15:22:05 +01:00
0fe1b0c622 fixed incorrect documentation use 2025-06-03 15:09:45 +01:00
7d3fc9b25f added generated docs html 2025-06-03 15:02:14 +01:00
Akilan Selvacoumar
6281b0f532 Merge pull request #128 from xecarlox94/nix
Nix template addition
2025-06-03 14:46:16 +01:00
1711b01979 finished documentation 2025-06-02 23:59:23 +01:00
438b4d0424 fixed coding bugs, refined text 2025-06-02 21:05:22 +01:00
22c5d61082 finished mvp for nix templating 2025-06-02 00:43:50 +01:00
a1bf13cac3 finishing automated setup 2025-06-02 00:37:28 +01:00
616bf76bda finishing automated setup 2025-06-02 00:31:29 +01:00
539bceee7d finishing automated setup 2025-06-02 00:31:04 +01:00
69b9ea3527 finishing automated setup 2025-06-02 00:11:59 +01:00
27f4615cd1 finishing automated setup 2025-06-02 00:07:53 +01:00
01e19d9a8c adding git actions 2025-06-02 00:03:24 +01:00
aafc0ebdd7 adding git actions 2025-06-01 23:54:25 +01:00
9de0b1b985 adding git actions 2025-06-01 23:52:00 +01:00
4c12d74789 adding git actions 2025-06-01 23:50:41 +01:00
f06df58a8a adding git actions 2025-06-01 23:47:11 +01:00
30004d59c6 adding git actions 2025-06-01 23:44:57 +01:00
4c8cde8857 adding git actions 2025-06-01 23:34:01 +01:00
994c52faf0 adding git actions 2025-06-01 23:25:41 +01:00
5e65381377 adding git actions 2025-06-01 20:57:38 +01:00
ff66d55153 adding git actions 2025-06-01 20:54:38 +01:00
50983dbb19 adding git actions 2025-06-01 20:50:49 +01:00
189edd73b1 adding git actions 2025-06-01 20:48:59 +01:00
27670f2408 adding git actions 2025-06-01 19:55:00 +01:00
b301a54fba adding git actions 2025-06-01 19:53:33 +01:00
05e29aab0d adding git actions 2025-06-01 19:47:17 +01:00
b883feb825 adding git actions 2025-06-01 19:44:43 +01:00
5059444d26 adding git actions 2025-06-01 19:42:23 +01:00
be3be96987 adding git actions 2025-06-01 19:39:32 +01:00
682424074f adding git actions 2025-06-01 19:36:41 +01:00
9d0ec3eba4 adding git actions 2025-06-01 19:34:32 +01:00
bf87217b9a adding git actions 2025-06-01 19:27:52 +01:00
e4638232d3 adding git actions 2025-06-01 19:24:56 +01:00
fb6c3ab091 adding new version for p2prc-hs bootstrapping 2025-06-01 15:12:16 +01:00
a59657a93b removing wasm file 2025-05-31 15:18:27 +01:00
30ea1f810a Merge remote-tracking branch 'refs/remotes/principal/master' 2025-05-31 11:11:00 +01:00
fe72625266 removed erroneous commands 2025-05-31 10:44:10 +01:00
d2030b7d74 updating lock versions 2025-05-31 00:17:59 +01:00
73a283a545 another attempt to test bootstrapping 2025-05-31 00:07:50 +01:00
623cfffb6a another attempt to test bootstrapping 2025-05-31 00:05:04 +01:00
3669fdfad2 adding initHaskellProject to bootstrap project 2025-05-30 23:56:03 +01:00
f39fab04ce adding initHaskellProject to bootstrap project 2025-05-30 23:41:34 +01:00
12ff0ceb37 adding initHaskellProject to bootstrap project 2025-05-30 23:36:27 +01:00
99c62a8dca adding initHaskellProject to bootstrap project 2025-05-30 23:32:49 +01:00
ce5c80a10e adding initHaskellProject to bootstrap project 2025-05-30 23:31:40 +01:00
46ef4e1db9 adding initHaskellProject to bootstrap project 2025-05-30 23:29:28 +01:00
14743f7f9b adding initHaskellProject to bootstrap project 2025-05-30 23:27:10 +01:00
a461c2da64 adding initHaskellProject to bootstrap project 2025-05-30 23:02:06 +01:00
99c9c41528 Documentation for Simulation added 2025-05-30 18:06:32 +01:00
0e260cf8e4 Made simulation simple 2025-05-30 17:10:54 +01:00
02e58493f5 added working version of simulator 2025-05-30 17:09:36 +01:00
a46ec3575c Simulation base code 2025-05-30 16:23:39 +01:00
2d55643dd9 removed heavy WASM binary 2025-05-30 11:59:32 +01:00
1ac0647d46 refactoring nix flake template 2025-05-30 00:04:43 +01:00
a07a412c72 temporary fix to test remote flake init 2025-05-29 23:52:14 +01:00
e04e918328 fixed haskell template 2025-05-29 23:36:11 +01:00
0bb949a1e2 fixed overlay and templates 2025-05-29 23:34:08 +01:00
cf6611087a adding haskell template providing dev/prod environments 2025-05-29 23:29:55 +01:00
b1a69189d3 adding haskell template providing dev/prod environments 2025-05-29 23:20:21 +01:00
ad76773111 adding haskell template providing dev/prod environments 2025-05-29 23:13:11 +01:00
ec46f74c4e adding haskell template providing dev/prod environments 2025-05-29 23:10:12 +01:00
3fb225676f fixed devshell environment 2025-05-29 00:23:42 +01:00
0e86459122 fixed p2prc-hs issue 2025-05-28 22:25:09 +01:00
77ebb76838 updating flake 2025-05-28 00:04:49 +01:00
85806ab035 refactoring flake code 2025-05-27 23:49:08 +01:00
9d92ecb202 working on template for haskell 2025-05-27 23:22:47 +01:00
5fa625160e improving overlays 2025-05-26 21:26:40 +01:00
403ad72ddb fixing environment 2025-05-26 21:08:11 +01:00
6446681719 renaming overlay file 2025-05-26 01:16:03 +01:00
d50534e270 improving executable exposure 2025-05-26 01:06:42 +01:00
8a2dfb7b5b fixed haskell development environment 2025-05-26 00:15:36 +01:00
0f4f52cb8b working on haskell development shell 2025-05-26 00:06:11 +01:00
cd8bb2df8c removed redundant code from flake 2025-05-25 23:45:44 +01:00
fa5b7a27d5 fixed overlays typo 2025-05-25 23:17:03 +01:00
21f0fc79c1 removed executable from haskell project 2025-05-25 22:07:26 +01:00
bebe2a02a0 improving nix flake for haskell library bootstrapping 2025-05-25 19:09:41 +01:00
7b0b2404b2 improving overlay 2025-05-24 17:59:16 +01:00
724daf5eda preparing overlays for build system 2025-05-24 16:58:08 +01:00
c262e612e1 preparing overlays for build system 2025-05-24 16:53:01 +01:00
257d91a432 fixing nix development and build environments 2025-05-21 23:11:56 +01:00
42cf9bd028 fixing nix env 2025-05-20 23:51:33 +01:00
7c34c5385a fix abstraction 2025-05-17 23:30:58 +01:00
d79c28c32f added kill docs script 2025-04-28 23:22:22 +01:00
Akilan Selvacoumar
7b0d36873b Merge pull request #127 from Akilan1999/add-root-node
Added feature to add root node
2025-04-28 23:17:49 +01:00
41 changed files with 2284 additions and 2194 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@@ -31,6 +31,7 @@ server/docker/containers/
# generic folder to ignore # generic folder to ignore
export/ export/
exports/
# Any testing file # Any testing file
*test *test

View File

@@ -15,7 +15,6 @@
|--------------|-----------------------------------------| |--------------|-----------------------------------------|
| Copyright | Copyright (C) 2006-2024 John MacFarlane | | Copyright | Copyright (C) 2006-2024 John MacFarlane |
| License | GNU GPL, version 2 or above | | License | GNU GPL, version 2 or above |
| Maintainer | John MacFarlane \<jgm@berkeley.edu\> |
| Stability | alpha | | Stability | alpha |
| Portability | portable | | Portability | portable |
| Safe Haskell | Safe-Inferred | | Safe Haskell | Safe-Inferred |

View File

@@ -18,23 +18,41 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gomod2nix": { "gomod2nix": {
"inputs": { "inputs": {
"flake-utils": [ "flake-utils": [
"p2prc", "p2prc-main",
"flake-utils" "flake-utils"
], ],
"nixpkgs": [ "nixpkgs": [
"p2prc", "p2prc-main",
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1733668782, "lastModified": 1745875161,
"narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=", "narHash": "sha256-0YkWCS13jpoo3+sX/3kcgdxBNt1VZTmvF+FhZb4rFKI=",
"owner": "nix-community", "owner": "nix-community",
"repo": "gomod2nix", "repo": "gomod2nix",
"rev": "514283ec89c39ad0079ff2f3b1437404e4cba608", "rev": "2cbd7fdd6eeab65c494cc426e18f4e4d2a5e35c0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -57,11 +75,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1735834308, "lastModified": 1748026106,
"narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=", "narHash": "sha256-6m1Y3/4pVw1RWTsrkAK2VMYSzG4MMIj7sqUy7o8th1o=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "6df24922a1400241dae323af55f30e4318a6ca65", "rev": "063f43f2dbdef86376cc29ad646c45c46e93234c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -71,28 +89,28 @@
"type": "github" "type": "github"
} }
}, },
"p2prc": { "p2prc-main": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils_2",
"gomod2nix": "gomod2nix", "gomod2nix": "gomod2nix",
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 0, "lastModified": 0,
"narHash": "sha256-f4e4JcQzBS+R+8HqJbQVeecInm2ggInJuSSymj6gtQo=", "narHash": "sha256-5qbaxawuLsg8iu9076zlAbLtTFSNJaTz7WERS5rWRxU=",
"path": "/nix/store/hmk8v0lxifsscll054qsdnzxqfql1998-source", "path": "/nix/store/lg694r23fzc6v5ai3qqcszq1fp44djg2-source",
"type": "path" "type": "path"
}, },
"original": { "original": {
"path": "/nix/store/hmk8v0lxifsscll054qsdnzxqfql1998-source", "path": "/nix/store/lg694r23fzc6v5ai3qqcszq1fp44djg2-source",
"type": "path" "type": "path"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"p2prc": "p2prc", "p2prc-main": "p2prc-main"
"utils": "utils"
} }
}, },
"systems": { "systems": {
@@ -124,24 +142,6 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@@ -1,24 +1,28 @@
{ {
description = "Nix flake for P2PRC Haskell library"; description = "Nix flake for P2PRC Haskell library";
inputs = { inputs = {
p2prc.url = "../../"; flake-utils.url = "github:numtide/flake-utils";
utils.url = "github:numtide/flake-utils"; p2prc-main.url = "../../";
}; };
outputs = { self, nixpkgs, utils, p2prc }: utils.lib.eachDefaultSystem (system: outputs = { nixpkgs, flake-utils, p2prc-main, ... }:
let flake-utils.lib.eachDefaultSystem (system:
pkgs = nixpkgs.legacyPackages.${system}; let
in pkgs = nixpkgs.legacyPackages.${system};
{ in
devShell = pkgs.mkShell { {
buildInputs = with pkgs; [
cabal-install packages.default = pkgs.haskellPackages.callPackage ./project.nix {};
haskell.compiler.ghc96
zlib.dev devShell = pkgs.mkShell {
p2prc.outputs.packages.${system}.default buildInputs = with pkgs; [
]; cabal2nix
}; cabal-install
} ghc
); zlib.dev
p2prc-main.packages.${system}.default
];
};
}
);
} }

View File

@@ -3,6 +3,7 @@ module API
( P2PRCapi(..) ( P2PRCapi(..)
, MapPortRequest(..) , MapPortRequest(..)
, p2prcAPI , p2prcAPI
, P2PRCommands(..)
) )
where where
@@ -13,6 +14,7 @@ import System.Process ( ProcessHandle )
import Data.Aeson ( FromJSON ) import Data.Aeson ( FromJSON )
import Error import Error
( IOEitherError ( IOEitherError
) )
@@ -49,15 +51,26 @@ data P2PRCapi
-- ^ List servers in network -- ^ List servers in network
, execMapPort :: MapPortRequest -> IOEitherError MapPortResponse , execMapPort :: MapPortRequest -> IOEitherError MapPortResponse
-- ^ Exposes and associates a local TCP port with a remote DNS address -- ^ Exposes and associates a local TCP port with a remote DNS address
, execMaybeMapPortAndAddCustomInformation :: Maybe MapPortRequest -> PassCustomInformation -> IOEitherError String
, execAddCustomInformation :: PassCustomInformation -> IOEitherError String
} }
-- | This defines the request required to create an association between a TCP socket port and a DNS server in the network. If successful, it makes a resource available in the network. -- | This defines the request required to create an association between a TCP socket port and a DNS server in the network. If successful, it makes a resource available in the network.
data MapPortRequest = data MapPortRequest =
MkMapPortRequest -- ^ P2PRC's port allocation request value MkMapPortRequest -- ^ P2PRC's port allocation request value
Int -- ^ TCP socket number Int -- ^ TCP socket number
String -- ^ Network domain name String -- ^ Network domain name
-- | This defined the custom information required to be passed through.
newtype PassCustomInformation =
MkAddCustomInformation
String
data P2PRCommands
= MapPort MapPortRequest
| CustomInformation PassCustomInformation
{-| {-|
This function intiates a pure P2PRC runtime state and builds up a 'P2PRCapi' API instance. It allows a developer to create computing orchestration algorithms using the API primitives. This function intiates a pure P2PRC runtime state and builds up a 'P2PRCapi' API instance. It allows a developer to create computing orchestration algorithms using the API primitives.
@@ -94,9 +107,15 @@ main =
{-# WARNING p2prcAPI "This function is currently unstable because the configuration reading is dependent on the following issue: https://github.com/Akilan1999/p2p-rendering-computation/issues/120" #-} {-# WARNING p2prcAPI "This function is currently unstable because the configuration reading is dependent on the following issue: https://github.com/Akilan1999/p2p-rendering-computation/issues/120" #-}
p2prcAPI :: P2PRCapi p2prcAPI :: P2PRCapi
p2prcAPI = p2prcAPI = let
execTemp :: Maybe MapPortRequest -> PassCustomInformation -> IOEitherError String
execTemp mp ci = undefined
in
MkP2PRCapi MkP2PRCapi
{ startServer = spawnProcP2PRC p2prcCmdName [ MkOptAtomic "--s" ] { startServer = spawnProcP2PRC p2prcCmdName [ MkOptAtomic "-s" ]
, execListServers = , execListServers =
execProcP2PRCParser [ MkOptAtomic "--ls" ] MkEmptyStdInput execProcP2PRCParser [ MkOptAtomic "--ls" ] MkEmptyStdInput
@@ -115,6 +134,9 @@ p2prcAPI =
] ]
MkEmptyStdInput MkEmptyStdInput
, execMaybeMapPortAndAddCustomInformation = execTemp
, execAddCustomInformation = execTemp Nothing
-- , execInitConfig = do -- , execInitConfig = do
-- confInitRes <- execProcP2PRC [ MkOptAtomic "--dc" ] MkEmptyStdInput -- confInitRes <- execProcP2PRC [ MkOptAtomic "--dc" ] MkEmptyStdInput

View File

@@ -116,5 +116,5 @@ eitherErrorDecode esa =
-- assumes the program is ran inside the haskell module in p2prc's repo -- assumes the program is ran inside the haskell module in p2prc's repo
-- assumes that last path segment is "haskell" and that p2prc binary's name is "p2p-rendering-computation" -- assumes that last path segment is "haskell" and that p2prc binary's name is "p2p-rendering-computation"
p2prcCmdName :: String p2prcCmdName :: String
p2prcCmdName = "p2prc" p2prcCmdName = "p2p-rendering-computation"

View File

@@ -2,6 +2,7 @@
module Engine module Engine
( runP2PRC ( runP2PRC
, fn
) )
where where
@@ -18,7 +19,7 @@ import System.Process ( terminateProcess )
import API import API
( P2PRCapi(..) ( P2PRCapi(..)
, MapPortRequest(..) , MapPortRequest(..)
, p2prcAPI , p2prcAPI, P2PRCommands
) )
@@ -58,14 +59,10 @@ import API
@ @
-} -}
runP2PRC runP2PRC
:: MapPortRequest -- ^ TCP Port Request :: [P2PRCommands] -- ^ TCP Port Request
-> IO () -> IO ()
runP2PRC runP2PRC [] = putStrLn ""
( MkMapPortRequest runP2PRC nonEmpty@(_:_) =
portNumber
domainName
)
=
let let
-- --
@@ -98,6 +95,7 @@ runP2PRC
-- , execInitConfig = execInitConfig -- , execInitConfig = execInitConfig
, execListServers = execListServers , execListServers = execListServers
, execMapPort = execMapPort , execMapPort = execMapPort
, execAddCustomInformation = _
} }
) = p2prcAPI ) = p2prcAPI
@@ -116,7 +114,8 @@ runP2PRC
case eitherStartProcessHandle of case eitherStartProcessHandle of
(Right startProcessHandle) -> do (Right startProcessHandle) ->
do
let sleepNSecs i = threadDelay (i * 1000000) let sleepNSecs i = threadDelay (i * 1000000)
@@ -125,7 +124,8 @@ runP2PRC
outputStr <- execListServers outputStr <- execListServers
print outputStr print outputStr
mapPortOut <- execMapPort $ MkMapPortRequest portNumber domainName -- mapPortOut <- execMapPort $ MkMapPortRequest portNumber domainName
mapPortOut <- execMapPort $ MkMapPortRequest undefined undefined
case mapPortOut of case mapPortOut of
(Right v) -> print v (Right v) -> print v
@@ -159,3 +159,7 @@ runP2PRC
when (toLower c /= 'q') $ exitOnQ exitF when (toLower c /= 'q') $ exitOnQ exitF
exitF exitF
fn :: undefined
fn = undefined

View File

@@ -52,6 +52,7 @@ module P2PRC
, P2PRCConfig(..) , P2PRCConfig(..)
, MapPortRequest(..) , MapPortRequest(..)
, MapPortResponse(..) , MapPortResponse(..)
, P2PRCommands(..)
-- ** Primitive data types -- ** Primitive data types
-- | These types represent the core data that is communicated between requests and the runtime. -- | These types represent the core data that is communicated between requests and the runtime.
@@ -83,6 +84,7 @@ import API
( MapPortRequest(..) ( MapPortRequest(..)
, P2PRCapi(..) , P2PRCapi(..)
, p2prcAPI , p2prcAPI
, P2PRCommands(..)
) )

View File

@@ -0,0 +1,13 @@
{ mkDerivation, aeson, base, bytestring, directory, lib, process
, text
}:
mkDerivation {
pname = "p2prc";
version = "0.1.0.0";
src = ./.;
libraryHaskellDepends = [
aeson base bytestring directory process text
];
description = "P2PRC haskell library";
license = "unknown";
}

View File

@@ -3,10 +3,14 @@ module Main where
import P2PRC import P2PRC
( runP2PRC ( runP2PRC
, MapPortRequest(MkMapPortRequest) , MapPortRequest(MkMapPortRequest)
, P2PRCommands(MapPort)
) )
main :: IO () main :: IO ()
main = main =let
in
runP2PRC runP2PRC
( MkMapPortRequest 8080 "jose.akilan.io" [
) ( MapPort $ MkMapPortRequest 8080 "jose.akilan.io")
]

File diff suppressed because it is too large Load Diff

View File

@@ -17,16 +17,16 @@
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: chapter1-introduction :CUSTOM_ID: chapter1-introduction
:END: :END:
This project focuses on creating a framework for running heavy computational tasks that a regular This project focuses on creating a framework for running heavy computational tasks that a regular
computer cannot handle easily. These tasks may include graphically demanding video games, rendering computer cannot handle easily. These tasks may include graphically demanding video games, rendering
3D animations, and performing complex protein folding simulations. The major focus of this project 3D animations, and performing complex protein folding simulations. The major focus of this project
is not on financial incentives but rather on building a robust and efficient peer-to-peer (P2P) is not on financial incentives but rather on building a robust and efficient peer-to-peer (P2P)
network to decentralise task execution and increase the computational bandwidth available for network to decentralise task execution and increase the computational bandwidth available for
such tasks. such tasks.
The P2PRC framework serves as a foundation for decentralised rendering and computation, The P2PRC framework serves as a foundation for decentralised rendering and computation,
providing insights into how tasks can be distributed efficiently across a network of peers. providing insights into how tasks can be distributed efficiently across a network of peers.
Leveraging the P2PRC approach, this project extends its capabilities to handle a Leveraging the P2PRC approach, this project extends its capabilities to handle a
wider range of computationally intensive tasks. wider range of computationally intensive tasks.
** Motivation ** Motivation
@@ -182,6 +182,110 @@ GLOBAL OPTIONS:
-------------- --------------
* Nix Flake
Nix is a growing ecosystem that allows flexibility on how you develop, build and package software and configurations. It brings all programming languages (and all other tooling) to an equal footing, despite deep design differences. More importantly, integrates all the "packaging" into the context of a "pure" function.
P2PRC aims to become a utility that can be used in various flexible manners and having Nix support is a good alternative to accomplish this goal.
Nix Flake is a format, within the Nix ecosystem, intentionally designed to encourage a standard in packaging distribution. The current packaging assumes that you have "nix flake" installed because it is currently an experimental feature of Nix.
** P2PRC core Go language repo
In case you want to develop, build or integrate using nix, you just need to run either "nix develop" or "nix run" from the command line locally in a cloned git repository or by running "nix run github:akilan1999/p2p-rendering-computation -- --help"
P2PRC library also is ready to be imported into other nix flakes. To accomplish that please make sure to override the target nixpkgs environment in the following manner;
#+begin_example
pkgs = import nixpkgs {
inherit system;
overlays = [
p2prc-flake.overlays.default
];
};
#+end_example
This will make the p2prc executable available in the environment of any application you use.
** P2PRC Haskell library
*** Nix:
The project is structured to provide language bindings to any programming language. The first one being supported in this manner is the Haskell programming language. It provides a bootstrapping script for a new Cabal project with p2prc binary available in the environment and, more relevantly, the Haskell library bindings available in the virtual environment cabal environment.
#+begin_example
nix run git+https://github.com/akilan1999/p2p-rendering-computation#initHaskellProject -- <PROJECT_NAME>
#+end_example
This will generate a new haskell project setup to automatically work with the p2prc development and running environment.
Once completed, you should go into the project directory and copy the nix flake template, necessary to define the project's environment.
#+begin_example
nix flake init -t github:akilan1999/p2p-rendering-computation#haskell
#+end_example
The previous command sets up the flake environment and its dependencies. It will look like the following
#+begin_example
{
description = "Start of Haskell P2PRC flake";
inputs =
{
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-util.url = "github:numtide/flake-utils";
p2prc-flake.url = "github:akilan1999/p2p-rendering-computation";
};
outputs = { nixpkgs, p2prc-flake, flake-utils, ... }:
(flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
p2prc-flake.overlays.default
p2prc-flake.overlays.bindings
];
};
in {
packages.default = pkgs.haskellPackages.callPackage ./cabal.nix { };
devShells.default = pkgs.haskellPackages.shellFor {
packages = p: [
(p.callPackage ./cabal.nix { })
];
buildInputs = with pkgs; [
p2prc-flake.packages.${system}.default
ghc
cabal2nix
cabal-install
];
shellHook = ''
cabal2nix . > ./cabal.nix
'';
};
}
));
}
#+end_example
The template uses Cabal2Nix which manages the Haskell virtual environment automatically. It applies the system overlays, sets up the shell environment for the project (updating the Cabal2Nix configuration) and packages the main executable.
* Using basic commands * Using basic commands
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: using-basic-commands :CUSTOM_ID: using-basic-commands
@@ -192,7 +296,7 @@ GLOBAL OPTIONS:
:END: :END:
#+begin_example #+begin_example
p2prc -s p2prc -s
#+end_example #+end_example
*** View server Specification *** View server Specification
@@ -220,7 +324,7 @@ p2prc --touch=<server ip address> -p <number of ports> --gpu
The docker id is present in the output where you create a container The docker id is present in the output where you create a container
#+begin_example #+begin_example
p2prc --rm=<server ip address> --id=<docker container id> p2prc --rm=<server ip address> --id=<docker container id>
#+end_example #+end_example
*** Adding servers to ip table *** Adding servers to ip table
@@ -228,7 +332,7 @@ p2prc --rm=<server ip address> --id=<docker container id>
:CUSTOM_ID: adding-servers-to-ip-table :CUSTOM_ID: adding-servers-to-ip-table
:END: :END:
#+begin_example #+begin_example
p2prc --as=<server ip address you want to add> p2prc --as=<server ip address you want to add>
#+end_example #+end_example
*** Update ip table *** Update ip table
@@ -236,7 +340,7 @@ p2prc --as=<server ip address you want to add>
:CUSTOM_ID: update-ip-table :CUSTOM_ID: update-ip-table
:END: :END:
#+begin_example #+begin_example
p2prc --us p2prc --us
#+end_example #+end_example
*** List Servers *** List Servers
@@ -244,7 +348,7 @@ p2prc --us
:CUSTOM_ID: list-servers :CUSTOM_ID: list-servers
:END: :END:
#+begin_example #+begin_example
p2prc --ls p2prc --ls
#+end_example #+end_example
*** View Network interfaces *** View Network interfaces
@@ -308,7 +412,7 @@ p2prc --group <group id>
:CUSTOM_ID: delete-container-from-group :CUSTOM_ID: delete-container-from-group
:END: :END:
#+begin_example #+begin_example
p2prc --rmcgroup --group <group id> --id <container id> p2prc --rmcgroup --group <group id> --id <container id>
#+end_example #+end_example
*** Delete entire group *** Delete entire group
@@ -332,7 +436,7 @@ p2prc --pp <repo link>
:CUSTOM_ID: deleting-plugin-from-the-plugin-directory :CUSTOM_ID: deleting-plugin-from-the-plugin-directory
:END: :END:
#+begin_example #+begin_example
p2prc --rp <plugin name> p2prc --rp <plugin name>
#+end_example #+end_example
*** Added custom metadata about the current node *** Added custom metadata about the current node
@@ -344,12 +448,12 @@ p2prc --amd "custom metadata"
#+end_example #+end_example
*** MapPort and link to domain name *** MapPort and link to domain name
Allows to expose remote ports from a machine in the P2P network. Allows to expose remote ports from a machine in the P2P network.
#+begin_example #+begin_example
p2prc --mp <port no to map> --dn <domain name to link Mapped port against> p2prc --mp <port no to map> --dn <domain name to link Mapped port against>
#+end_example #+end_example
**** MapPort in remote machine **** MapPort in remote machine
This is to ensure ports on remote machines on the P2PRC can be easily opened. This is to ensure ports on remote machines on the P2PRC can be easily opened.
#+begin_example #+begin_example
p2prc --mp <port no to map> --dn <domain name to link Mapped port against> --ra <remote server address> p2prc --mp <port no to map> --dn <domain name to link Mapped port against> --ra <remote server address>
#+end_example #+end_example
@@ -357,7 +461,7 @@ p2prc --mp <port no to map> --dn <domain name to link Mapped port against> --ra
-------------- --------------
*** Add root node *** Add root node
Adds a root node to P2RRC and overwrites all other nodes in the ip table. Adds a root node to P2RRC and overwrites all other nodes in the ip table.
To be only added before the network is started and with To be only added before the network is started and with
the intention of a fresh instance. the intention of a fresh instance.
#+begin_example #+begin_example
@@ -368,10 +472,10 @@ p2prc --arn --ip <root node ip address> -p <root node port no>
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: p2p-module-implementation :CUSTOM_ID: p2p-module-implementation
:END: :END:
The P2P module is for managing server information within the network. The P2P module is for managing server information within the network.
It maintains and updates the IP table, ensuring accuracy by preventing duplicates and removing It maintains and updates the IP table, ensuring accuracy by preventing duplicates and removing
entries for unreachable servers. Furthermore, the module conducts speed tests on the listed servers entries for unreachable servers. Furthermore, the module conducts speed tests on the listed servers
to determine upload and download speeds. This valuable information enables users to identify nearby to determine upload and download speeds. This valuable information enables users to identify nearby
servers with optimal performance, enhancing their overall network experience. servers with optimal performance, enhancing their overall network experience.
#+caption: UML diagram of P2P module #+caption: UML diagram of P2P module
@@ -415,7 +519,7 @@ configuration module.
"NAT": "<boolean representing if the node is behind NAT or not>", "NAT": "<boolean representing if the node is behind NAT or not>",
"EscapeImplementation": "<NAT traversal implementation>", "EscapeImplementation": "<NAT traversal implementation>",
"ProxyServer": "<If the node listed is acting as a proxy server>", "ProxyServer": "<If the node listed is acting as a proxy server>",
"UnSafeMode": <Unsafe mode if turned on will allow all nodes in the network public keys to be "UnSafeMode": <Unsafe mode if turned on will allow all nodes in the network public keys to be
added to that particular node>", added to that particular node>",
"PublicKey": "<Public key of that particular node>", "PublicKey": "<Public key of that particular node>",
"CustomInformation": "<custom information passed in through all the nodes in the network>" "CustomInformation": "<custom information passed in through all the nodes in the network>"
@@ -538,7 +642,7 @@ Below are a sample set of commands to open the bindings implementation.
# run # run
cd Bindings/ cd Bindings/
# list files # list files
ls ls
# search for file # search for file
Client.go Client.go
#+end_example #+end_example
@@ -653,7 +757,7 @@ cd Bindings/python/export/
# list files # list files
ls ls
# Expected output # Expected output
SharedObjects/ library.py requirements.txt SharedObjects/ library.py requirements.txt
#+end_src #+end_src
Above shows a generated folder which consists of a folder called Above shows a generated folder which consists of a folder called
@@ -668,7 +772,24 @@ called from other programming languages.
P2PRC officially supports Haskell bindings and will further support P2PRC officially supports Haskell bindings and will further support
project using Haskell to build orchestrators on top of P2PRC. project using Haskell to build orchestrators on top of P2PRC.
[[https://p2prc.akilan.io/Docs/haskell][Read more...]] **** Local machine without Nix
On the local machine we just create a folder as exports which copies the p2prc haskell bindings
and the latest build of the p2prc binary. After the binary is copied it runs the --dc in p2prc to setup the
basic p2prc configurations. To do the following just do:
#+begin_example
make haskell
#+end_example
**** Directory to enter into
#+begin_example
cd Bindings/Haskell/exports
#+end_example
**** Run
#+begin_example
- cabal build
- cabal run
#+end_example
[[https://p2prc.akilan.io/haskell][Read more...]]
* Config Implementation * Config Implementation
:PROPERTIES: :PROPERTIES:
@@ -690,18 +811,28 @@ configuration file was generated using JSON format.
#+begin_src json #+begin_src json
{ {
"MachineName": "pc-74-120.customer.ask4.lan", "MachineName": "Akilans-MacBook-Pro.local-975261X",
"IPTable": "/Users/akilan/Documents/p2p-rendering-computation/p2p/iptable/ip_table.json", "IPTable": "/Users/akilan/Documents/p2p-rendering-computation/p2p/iptable/ip_table.json",
"DockerContainers": "/Users/akilan/Documents/p2p-rendering-computation/server/docker/containers/", "DockerContainers": "/Users/akilan/Documents/p2p-rendering-computation/server/docker/containers/",
"DefaultDockerFile": "/Users/akilan/Documents/p2p-rendering-computation/server/docker/containers/docker-ubuntu-sshd/", "DefaultDockerFile": "/Users/akilan/Documents/p2p-rendering-computation/server/docker/containers/docker-ubuntu-sshd/",
"DockerRunLogs": "/tmp/",
"SpeedTestFile": "/Users/akilan/Documents/p2p-rendering-computation/p2p/50.bin", "SpeedTestFile": "/Users/akilan/Documents/p2p-rendering-computation/p2p/50.bin",
"IPV6Address": "", "IPV6Address": "",
"PluginPath": "/Users/akilan/Documents/p2p-rendering-computation/plugin/deploy", "PluginPath": "/Users/akilan/Documents/p2p-rendering-computation/plugin/deploy",
"TrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/trackcontainers.json", "TrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/trackcontainers.json",
"ServerPort": "8088", "ServerPort": "8088",
"ProxyPort": "",
"GroupTrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/grouptrackcontainers.json", "GroupTrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/grouptrackcontainers.json",
"FRPServerPort": "True", "FRPServerPort": true,
"BehindNAT": "True", "BehindNAT": true,
"IPTableKey": "QH3yQrGRe2X4",
"PublicKeyFile": "/Users/akilan/Documents/p2p-rendering-computation/p2prc.publicKey",
"PrivateKeyFile": "/Users/akilan/Documents/p2p-rendering-computation/p2prc.privateKey",
"PemFile": "/Users/akilan/Documents/p2p-rendering-computation/cert.pem",
"KeyFile": "/Users/akilan/Documents/p2p-rendering-computation/key.pem",
"BareMetal": false,
"UnsafeMode": false,
"Test": false,
"CustomConfig": null "CustomConfig": null
} }
#+end_src #+end_src
@@ -732,7 +863,7 @@ The Abstractions package consists of black-boxed functions for P2PRC.
- =ViewIPTable()=: View the IP table which about nodes in the network. - =ViewIPTable()=: View the IP table which about nodes in the network.
- =UpdateIPTable()=: Force update IP table to learn about new nodes - =UpdateIPTable()=: Force update IP table to learn about new nodes
faster. faster.
* NAT Traversal * NAT Traversal
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: nat-traversal :CUSTOM_ID: nat-traversal
@@ -783,17 +914,55 @@ func main() {
} }
} }
#+end_src #+end_src
* Simulation
The simulation library is a really simple library to locally
run serveral instances of P2PRC to test the functionality
of sending information through the network and particularly
the Node discovery mechanism. There are current 5 commands
which can be used as shown below:
** Command
- ***P2PRC instances "number of instances"*** : Starts p2prc processes
based on the number of number instances provided. This includes
creating temporary folders with independent IPTables.
- ***Start all instances "number of instances"***: Starts the P2PRC instances
created. This function enters into the instance directory and runs
```p2prc -s &``` with a delay for approximately 3 seconds. This allows
each independent created node to learn about each other.
- ***IP Tables after Started number of instances"***: Prints the IPTable of
each independant node.
- ***Remove all test files***: Removes all test files created which needed for
testing the generated P2PRC instances.
- ***Kill all instances***: Kills all the background P2PRC instances created.
** Sample bash instructions
#+begin_src bash
# Unset default P2PRC env path
unset P2PRC
# Run with 2 nodes
P2PRC_instances 3
# Start instances
Start_all_instances 3
# List ip tables of nodes started
IP_Tables_after_Started 3
# Remove test files created
Remove_all_test_files
# Kill all instances
Kill_all_instances
#+end_src
Source code: https://github.com/Akilan1999/p2p-rendering-computation/blob/master/Simulation/simulation.sh
* Blog posts * Blog posts
** Self host within 5 minutes any program ** Self host within 5 minutes any program
- Author: [[http://akilan.io/][Akilan Selvacoumar]] - Author: [[http://akilan.io/][Akilan Selvacoumar]]
- Date: 28-01-2025 - Date: 28-01-2025
- Video tutorial: - Video tutorial:
#+attr_html: :class video #+attr_html: :class video
[[https://youtu.be/rN4SiVowg5E][https://i3.ytimg.com/vi/rN4SiVowg5E/maxresdefault.jpg]] [[https://youtu.be/rN4SiVowg5E][https://i3.ytimg.com/vi/rN4SiVowg5E/maxresdefault.jpg]]
@@ -805,7 +974,7 @@ quickly do a map port and domain name mapping in a single command.
Let's try to setup a really easy program (Let's do with Linkwarden Let's try to setup a really easy program (Let's do with Linkwarden
with docker compose :) ). This is under the assumption you have docker with docker compose :) ). This is under the assumption you have docker
compose installed on your local machine. compose installed on your local machine.
**** Let's run Linkwarden using docker compose and P2PRC **** Let's run Linkwarden using docker compose and P2PRC
[[https://docs.linkwarden.app/self-hosting/installation][Installation instructions]]: [[https://docs.linkwarden.app/self-hosting/installation][Installation instructions]]:
#+BEGIN_SRC #+BEGIN_SRC
@@ -861,14 +1030,14 @@ A entry 217.76.63.222
Your done now just head to the DOMAIN NAME you added. Your done now just head to the DOMAIN NAME you added.
ex: https://linkwarden.akilan.io ex: https://linkwarden.akilan.io
* Ideas for future potencial features * Ideas for future potencial features
@@ -905,7 +1074,7 @@ network nodes.
- Spin servers on node not running P2PRC using the P2PRC standard abstractions. - Spin servers on node not running P2PRC using the P2PRC standard abstractions.
- Auto-setup P2PRC nodes with just SSH access via potencially a DSL. - Auto-setup P2PRC nodes with just SSH access via potencially a DSL.
- A neat use case for CHERI for instance would be use the architecture to run light - A neat use case for CHERI for instance would be use the architecture to run light
weight hypervisors. weight hypervisors.
*** Implementation *** Implementation
- To use implementations similar to [[https://linux.die.net/man/1/socat][socat]] to ensure we can bind address of local - To use implementations similar to [[https://linux.die.net/man/1/socat][socat]] to ensure we can bind address of local
nodes to a node running P2PRC and the node running P2PRC can do a local map port. nodes to a node running P2PRC and the node running P2PRC can do a local map port.

1
Docs/kill-docs.sh Normal file
View File

@@ -0,0 +1 @@
kill -9 $(lsof -t -i:8083)

View File

@@ -14,6 +14,9 @@ sharedObjects:
python: python:
sh build-python-package.sh sh build-python-package.sh
haskell:
sh build-haskell.sh
clean: clean:
go clean -modcache go clean -modcache
rm -fr .go-build vendor result* rm -fr .go-build vendor result*

View File

@@ -1,5 +1,5 @@
> [!TIP] > [!TIP]
> Haskell bindings supported!: [Bindings documentaton](https://p2prc.akilan.io/Docs/haskell/P2PRC.html) > Haskell bindings supported!: [Bindings documentaton](https://p2prc.akilan.io/haskell/P2PRC.html)
> [!NOTE] > [!NOTE]
> Fixing documentation to latest changes. If you have any questions setting up P2PRC either [create an issue](https://github.com/Akilan1999/p2p-rendering-computation/issues/new/choose) or send me an email (me AT akilan dot io). > Fixing documentation to latest changes. If you have any questions setting up P2PRC either [create an issue](https://github.com/Akilan1999/p2p-rendering-computation/issues/new/choose) or send me an email (me AT akilan dot io).
@@ -57,7 +57,7 @@ alias p2prc='env P2PRC=<path you want the configs to exist in> nix run github:ak
```bash ```bash
p2prc -h p2prc -h
``` ```
Read more on command usage: https://p2prc.akilan.io/Docs/#using-basic-commands Read more on command usage: https://p2prc.akilan.io/#using-basic-commands
## Latest tutorial ## Latest tutorial
[![IMAGE ALT TEXT](https://i.ytimg.com/vi/OMwCpedu5cs/hqdefault.jpg)](https://www.youtube.com/watch?v=OMwCpedu5cs") [![IMAGE ALT TEXT](https://i.ytimg.com/vi/OMwCpedu5cs/hqdefault.jpg)](https://www.youtube.com/watch?v=OMwCpedu5cs")
@@ -66,7 +66,7 @@ Read more on command usage: https://p2prc.akilan.io/Docs/#using-basic-commands
## Documentation ## Documentation
### We have documented to our best effort the basics of p2prc: [Link](https://p2prc.akilan.io/Docs/) ### We have documented to our best effort the basics of p2prc: [Link](https://p2prc.akilan.io/)
<!-- ## Table of contents in the current README --> <!-- ## Table of contents in the current README -->
<!-- 1. [Introduction](#Introduction) <!-- 1. [Introduction](#Introduction)

132
Simulation/simulation.sh Normal file
View File

@@ -0,0 +1,132 @@
# Unset P2PRC
unset P2PRC
# Create P2PRC instances
# Parameters:
# 1. Number of nodes
P2PRC_instances() {
local rootPort=""
for (( counter=0; counter<$1; counter++ )); do
mkdir test-$counter/
cd test-$counter/
# Generate a new random port
NEW_PORT=$(shuf -i 2000-65000 -n 1)
# Initialize node
p2prc --dc
# Save rootPort from the first node
if [[ $counter -eq 0 ]]; then
rootPort=$NEW_PORT
fi
echo "Root port: $rootPort"
echo "This node's port: $NEW_PORT"
# Set root port
p2prc --arn --ip 0.0.0.0 -p "$rootPort"
# add current node
p2prc --as 0.0.0.0 -p "$NEW_PORT"
# Replace the ServerPort in config
# test-$counter
sed -i.bak "s/\"ServerPort\": \"[0-9]*\"/\"ServerPort\": \"$NEW_PORT\"/" "./config.json"
sed -i.bak "s/\"Test\": false/\"Test\": true/" "./config.json"
sed -i.bak "s/\"BehindNAT\": true/\"BehindNAT\": false/" "./config.json"
# sed -i.bak "s/\"MachineName\": "Akilans-MacBook-Pro.local-J2UbbkF"/\"MachineName\": \"test-$counter\"/" "./config.json"
cat config.json
# List services
p2prc --ls
cd ..
done
# remove all instances after testing (optional — might want to comment this during testing)
# rm -rf test-*
}
# Arrays of process IDs
pids=()
# Calls all the P2PRC instances created for testing
Start_all_instances() {
for (( counter=0; counter<$1; counter++ )); do
cd test-$counter/
# Start P2PRC as background process
p2prc -s &
pids+=($!)
sleep 3
cd ..
done
}
# Kills all P2PRC instances
Kill_all_instances() {
for pid in "${pids[@]}"; do
kill "$pid"
echo "Killed process $pid"
done
}
# Removes all test files created
Remove_all_test_files() {
rm -rf test-*
}
# List ip tables of all nodes started
IP_Tables_after_Started() {
for (( counter=0; counter<$1; counter++ )); do
cd test-$counter/
p2prc --ls
cd ..
done
}
Update_All_IP_Tables() {
for (( counter=0; counter<$1; counter++ )); do
cd test-$counter/
p2prc --us
cd ..
done
}
# Test function for the root to custom information
# through the network
Send_Information() {
ls
cd test-2/
p2prc --amd "test message"
cd ..
}
# ---------------- Work flow test ---------------
# Unset default P2PRC env path
unset P2PRC
# Run with 2 nodes
P2PRC_instances 3
## Start instances
Start_all_instances 3
# Send test information from node 1
sleep 10
Send_Information
sleep 10
## List ip tables of nodes started
IP_Tables_after_Started 3
#
## Remove test files created
Remove_all_test_files
## Kill all instances
Kill_all_instances

View File

@@ -1,109 +1,108 @@
package abstractions package abstractions
import "C"
import ( import (
"github.com/Akilan1999/p2p-rendering-computation/client" "github.com/Akilan1999/p2p-rendering-computation/client"
"github.com/Akilan1999/p2p-rendering-computation/client/clientIPTable" "github.com/Akilan1999/p2p-rendering-computation/client/clientIPTable"
Config "github.com/Akilan1999/p2p-rendering-computation/config" Config "github.com/Akilan1999/p2p-rendering-computation/config"
"github.com/Akilan1999/p2p-rendering-computation/config/generate" "github.com/Akilan1999/p2p-rendering-computation/config/generate"
"github.com/Akilan1999/p2p-rendering-computation/p2p" "github.com/Akilan1999/p2p-rendering-computation/p2p"
"github.com/Akilan1999/p2p-rendering-computation/server" "github.com/Akilan1999/p2p-rendering-computation/server"
"github.com/Akilan1999/p2p-rendering-computation/server/docker" "github.com/Akilan1999/p2p-rendering-computation/server/docker"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"os" "os"
) )
// Init Initialises p2prc // Init Initialises p2prc
func Init(customConfig interface{}) (config *Config.Config, err error) { func Init(customConfig interface{}) (config *Config.Config, err error) {
// Get config file path // Get config file path
// Checks P2PRC path initially // Checks P2PRC path initially
// - Get PATH if environment varaible // - Get PATH if environment varaible
path, err := Config.GetPathP2PRC("P2PRC") path, err := Config.GetPathP2PRC("P2PRC")
if err != nil { if err != nil {
return return
} }
// check if the config file exists // check if the config file exists
if _, err = os.Stat(path + "config.json"); err != nil { if _, err = os.Stat(path + "config.json"); err != nil {
// Initialize with base p2prc config files // Initialize with base p2prc config files
// set the config file with default paths // set the config file with default paths
config, err = generate.SetDefaults("P2PRC", false, customConfig, false) config, err = generate.SetDefaults("P2PRC", false, customConfig, false)
if err != nil { if err != nil {
return return
} }
} else { } else {
// If the configs are available then use them over generating new ones. // If the configs are available then use them over generating new ones.
config, err = Config.ConfigInit(nil, nil) config, err = Config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return return
} }
} }
return return
} }
// Start p2prc in a server mode // Start p2prc in a server mode
func Start() (*gin.Engine, error) { func Start() (*gin.Engine, error) {
engine, err := server.Server() engine, err := server.Server()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return engine, nil return engine, nil
} }
// MapPort Creates a reverse proxy connection and maps the appropriate port // MapPort Creates a reverse proxy connection and maps the appropriate port
func MapPort(port string, domainName string, serverAddress string) (response *client.ResponseMAPPort, err error) { func MapPort(port string, domainName string, serverAddress string) (response *client.ResponseMAPPort, err error) {
response, err = client.MAPPort(port, domainName, serverAddress) response, err = client.MAPPort(port, domainName, serverAddress)
return return
} }
// StartContainer Starts docker container on the remote machine // StartContainer Starts docker container on the remote machine
func StartContainer(IP string) (container *docker.DockerVM, err error) { func StartContainer(IP string) (container *docker.DockerVM, err error) {
container, err = client.StartContainer(IP, 0, false, "", "") container, err = client.StartContainer(IP, 0, false, "", "")
return return
} }
// RemoveContainer Removes docker container based on the IP address and ID // RemoveContainer Removes docker container based on the IP address and ID
// provided // provided
func RemoveContainer(IP string, ID string) error { func RemoveContainer(IP string, ID string) error {
return client.RemoveContianer(IP, ID) return client.RemoveContianer(IP, ID)
} }
// GetSpecs Get spec information about the remote server // GetSpecs Get spec information about the remote server
func GetSpecs(IP string) (specs *server.SysInfo, err error) { func GetSpecs(IP string) (specs *server.SysInfo, err error) {
specs, err = client.GetSpecs(IP) specs, err = client.GetSpecs(IP)
return return
} }
// ViewIPTable View information of nodes in the network // ViewIPTable View information of nodes in the network
func ViewIPTable() (table *p2p.IpAddresses, err error) { func ViewIPTable() (table *p2p.IpAddresses, err error) {
table, err = p2p.ReadIpTable() table, err = p2p.ReadIpTable()
return return
} }
// UpdateIPTable Force updates IP tables based on new // UpdateIPTable Force updates IP tables based on new
// new nodes discovered in the network // new nodes discovered in the network
func UpdateIPTable() (err error) { func UpdateIPTable() (err error) {
return clientIPTable.UpdateIpTableListClient() return clientIPTable.UpdateIpTableListClient()
} }
// AddCustomInformation allows to pass custom information // AddCustomInformation allows to pass custom information
// through the network to which can be listened on // through the network to which can be listened on
// all peers in the network to execute a task. // all peers in the network to execute a task.
func AddCustomInformation(information string) error { func AddCustomInformation(information string) error {
return clientIPTable.AddCustomInformationToIPTable(information) return clientIPTable.AddCustomInformationToIPTable(information)
} }
// AddRootNode Adds root node to the network by using defaults except for // AddRootNode Adds root node to the network by using defaults except for
// ip address and port no. Supports only IPV4 as of now. // ip address and port no. Supports only IPV4 as of now.
func AddRootNode(rootIP string, portNo string) error { func AddRootNode(rootIP string, portNo string) error {
var rootNode []p2p.IpAddress var rootNode []p2p.IpAddress
rootNode = append(rootNode, p2p.IpAddress{ rootNode = append(rootNode, p2p.IpAddress{
Name: "", Name: "",
Ipv4: rootIP, Ipv4: rootIP,
ServerPort: portNo, ServerPort: portNo,
NAT: false, NAT: false,
}) })
return generate.GenerateIPTableFile(rootNode) return generate.GenerateIPTableFile(rootNode)
} }

BIN
artwork/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 KiB

31
build-haskell.sh Normal file
View File

@@ -0,0 +1,31 @@
# This bash script consists of the manaul build script of haskell
go build .
# Unset p2prc path
unset p2prc
# Enter the haskell project
cd Bindings/haskell
# Remove exsisting export build
rm -rf exports
# Create exports directory
mkdir exports
# Copy current haskell project into the folder
cp -rf . exports/
# Copy p2prc binary inside haskell folder
cp ../../p2p-rendering-computation exports/
cd exports
# remove exports directory copied inside exports
rm -rf exports/
# Set p2prc variable
echo export P2PRC=$PWD
export P2PRC=$PWD
./p2p-rendering-computation --dc
echo "Run the following commands in the directory: ${PWD}"
echo "To build: cabal build"
echo "To run sample program: cabal run"

View File

@@ -1,40 +1,40 @@
package clientIPTable package clientIPTable
import ( import (
"errors" "errors"
"github.com/Akilan1999/p2p-rendering-computation/config" "github.com/Akilan1999/p2p-rendering-computation/config"
"github.com/Akilan1999/p2p-rendering-computation/p2p" "github.com/Akilan1999/p2p-rendering-computation/p2p"
) )
func AddCustomInformationToIPTable(text string) error { func AddCustomInformationToIPTable(text string) error {
// Get config information // Get config information
Config, err := config.ConfigInit(nil, nil) Config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return err return err
} }
// Get IPTable information // Get IPTable information
table, err := p2p.ReadIpTable() table, err := p2p.ReadIpTable()
if err != nil { if err != nil {
return err return err
} }
found := false found := false
for i, _ := range table.IpAddress { for i, _ := range table.IpAddress {
if table.IpAddress[i].Name == Config.MachineName { if table.IpAddress[i].Name == Config.MachineName {
table.IpAddress[i].CustomInformation = text table.IpAddress[i].CustomInformation = text
found = true found = true
} }
} }
if found { if found {
table.WriteIpTable() table.WriteIpTable()
// update IPTable after modified entry // update IPTable after modified entry
go UpdateIpTableListClient() UpdateIpTableListClient()
} else { } else {
return errors.New("start server with p2prc -s as the server is currently not running") return errors.New("start server with p2prc -s as the server is currently not running")
} }
return nil return nil
} }

View File

@@ -1,16 +1,16 @@
package clientIPTable package clientIPTable
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/Akilan1999/p2p-rendering-computation/config" "github.com/Akilan1999/p2p-rendering-computation/config"
"github.com/Akilan1999/p2p-rendering-computation/p2p" "github.com/Akilan1999/p2p-rendering-computation/p2p"
"io" "io"
"io/ioutil" "io/ioutil"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"os" "os"
"sync" "sync"
) )
var mu sync.Mutex var mu sync.Mutex
@@ -18,146 +18,147 @@ var mu sync.Mutex
// UpdateIpTable Does the following to update it's IP table // UpdateIpTable Does the following to update it's IP table
func UpdateIpTable(IpAddress string, serverPort string, wg *sync.WaitGroup) error { func UpdateIpTable(IpAddress string, serverPort string, wg *sync.WaitGroup) error {
config, err := config.ConfigInit(nil, nil) config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return err return err
} }
client := http.Client{} client := http.Client{}
var resp []byte var resp []byte
version := p2p.Ip4or6(IpAddress) version := p2p.Ip4or6(IpAddress)
if version == "version 6" { if version == "version 6" {
resp, err = UploadMultipartFile(client, "http://["+IpAddress+"]:"+serverPort+"/IpTable", "json", config.IPTable) resp, err = UploadMultipartFile(client, "http://["+IpAddress+"]:"+serverPort+"/IpTable", "json", config.IPTable)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
resp, err = UploadMultipartFile(client, "http://"+IpAddress+":"+serverPort+"/IpTable", "json", config.IPTable) resp, err = UploadMultipartFile(client, "http://"+IpAddress+":"+serverPort+"/IpTable", "json", config.IPTable)
if err != nil { if err != nil {
return err return err
} }
} }
if resp == nil { if resp == nil {
return nil return nil
} }
//resp, err := SendPostRequest("http://"+IpAddress+":8088/IpTable", //resp, err := SendPostRequest("http://"+IpAddress+":8088/IpTable",
// config.IPTable, // config.IPTable,
// "json") // "json")
// //
//if err != nil { //if err != nil {
// return err // return err
//} //}
var ipStruct p2p.IpAddresses var ipStruct p2p.IpAddresses
json.Unmarshal(resp, &ipStruct) json.Unmarshal(resp, &ipStruct)
// Updates IP table based on information provided // Updates IP table based on information provided
// by the server // by the server
if len(ipStruct.IpAddress) > 0 { if len(ipStruct.IpAddress) > 0 {
err = ipStruct.SpeedTestUpdatedIPTable() err = ipStruct.SpeedTestUpdatedIPTable()
if err != nil { if err != nil {
return err return err
} }
} }
// Not required to update IP table as speed test updates the IP Table // Not required to update IP table as speed test updates the IP Table
//err = ipStruct.WriteIpTable() //err = ipStruct.WriteIpTable()
//if err != nil { //if err != nil {
// return err // return err
//} //}
wg.Done() wg.Done()
return nil return nil
} }
// UpdateIpTableListClient updates IP tables (Default 3 hops) based on server information available // UpdateIpTableListClient updates IP tables (Default 3 hops) based on server information available
// on the ip tables // on the ip tables
func UpdateIpTableListClient() error { func UpdateIpTableListClient() error {
// Get config information // Get config information
Config, err := config.ConfigInit(nil, nil) Config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return err return err
} }
// Ensure that the IP Table has Node pingable // Ensure that the IP Table has Node pingable
err = p2p.LocalSpeedTestIpTable() err = p2p.LocalSpeedTestIpTable()
if err != nil { if err != nil {
return err return err
} }
// IP addresses to not append to struct due to // IP addresses to not append to struct due to
// duplication // duplication
Addresses, err := p2p.ReadIpTable() Addresses, err := p2p.ReadIpTable()
var DoNotRead p2p.IpAddresses var DoNotRead p2p.IpAddresses
currentIPV4, err := p2p.CurrentPublicIP() currentIPV4, err := p2p.CurrentPublicIP()
if err != nil { if err != nil {
return err return err
} }
var w sync.WaitGroup var w sync.WaitGroup
// Run loop 2 times // Run loop 2 times
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
// Gets information from IP table // Gets information from IP table
Addresses, err = p2p.ReadIpTable() Addresses, err = p2p.ReadIpTable()
if err != nil { if err != nil {
return err return err
} }
//DoNotRead.IpAddress = append(DoNotRead.IpAddress, PublicIP) //DoNotRead.IpAddress = append(DoNotRead.IpAddress, PublicIP)
// Updates IP table based on server IP table // Updates IP table based on server IP table
for j := range Addresses.IpAddress { for j := range Addresses.IpAddress {
Exists := false Exists := false
// If the address is local then add to the local list // If the address is local then add to the local list
if currentIPV4 == Addresses.IpAddress[j].Ipv4 && Addresses.IpAddress[j].ServerPort == Config.ServerPort { if (currentIPV4 == Addresses.IpAddress[j].Ipv4 && !Config.Test) && Addresses.IpAddress[j].ServerPort == Config.ServerPort {
Exists = true Exists = true
} }
// Check if IP addresses is there in the struct DoNotRead // Check if IP addresses is there in the struct DoNotRead
for k := range DoNotRead.IpAddress { for k := range DoNotRead.IpAddress {
if (DoNotRead.IpAddress[k].Ipv4 == Addresses.IpAddress[j].Ipv4 && DoNotRead.IpAddress[k].ServerPort == Addresses.IpAddress[j].ServerPort) || (DoNotRead.IpAddress[k].Ipv6 != "" && Addresses.IpAddress[j].Ipv6 == DoNotRead.IpAddress[k].Ipv6) { if (DoNotRead.IpAddress[k].Ipv4 == Addresses.IpAddress[j].Ipv4 && DoNotRead.IpAddress[k].ServerPort == Addresses.IpAddress[j].ServerPort) || (DoNotRead.IpAddress[k].Ipv6 != "" && Addresses.IpAddress[j].Ipv6 == DoNotRead.IpAddress[k].Ipv6) {
Exists = true Exists = true
break break
} }
} }
// If the struct exists then continues // If the struct exists then continues
if Exists { if Exists {
continue continue
} }
w.Add(1) w.Add(1)
if Addresses.IpAddress[j].Ipv6 != "" { //if Addresses.IpAddress[j].Ipv6 != "" {
go UpdateIpTable(Addresses.IpAddress[j].Ipv6, Addresses.IpAddress[j].ServerPort, &w) // go UpdateIpTable(Addresses.IpAddress[j].Ipv6, Addresses.IpAddress[j].ServerPort, &w)
} else if Addresses.IpAddress[j].Ipv4 != currentIPV4 { //} else if Addresses.IpAddress[j].Ipv4 != currentIPV4 {
go UpdateIpTable(Addresses.IpAddress[j].Ipv4, Addresses.IpAddress[j].ServerPort, &w) // //go UpdateIpTable(Addresses.IpAddress[j].Ipv4, Addresses.IpAddress[j].ServerPort, &w)
} //}
w.Wait() go UpdateIpTable(Addresses.IpAddress[j].Ipv4, Addresses.IpAddress[j].ServerPort, &w)
w.Wait()
//Appends server1 IP address to variable DoNotRead //Appends server1 IP address to variable DoNotRead
DoNotRead.IpAddress = append(DoNotRead.IpAddress, Addresses.IpAddress[j]) DoNotRead.IpAddress = append(DoNotRead.IpAddress, Addresses.IpAddress[j])
} }
} }
return nil return nil
} }
func RemoveOfflineNodes() error { func RemoveOfflineNodes() error {
// Ensure that the IP Table has Node pingable // Ensure that the IP Table has Node pingable
err := p2p.LocalSpeedTestIpTable() err := p2p.LocalSpeedTestIpTable()
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// SendPostRequest Sends a file as a // SendPostRequest Sends a file as a
@@ -207,58 +208,58 @@ func RemoveOfflineNodes() error {
//} //}
func UploadMultipartFile(client http.Client, uri, key, path string) ([]byte, error) { func UploadMultipartFile(client http.Client, uri, key, path string) ([]byte, error) {
body, writer := io.Pipe() body, writer := io.Pipe()
req, err := http.NewRequest(http.MethodPost, uri, body) req, err := http.NewRequest(http.MethodPost, uri, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mwriter := multipart.NewWriter(writer) mwriter := multipart.NewWriter(writer)
req.Header.Add("Content-Type", mwriter.FormDataContentType()) req.Header.Add("Content-Type", mwriter.FormDataContentType())
errchan := make(chan error) errchan := make(chan error)
go func() { go func() {
defer close(errchan) defer close(errchan)
defer writer.Close() defer writer.Close()
defer mwriter.Close() defer mwriter.Close()
w, err := mwriter.CreateFormFile(key, path) w, err := mwriter.CreateFormFile(key, path)
if err != nil { if err != nil {
errchan <- err errchan <- err
return return
} }
in, err := os.Open(path) in, err := os.Open(path)
if err != nil { if err != nil {
errchan <- err errchan <- err
return return
} }
defer in.Close() defer in.Close()
if written, err := io.Copy(w, in); err != nil { if written, err := io.Copy(w, in); err != nil {
errchan <- fmt.Errorf("error copying %s (%d bytes written): %v", path, written, err) errchan <- fmt.Errorf("error copying %s (%d bytes written): %v", path, written, err)
return return
} }
if err := mwriter.Close(); err != nil { if err := mwriter.Close(); err != nil {
errchan <- err errchan <- err
return return
} }
}() }()
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
content, err := ioutil.ReadAll(resp.Body) content, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return content, nil return content, nil
} }

View File

@@ -5,6 +5,7 @@ import (
"github.com/Akilan1999/p2p-rendering-computation/abstractions" "github.com/Akilan1999/p2p-rendering-computation/abstractions"
"github.com/Akilan1999/p2p-rendering-computation/client" "github.com/Akilan1999/p2p-rendering-computation/client"
"github.com/Akilan1999/p2p-rendering-computation/client/clientIPTable" "github.com/Akilan1999/p2p-rendering-computation/client/clientIPTable"
Config "github.com/Akilan1999/p2p-rendering-computation/config"
"github.com/Akilan1999/p2p-rendering-computation/config/generate" "github.com/Akilan1999/p2p-rendering-computation/config/generate"
"github.com/Akilan1999/p2p-rendering-computation/p2p" "github.com/Akilan1999/p2p-rendering-computation/p2p"
"github.com/Akilan1999/p2p-rendering-computation/plugin" "github.com/Akilan1999/p2p-rendering-computation/plugin"
@@ -232,6 +233,12 @@ var CliAction = func(ctx *cli.Context) error {
standardOutput(err, nil) standardOutput(err, nil)
} }
// display configuration
if ViewConfig {
config, err := Config.ConfigInit(nil, nil)
standardOutput(err, config)
}
return nil return nil
} }

View File

@@ -43,6 +43,7 @@ var (
AddMetaData string AddMetaData string
AddRootNode bool AddRootNode bool
IP string IP string
ViewConfig bool
) )
var AppConfigFlags = []cli.Flag{ var AppConfigFlags = []cli.Flag{
@@ -288,4 +289,11 @@ var AppConfigFlags = []cli.Flag{
EnvVars: []string{"IP"}, EnvVars: []string{"IP"},
Destination: &IP, Destination: &IP,
}, },
&cli.BoolFlag{
Name: "ViewConfig",
Aliases: []string{"vc"},
Usage: "View config file",
EnvVars: []string{"VC"},
Destination: &ViewConfig,
},
} }

View File

@@ -38,7 +38,9 @@ type Config struct {
KeyFile string KeyFile string
BareMetal bool BareMetal bool
UnsafeMode bool UnsafeMode bool
Test bool
CustomConfig interface{} CustomConfig interface{}
ConfigPath string
//NetworkInterface string //NetworkInterface string
//NetworkInterfaceIPV6Index int //NetworkInterfaceIPV6Index int
} }

View File

@@ -95,6 +95,8 @@ func SetDefaults(envName string, forceDefault bool, CustomConfig interface{}, No
Defaults.PrivateKeyFile = defaultPath + "p2prc.privateKey" Defaults.PrivateKeyFile = defaultPath + "p2prc.privateKey"
Defaults.BareMetal = false Defaults.BareMetal = false
Defaults.UnsafeMode = false Defaults.UnsafeMode = false
Defaults.Test = false
Defaults.ConfigPath = defaultPath + "config.json"
// Generate certificate files for SSL // Generate certificate files for SSL
err = GenerateCertificate() err = GenerateCertificate()

View File

@@ -16,7 +16,6 @@ import (
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"github.com/Akilan1999/p2p-rendering-computation/p2p"
"log" "log"
"math/big" "math/big"
"net" "net"
@@ -54,7 +53,8 @@ func GenerateCertificate() error {
//flag.Parse() //flag.Parse()
host, err = p2p.CurrentPublicIP() //host, err = p2p.CurrentPublicIP()
host = "0.0.0.0"
if err != nil { if err != nil {
return err return err

12
flake.lock generated
View File

@@ -28,11 +28,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1733668782, "lastModified": 1745875161,
"narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=", "narHash": "sha256-0YkWCS13jpoo3+sX/3kcgdxBNt1VZTmvF+FhZb4rFKI=",
"owner": "nix-community", "owner": "nix-community",
"repo": "gomod2nix", "repo": "gomod2nix",
"rev": "514283ec89c39ad0079ff2f3b1437404e4cba608", "rev": "2cbd7fdd6eeab65c494cc426e18f4e4d2a5e35c0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -43,11 +43,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1735834308, "lastModified": 1748460289,
"narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=", "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "6df24922a1400241dae323af55f30e4318a6ca65", "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
"type": "github" "type": "github"
}, },
"original": { "original": {

152
flake.nix
View File

@@ -1,41 +1,129 @@
{ {
description = "P2PRC nix flake"; description = "P2PRC nix flake";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; inputs = {
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.gomod2nix.url = "github:nix-community/gomod2nix";
inputs.gomod2nix.inputs.nixpkgs.follows = "nixpkgs";
inputs.gomod2nix.inputs.flake-utils.follows = "flake-utils";
outputs = { self, nixpkgs, flake-utils, gomod2nix }: nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
(flake-utils.lib.eachDefaultSystem
(system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ gomod2nix.overlays.default ];
};
# pkgs = nixpkgs.legacyPackages.${system}; flake-utils = {
url = "github:numtide/flake-utils";
};
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now. gomod2nix = {
# This has no effect on other platforms. url = "github:nix-community/gomod2nix";
callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage; inputs.nixpkgs.follows = "nixpkgs";
in inputs.flake-utils.follows = "flake-utils";
{ };
packages.default = callPackage ./. { }; };
devShells.default = pkgs.mkShell { outputs =
buildInputs = with pkgs; [ {
go nixpkgs,
gopls flake-utils,
gotools gomod2nix,
go-tools ...
gomod2nix.packages.${system}.default }:
sqlite-interactive let
];
}; bindingsOverlay = import ./nix/overlays/bindings.nix;
}) coreOverlay = (final: prev: {
); p2prc = final.callPackage ./. { };
});
in
(flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
gomod2nix.overlays.default
coreOverlay
bindingsOverlay
];
};
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
# This has no effect on other platforms.
callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage;
p2prcDefault = callPackage ./. { };
in
{
packages.default = p2prcDefault;
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
go
gopls
gotools
go-tools
gomod2nix.packages.${system}.default
sqlite-interactive
];
};
packages.initHaskellProject = pkgs.writeShellApplication {
name = "initHaskellProject";
runtimeInputs = with pkgs; [
ghc
cabal2nix
cabal-install
git
p2prcDefault
];
text =
''
clear
if [ "$#" -eq 0 ]; then
echo "No arguments provided."
echo "Please provide the name of your project"
echo "nix run git+https://github:akilan1999/p2p-rendering-computation#initHaskellProject -- <NAME-PROJECT>"
exit 1;
fi
PROJECT_DIR="$1"
mkdir "$PROJECT_DIR"
cd "$PROJECT_DIR"
git init .
clear
cabal init --exe --simple
sed -i 's/base.*$/base, p2prc/' "$PROJECT_DIR".cabal
cabal2nix . > ./cabal.nix;
git add .
clear
echo -e "run the following commands to finish nix development and production environment:\n\n"
echo -e "cd $PROJECT_DIR"
echo -e "nix flake init -t github:akilan1999/p2p-rendering-computation#haskell"
echo -e "nix develop"
echo -e "nix run"
echo -e "\n\n"
'';
};
}
)) //
{
overlays = {
default = coreOverlay;
bindings = bindingsOverlay;
};
templates.haskell = {
path = ./nix/templates/haskell;
description = "Haskell Bindings to p2prc protocol";
};
};
} }

View File

@@ -1,909 +0,0 @@
#+SETUPFILE: https://fniessen.github.io/org-html-themes/org/theme-readtheorg.setup
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://fniessen.github.io/org-html-themes/src/readtheorg_theme/css/search.css"/>
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style.css"/>
#+attr_html: :width 300px :align center
[[./artwork/p2prc-logos/Colored-On-Light-Image.png]]
* Guide through video
*** The video below shows the setup and usage of P2PRC.
#+attr_html: :class video
[[https://www.youtube.com/watch?v=OMwCpedu5cs][https://i3.ytimg.com/vi/OMwCpedu5cs/maxresdefault.jpg]]
* Introduction
:PROPERTIES:
:CUSTOM_ID: chapter1-introduction
:END:
This project focuses on creating a framework for running heavy computational tasks that a regular
computer cannot handle easily. These tasks may include graphically demanding video games, rendering
3D animations, and performing complex protein folding simulations. The major focus of this project
is not on financial incentives but rather on building a robust and efficient peer-to-peer (P2P)
network to decentralise task execution and increase the computational bandwidth available for
such tasks.
The P2PRC framework serves as a foundation for decentralised rendering and computation,
providing insights into how tasks can be distributed efficiently across a network of peers.
Leveraging the P2PRC approach, this project extends its capabilities to handle a
wider range of computationally intensive tasks.
** Motivation
:PROPERTIES:
:CUSTOM_ID: motivation
:END:
Many of the users rely on our PC / Laptop or servers that belong to a
server farm to run heavy tasks and with the demand of high creativity
requires higher computing power. Buying a powerful computer every few
years to run a bunch of heavy tasks which are not executed as frequently
to reap the benefits can be inefficient utilization of hardware. On the
other end, renting servers to run these heavy tasks can be really
useful. Ethically speaking this is leading to monopolisation of
computing power similar to what is happening in the web server area. By
using peer to peer principles it is possible to remove the
monopolisation factor and increase the bandwidth between the client and
server.
* Installation
:PROPERTIES:
:CUSTOM_ID: installation
:END:
Over here we will cover the basic steps to get the server and client
side running.
** Latest release install
:PROPERTIES:
:CUSTOM_ID: latest-release-install
:END:
https://github.com/Akilan1999/p2p-rendering-computation/releases
** Install from Github master branch
:PROPERTIES:
:CUSTOM_ID: install-from-github-master-branch
:END:
*** Install Go lang
:PROPERTIES:
:CUSTOM_ID: install-go-lang
:END:
The entire the implementation of this project is done using Go lang.
Thus, we need go lang to compile to code to a binary file.
[[https://golang.org/doc/install][Instructions to install Go lang]]
*** Install Docker
:PROPERTIES:
:CUSTOM_ID: install-docker
:END:
In this project the choice of virtualization is Docker due to it's wide
usage in the developer community. In the server module we use the Docker
Go API to create and interact with the containers.
[[https://docs.docker.com/get-docker/][Instructions to install docker]]
[[https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#docker][Instructions
to install docker GPU]]
#+begin_example
// Do ensure that the docker command does not need sudo to run
sudo chmod 666 /var/run/docker.sock
#+end_example
*** Build Project and install project
:PROPERTIES:
:CUSTOM_ID: build-project-and-install-project
:END:
To set up the internal dependencies and build the entire go code into a
single binary
#+begin_example
make
#+end_example
**** For Windows
:PROPERTIES:
:CUSTOM_ID: for-windows
:END:
To set up P2PRC on Windows, simply run this batch file. *Make sure you
are not in admin mode when running this.*
#+begin_example
.\install.bat
#+end_example
*** Add appropriate paths to =.bashrc=
:PROPERTIES:
:CUSTOM_ID: add-appropriate-paths-to-.bashrc
:END:
#+begin_example
export P2PRC=/<PATH>/p2p-rendering-computation
export PATH=/<PATH>/p2p-rendering-computation:${PATH}
#+end_example
*** Test if binary works
:PROPERTIES:
:CUSTOM_ID: test-if-binary-works
:END:
#+begin_example
p2prc --help
#+end_example
**** Output:
:PROPERTIES:
:CUSTOM_ID: output
:END:
#+begin_example
NAME:
p2p-rendering-computation - p2p cli application to create and access VMs in other servers
USAGE:
p2prc [global options] command [command options] [arguments...]
VERSION:
2.0.0
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--Server, -s Starts server (default: false) [$SERVER]
--UpdateServerList, --us Update List of Server available based on servers iptables (default: false) [$UPDATE_SERVER_LIST]
--ListServers, --ls List servers which can render tasks (default: false) [$LIST_SERVERS]
--AddServer value, --as value Adds server IP address to iptables [$ADD_SERVER]
--ViewImages value, --vi value View images available on the server IP address [$VIEW_IMAGES]
--CreateVM value, --touch value Creates Docker container on the selected server [$CREATE_VM]
--ContainerName value, --cn value Specifying the container run on the server side [$CONTAINER_NAME]
--BaseImage value, --bi value Specifying the docker base image to template the dockerfile [$CONTAINER_NAME]
--RemoveVM value, --rm value Stop and Remove Docker container (IP:port) accompanied by container ID via --ID or --id [$REMOVE_VM]
--ID value, --id value Docker Container ID [$ID]
--Ports value, -p value Number of ports to open for the Docker Container [$NUM_PORTS]
--GPU, --gpu Create Docker Containers to access GPU (default: false) [$USE_GPU]
--Specification value, --specs value Specs of the server node [$SPECS]
--SetDefaultConfig, --dc Sets a default configuration file (default: false) [$SET_DEFAULT_CONFIG]
--NetworkInterfaces, --ni Shows the network interface in your computer (default: false) [$NETWORK_INTERFACE]
--ViewPlugins, --vp Shows plugins available to be executed (default: false) [$VIEW_PLUGIN]
--TrackedContainers, --tc View (currently running) containers which have been created from the client side (default: false) [$TRACKED_CONTAINERS]
--ExecutePlugin value, --plugin value Plugin which needs to be executed [$EXECUTE_PLUGIN]
--CreateGroup, --cgroup Creates a new group (default: false) [$CREATE_GROUP]
--Group value, --group value group flag with argument group ID [$GROUP]
--Groups, --groups View all groups (default: false) [$GROUPS]
--RemoveContainerGroup, --rmcgroup Remove specific container in the group (default: false) [$REMOVE_CONTAINER_GROUP]
--RemoveGroup value, --rmgroup value Removes the entire group [$REMOVE_GROUP]
--MAPPort value, --mp value Maps port for a specific port provided as the parameter [$MAPPORT]
--DomainName value, --dn value While mapping ports allows to set a domain name to create a mapping in the proxy server [$DOMAINNAME]
--Generate value, --gen value Generates a new copy of P2PRC which can be modified based on your needs [$GENERATE]
--ModuleName value, --mod value New go project module name [$MODULENAME]
--PullPlugin value, --pp value Pulls plugin from git repos [$PULLPLUGIN]
--RemovePlugin value, --rp value Removes plugin [$REMOVEPLUGIN]
--AddMetaData value, --amd value Adds metadata about the current node in the p2p network which is then propagated through the network [$ADDMETADATA]
--help, -h show help (default: false)
--version, -v print the version (default: false)
#+end_example
--------------
* Using basic commands
:PROPERTIES:
:CUSTOM_ID: using-basic-commands
:END:
*** Start as a server
:PROPERTIES:
:CUSTOM_ID: start-as-a-server
:END:
#+begin_example
p2prc -s
#+end_example
*** View server Specification
:PROPERTIES:
:CUSTOM_ID: view-server-specification
:END:
#+begin_example
p2prc --specs=<ip address>
#+end_example
*** Run container
:PROPERTIES:
:CUSTOM_ID: run-container
:END:
use the =--gpu= if you know the other machine has a gpu.
#+begin_example
p2prc --touch=<server ip address> -p <number of ports> --gpu
#+end_example
*** Remove container
:PROPERTIES:
:CUSTOM_ID: remove-container
:END:
The docker id is present in the output where you create a container
#+begin_example
p2prc --rm=<server ip address> --id=<docker container id>
#+end_example
*** Adding servers to ip table
:PROPERTIES:
:CUSTOM_ID: adding-servers-to-ip-table
:END:
#+begin_example
p2prc --as=<server ip address you want to add>
#+end_example
*** Update ip table
:PROPERTIES:
:CUSTOM_ID: update-ip-table
:END:
#+begin_example
p2prc --us
#+end_example
*** List Servers
:PROPERTIES:
:CUSTOM_ID: list-servers
:END:
#+begin_example
p2prc --ls
#+end_example
*** View Network interfaces
:PROPERTIES:
:CUSTOM_ID: view-network-interfaces
:END:
#+begin_example
p2prc --ni
#+end_example
*** Viewing Containers created Client side
:PROPERTIES:
:CUSTOM_ID: viewing-containers-created-client-side
:END:
#+begin_example
p2prc --tc
#+end_example
*** Running plugin
:PROPERTIES:
:CUSTOM_ID: running-plugin
:END:
#+begin_example
p2prc --plugin <plugin name> --id <container id or group id>
#+end_example
*** Create group
:PROPERTIES:
:CUSTOM_ID: create-group
:END:
#+begin_example
p2prc --cgroup
#+end_example
*** Add container to group
:PROPERTIES:
:CUSTOM_ID: add-container-to-group
:END:
#+begin_example
p2prc --group <group id> --id <container id>
#+end_example
*** View groups
:PROPERTIES:
:CUSTOM_ID: view-groups
:END:
#+begin_example
p2prc --groups
#+end_example
*** View specific group
:PROPERTIES:
:CUSTOM_ID: view-specific-group
:END:
#+begin_example
p2prc --group <group id>
#+end_example
*** Delete container from group
:PROPERTIES:
:CUSTOM_ID: delete-container-from-group
:END:
#+begin_example
p2prc --rmcgroup --group <group id> --id <container id>
#+end_example
*** Delete entire group
:PROPERTIES:
:CUSTOM_ID: delete-entire-group
:END:
#+begin_example
p2prc --rmgroup <group id>
#+end_example
*** Pulling plugin from a remote repo
:PROPERTIES:
:CUSTOM_ID: pulling-plugin-from-a-remote-repo
:END:
#+begin_example
p2prc --pp <repo link>
#+end_example
*** Deleting plugin from the plugin directory
:PROPERTIES:
:CUSTOM_ID: deleting-plugin-from-the-plugin-directory
:END:
#+begin_example
p2prc --rp <plugin name>
#+end_example
*** Added custom metadata about the current node
:PROPERTIES:
:CUSTOM_ID: added-custom-metadata-about-the-current-node
:END:
#+begin_example
p2prc --amd "custom metadata"
#+end_example
*** MapPort and link to domain name
Allows to expose remote ports from a machine in the P2P network.
#+begin_example
p2prc --mp <port no to map> --dn <domain name to link Mapped port against>
#+end_example
**** MapPort in remote machine
This is to ensure ports on remote machines on the P2PRC can be easily opened.
#+begin_example
p2prc --mp <port no to map> --dn <domain name to link Mapped port against> --ra <remote server address>
#+end_example
--------------
* P2P Module Implementation
:PROPERTIES:
:CUSTOM_ID: p2p-module-implementation
:END:
The P2P module is for managing server information within the network.
It maintains and updates the IP table, ensuring accuracy by preventing duplicates and removing
entries for unreachable servers. Furthermore, the module conducts speed tests on the listed servers
to determine upload and download speeds. This valuable information enables users to identify nearby
servers with optimal performance, enhancing their overall network experience.
#+caption: UML diagram of P2P module
[[file:Docs/images/p2pmoduleArch.png]
The peer to peer implementation was built from scratch. This is because
other peer to peer libraries were on the implementation of the
Distributed hash table. At the current moment all those heavy features
are not needed because the objective is to search and list all possible
servers available. The limitation being that to be a part of the network
the user has to know at least 1 server. The advantage of building from
scratch makes the module super light and possibility for custom
functions and structs. The sub topics below will mention the
implementations of each functionality in depth.
** IP Table
:PROPERTIES:
:CUSTOM_ID: ip-table
:END:
The ip table file is a json as the format with a list of servers ip
addresses, latencies, downloads and uploads speeds. The functions
implemented include read file, write file and remove duplicate IP
addresses. The remove duplicate IP address function exists because
sometimes servers IP tables can have the same ip addresses as what the
client has. The path of the IP table json file is received from the
configuration module.
#+begin_src json
{
"ip_address": [
{
"Name": "<hostname of the machine>",
"MachineUsername": "<machine username>",
"IPV4": "<ipv4 address>",
"IPV6": "<ipv6 address (Not used)>",
"Latency": <latency to the server>,
"Download": <download speed (Not used)>,
"Upload": <upload speed (Not used)>,
"ServerPort": "<server port no>",
"BareMetalSSHPort": "<Baremetal ssh port no>",
"NAT": "<boolean representing if the node is behind NAT or not>",
"EscapeImplementation": "<NAT traversal implementation>",
"ProxyServer": "<If the node listed is acting as a proxy server>",
"UnSafeMode": <Unsafe mode if turned on will allow all nodes in the network public keys to be
added to that particular node>",
"PublicKey": "<Public key of that particular node>",
"CustomInformation": "<custom information passed in through all the nodes in the network>"
}
]
}
#+end_src
*** Latency
:PROPERTIES:
:CUSTOM_ID: latency
:END:
The latency is measured in milliseconds. The route /server_info is
called from the server and time it takes to provide a json response is
recorded.
** NAT Traversal
:PROPERTIES:
:CUSTOM_ID: nat-traversal
:END:
P2PRC currently supports TURN for NAT traversal.
** TURN
:PROPERTIES:
:CUSTOM_ID: turn
:END:
The current TURN implementation used is FRP. The TURN server is also
required when a P2PRC node is acting as a Server. The TURN server is
determined based on the Node with the least amount of latency based on
the Nodes available on the IPTable. Once a TURN server is determined
there are 2 actions performed. The first one is =/FRPPort= to the TURN
server to receive a port which is used to generate the external port
from the TURN server. The flow below describes the workflow.
*** Client mode
:PROPERTIES:
:CUSTOM_ID: client-mode
:END:
- Call =/FRPPort=
#+begin_example
http://<turn server ip>:<server port no>/FRPport
#+end_example
- Call the TURN server in the following manner. The following is a
sample code snippet below.
#+begin_src go
import (
"github.com/Akilan1999/p2p-rendering-computation/p2p/frp"
)
func main() {
serverPort, err := frp.GetFRPServerPort("http://" + <lowestLatencyIpAddress.Ipv4> + ":" + lowestLatencyIpAddress.ServerPort)
if err != nil {
return nil, err
}
// Create 1 second delay to allow FRP server to start
time.Sleep(1 * time.Second)
// Starts FRP as a client with
proxyPort, err := frp.StartFRPClientForServer(<lowestLatencyIpAddress.Ipv4>, serverPort, <the port you want to expose externally>)
if err != nil {
return nil, err
}
}
#+end_src
* Language Bindings
:PROPERTIES:
:CUSTOM_ID: language-bindings
:END:
[[https://en.wikipedia.org/wiki/Language_binding][Language bindings]]
refers to wrappers to bridge 2 programming languages. This is used in
P2PRC to extend calling P2PRC functions in other programming languages.
Currently this is done by generating =.so= and =.h= from the Go
compiler.
** How to build shared object files
:PROPERTIES:
:CUSTOM_ID: how-to-build-shared-object-files
:END:
**** The easier way
:PROPERTIES:
:CUSTOM_ID: the-easier-way
:END:
#+begin_src sh
# Run
make sharedObjects
#+end_src
**** Or the direct way
:PROPERTIES:
:CUSTOM_ID: or-the-direct-way
:END:
#+begin_src sh
# Run
cd Bindings && go build -buildmode=c-shared -o p2prc.so
#+end_src
**** If successfully built:
:PROPERTIES:
:CUSTOM_ID: if-successfully-built
:END:
#+begin_src sh
# Enter into the Bindings directory
cd Bindings
# List files
ls
# Find files
p2prc.h p2prc.so
#+end_src
** Workings under the hood
:PROPERTIES:
:CUSTOM_ID: workings-under-the-hood
:END:
Below are a sample set of commands to open the bindings implementation.
#+begin_example
# run
cd Bindings/
# list files
ls
# search for file
Client.go
#+end_example
*** In Client go
:PROPERTIES:
:CUSTOM_ID: in-client-go
:END:
There a few things to notice which are different from your standard Go
programs:
**** 1. We import "C" which means [[https://pkg.go.dev/cmd/cgo][Cgo]] is required.
:PROPERTIES:
:CUSTOM_ID: we-import-c-which-means-cgo-is-required.
:END:
#+begin_src go
import "C"
#+end_src
**** 2. All functions which are required to be called from other programming languages have comment such as.
:PROPERTIES:
:CUSTOM_ID: all-functions-which-are-required-to-be-called-from-other-programming-languages-have-comment-such-as.
:END:
#+begin_src go
//export <function name>
// ------------ Example ----------------
// The function below allows to externally
// to call the P2PRC function to start containers
// in a specific node in the know list of nodes
// in the p2p network.
// Note: the comment "//export StartContainer".
//export StartContainer
func StartContainer(IP string) (output *C.char) {
container, err := client.StartContainer(IP, 0, false, "", "")
if err != nil {
return C.CString(err.Error())
}
return ConvertStructToJSONString(container)
}
#+end_src
**** 3. While looking through the file (If 2 files are compared it is pretty trivial to notice a common structure).
:PROPERTIES:
:CUSTOM_ID: while-looking-through-the-file-if-2-files-are-compared-it-is-pretty-trivial-to-notice-a-common-structure.
:END:
#+begin_src go
// --------- Example ------------
//export StartContainer
func StartContainer(IP string) (output *C.char) {
container, err := client.StartContainer(IP, 0, false, "", "")
if err != nil {
return C.CString(err.Error())
}
return ConvertStructToJSONString(container)
}
//export ViewPlugin
func ViewPlugin() (output *C.char) {
plugins, err := plugin.DetectPlugins()
if err != nil {
return C.CString(err.Error())
}
return ConvertStructToJSONString(plugins)
}
#+end_src
**** It is easy to notice that:
:PROPERTIES:
:CUSTOM_ID: it-is-easy-to-notice-that
:END:
- =ConvertStructToJSONString(<go object>)=: This is a helper function
that convert a go object to JSON string initially and converts it to
=CString=.
- =(output *C.char)=: This is the return type for most of the functions.
**** A Pseudo code to refer to the common function implementation shape could be represented as:
:PROPERTIES:
:CUSTOM_ID: a-pseudo-code-to-refer-to-the-common-function-implementation-shape-could-be-represented-as
:END:
#+begin_example
func <Function name> (output *C.char) {
<response>,<error> := <P2PRC function name>(<parameters if needed>)
if <error> != nil {
return C.CString(<error>.Error())
}
return ConvertStructToJSONString(<response>)
}
#+end_example
** Current languages supported
:PROPERTIES:
:CUSTOM_ID: current-languages-supported
:END:
*** Python
**** Build sample python program
:PROPERTIES:
:CUSTOM_ID: build-sample-python-program
:END:
The easier way
#+begin_src sh
# Run
make python
# Expected ouput
Output is in the Directory Bindings/python/export/
# Run
cd Bindings/python/export/
# list files
ls
# Expected output
SharedObjects/ library.py requirements.txt
#+end_src
Above shows a generated folder which consists of a folder called
"SharedObjects/" which consists of =p2prc.so= and =p2prc.h= files.
=p2prc.py= refers to a sample python script calling P2PRC go functions.
To start an any project to extend P2PRC with python, This generated
folder can copied and created as a new git repo for P2PRC extensions
scripted or used a reference point as proof of concept that P2PRC can be
called from other programming languages.
*** Haskell
P2PRC officially supports Haskell bindings and will further support
project using Haskell to build orchestrators on top of P2PRC.
[[https://p2prc.akilan.io/Docs/haskell][Read more...]]
* Config Implementation
:PROPERTIES:
:CUSTOM_ID: config-implementation
:END:
The configuration module is responsible to store basic information of
absolute paths of files being called in the Go code. In a full-fledged
Cli the configuration file can be found in the directory /etc/ and from
there points to location such as where the IP table file is located. In
the future implementation the config file will have information such as
number of hops and other parameters to tweak and to improve the
effectiveness of the peer to peer network. The configuration module was
implemented using the library Viper. The Viper library automates
features such as searching in default paths to find out if the
configuration file is present. If the configuration file is not present
in the default paths then it auto generates the configuration file. The
configurations file can be in any format. In this project the
configuration file was generated using JSON format.
#+begin_src json
{
"MachineName": "pc-74-120.customer.ask4.lan",
"IPTable": "/Users/akilan/Documents/p2p-rendering-computation/p2p/iptable/ip_table.json",
"DockerContainers": "/Users/akilan/Documents/p2p-rendering-computation/server/docker/containers/",
"DefaultDockerFile": "/Users/akilan/Documents/p2p-rendering-computation/server/docker/containers/docker-ubuntu-sshd/",
"SpeedTestFile": "/Users/akilan/Documents/p2p-rendering-computation/p2p/50.bin",
"IPV6Address": "",
"PluginPath": "/Users/akilan/Documents/p2p-rendering-computation/plugin/deploy",
"TrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/trackcontainers.json",
"ServerPort": "8088",
"GroupTrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/grouptrackcontainers.json",
"FRPServerPort": "True",
"BehindNAT": "True",
"CustomConfig": null
}
#+end_src
* Abstractions
:PROPERTIES:
:CUSTOM_ID: abstractions
:END:
The Abstractions package consists of black-boxed functions for P2PRC.
** Functions
:PROPERTIES:
:CUSTOM_ID: functions
:END:
- =Init(<Project name>)=: Initializes P2PRC with all the needed
configurations.
- =Start()=: Starts p2prc as a server and makes it possible to extend by
adding other routes and functionality to P2PRC.
- =MapPort(<port no>)=: On the local machine the port you want to export
to world.
- =StartContainer(<ip address>)=: The machine on the p2p network where
you want to spin up a docker container.
- =RemoveContainer(<ip address>,<container id>)=: Terminate container
based on the IP address and container name.
- =GetSpecs(<ip address>)=: Get specs of a machine on the network based
on the IP address.
- =ViewIPTable()=: View the IP table which about nodes in the network.
- =UpdateIPTable()=: Force update IP table to learn about new nodes
faster.
* NAT Traversal
:PROPERTIES:
:CUSTOM_ID: nat-traversal
:END:
P2PRC currently supports TURN for NAT traversal.
** TURN
:PROPERTIES:
:CUSTOM_ID: turn
:END:
The current TURN implementation used is FRP. The TURN server is also
required when a P2PRC node is acting as a Server. The TURN server is
determined based on the Node with the least amount of latency based on
the Nodes available on the IPTable. Once a TURN server is determined
there are 2 actions performed. The first one is =/FRPPort= to the TURN
server to receive a port which is used to generate the external port
from the TURN server. The flow below describes the workflow.
* Client mode
:PROPERTIES:
:CUSTOM_ID: client-mode
:END:
- Call =/FRPPort=
#+begin_example
http://<turn server ip>:<server port no>/FRPport
#+end_example
- Call the TURN server in the following manner. The following is a
sample code snippet below.
#+begin_src go
import (
"github.com/Akilan1999/p2p-rendering-computation/p2p/frp"
)
func main() {
serverPort, err := frp.GetFRPServerPort("http://" + <lowestLatencyIpAddress.Ipv4> + ":" + lowestLatencyIpAddress.ServerPort)
if err != nil {
return nil, err
}
// Create 1 second delay to allow FRP server to start
time.Sleep(1 * time.Second)
// Starts FRP as a client with
proxyPort, err := frp.StartFRPClientForServer(<lowestLatencyIpAddress.Ipv4>, serverPort, <the port you want to expose externally>)
if err != nil {
return nil, err
}
}
#+end_src
* Blog posts
** Self host within 5 minutes any program
- Author: [[http://akilan.io/][Akilan Selvacoumar]]
- Date: 28-01-2025
- Video tutorial:
[[https://youtu.be/rN4SiVowg5E][https://i3.ytimg.com/vi/rN4SiVowg5E/maxresdefault.jpg]]
This is a fun expirement for anyone to try to quickly run a server and
quickly do a map port and domain name mapping in a single command.
*** 1. Find a program you want to run
Let's try to setup a really easy program (Let's do with Linkwarden
with docker compose :) ). This is under the assumption you have docker
compose installed on your local machine.
**** Let's run Linkwarden using docker compose and P2PRC
[[https://docs.linkwarden.app/self-hosting/installation][Installation instructions]]:
#+BEGIN_SRC
mkdir linkwarden && cd linkwarden
curl -O https://raw.githubusercontent.com/linkwarden/linkwarden/refs/heads/main/docker-compose.yml
curl -L https://raw.githubusercontent.com/linkwarden/linkwarden/refs/heads/main/.env.sample -o ".env"
#+END_SRC
Environment configuration
#+BEGIN_SRC
vim .env
# Change values
NEXTAUTH_URL=https://<DOMAIN NAME>/api/v1/auth
NEXTAUTH_SECRET=VERY_SENSITIVE_SECRET
POSTGRES_PASSWORD=CUSTOM_POSTGRES_PASSWORD
#+END_SRC
Run linkwarden!
#+BEGIN_SRC
docker compose up
#+END_SRC
If setup correctly linkwarden should
be running.
Local link: http://localhost:3000
Time to setup P2PRC
[[https://p2prc.akilan.io/Docs/#build-project-and-install-project][Installation Instructions]]
Run p2prc as a background
#+BEGIN_SRC
p2prc -s &
#+END_SRC
Run map port and domain mapping
#+BEGIN_SRC
p2prc --mp 3000 --dn <DOMAIN NAME>
#+END_SRC
Sample response
#+BEGIN_SRC
{
"IPAddress": "217.76.63.222",
"PortNo": "61582",
"EntireAddress": "217.76.63.222:61582"
}
#+END_SRC
Add DNS entry
#+BEGIN_SRC
A entry 217.76.63.222
#+END_SRC
Your done now just head to the DOMAIN NAME you added.
ex: https://linkwarden.akilan.io
* Ideas for future potencial features
Consists of personal loideas for the future of P2PRC.
At moment only has main contributors writiing to this.
** To support hetrogenous set of Nodes that cannot run P2PRC
This stems from a personal issue I have when doing research
on [[https://github.com/CTSRD-CHERI/cheribsd][CheriBSD]] kernel. For my research I am using the ARM morello
which is a 128bit ARMv8 processor. At the moment Go programs
can cannot compile and run inside the CPU. This means I cannot
run P2PRC at the moment inside the ARM morello to remotely access
it when it's behind NAT using P2PRC. This would indeed be a common
problem when running against various Architectures that do not
support running P2PRC. As you will see soon this also creates
oppurtunity space to scale faster to nodes in a local network
and would introduce a new layer fault tolerance within a local
network nodes.
*** Assumptions:
- I have a Morello board that cannot run P2PRC
- The Morello has a local IP address (ex: 192.168.0.10)
- I have 2 laptops running P2PRC in that local network.
- This means I have 2 ways to access the Morello board: Which is to SSH
into either 2 laptops and then SSH into 192.168.0.10 to gain access
to the board. Wouldn't it be great to automate this whole layer and
as well look into custom tasks into the hetrogenous hardware.
*** Set of interesting possible:
We build a cool set possibilities before and use this to build up the implementation
plan.
- We can use P2PRC access the morello board remotely in a single command.
- We can use the P2PRC protocol to run servers inside the morello board via traversed
node locally which can access that Node.
- Spin servers on node not running P2PRC using the P2PRC standard abstractions.
- Auto-setup P2PRC nodes with just SSH access via potencially a DSL.
- A neat use case for CHERI for instance would be use the architecture to run light
weight hypervisors.
*** Implementation
- To use implementations similar to [[https://linux.die.net/man/1/socat][socat]] to ensure we can bind address of local
nodes to a node running P2PRC and the node running P2PRC can do a local map port.
- We are working on hardening the implementation of the --mp (Map port) to even
map ports to machines which remotely running P2PRC. This means of instance I
can issue a command to the Morello board without the morello board being in
my local network.
- We would want to implement the exsisting P2PRC public key mechanism as well so that
other nodes can access the Morello board who have permission access.
#+CAPTION: Implementation idea (To be improved upon)
[[./Docs/images/P2PRCRemoteNodes.png]]

View File

@@ -0,0 +1,7 @@
final: prev: {
haskellPackages = prev.haskellPackages.override {
overrides = hfinal: hprev: {
p2prc = hfinal.callPackage ../../Bindings/Haskell/project.nix { };
};
};
}

190
nix/templates/haskell/flake.lock generated Normal file
View File

@@ -0,0 +1,190 @@
{
"nodes": {
"flake-util": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gomod2nix": {
"inputs": {
"flake-utils": [
"p2prc-flake",
"flake-utils"
],
"nixpkgs": [
"p2prc-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1745875161,
"narHash": "sha256-0YkWCS13jpoo3+sX/3kcgdxBNt1VZTmvF+FhZb4rFKI=",
"owner": "nix-community",
"repo": "gomod2nix",
"rev": "2cbd7fdd6eeab65c494cc426e18f4e4d2a5e35c0",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "gomod2nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1748460289,
"narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1748370509,
"narHash": "sha256-QlL8slIgc16W5UaI3w7xHQEP+Qmv/6vSNTpoZrrSlbk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4faa5f5321320e49a78ae7848582f684d64783e9",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"p2prc-flake": {
"inputs": {
"flake-utils": "flake-utils_2",
"gomod2nix": "gomod2nix",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1748646470,
"narHash": "sha256-7aZQQ+jHdlxUzFPXzEEYfH1oU3isSZlZnMC7HWUDHCk=",
"owner": "xecarlox94",
"repo": "p2p-rendering-computation",
"rev": "73a283a545d61754a0fe821287541c2cd412bc0e",
"type": "github"
},
"original": {
"owner": "xecarlox94",
"ref": "nix",
"repo": "p2p-rendering-computation",
"type": "github"
}
},
"root": {
"inputs": {
"flake-util": "flake-util",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"p2prc-flake": "p2prc-flake"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -0,0 +1,48 @@
{
description = "Start of Haskell P2PRC flake";
inputs =
{
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-util.url = "github:numtide/flake-utils";
p2prc-flake.url = "github:akilan1999/p2p-rendering-computation";
};
outputs = { nixpkgs, p2prc-flake, flake-utils, ... }:
(flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
p2prc-flake.overlays.default
p2prc-flake.overlays.bindings
];
};
in {
packages.default = pkgs.haskellPackages.callPackage ./cabal.nix { };
devShells.default = pkgs.haskellPackages.shellFor {
packages = p: [
(p.callPackage ./cabal.nix { })
];
buildInputs = with pkgs; [
p2prc-flake.packages.${system}.default
ghc
cabal2nix
cabal-install
];
shellHook = ''
cabal2nix . > ./cabal.nix
'';
};
}
));
}

View File

@@ -1,284 +1,297 @@
package p2p package p2p
import ( import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/Akilan1999/p2p-rendering-computation/config" "github.com/Akilan1999/p2p-rendering-computation/config"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
"os" "os"
"time" "time"
) )
// Get IP table Data // Get IP table Data
type IpAddresses struct { type IpAddresses struct {
IpAddress []IpAddress `json:"ip_address"` IpAddress []IpAddress `json:"ip_address"`
} }
type IpAddress struct { type IpAddress struct {
Name string `json:"Name"` Name string `json:"Name"`
MachineUsername string `json:"MachineUsername"` MachineUsername string `json:"MachineUsername"`
Ipv4 string `json:"IPV4"` Ipv4 string `json:"IPV4"`
Ipv6 string `json:"IPV6"` Ipv6 string `json:"IPV6"`
Latency time.Duration `json:"Latency"` Latency time.Duration `json:"Latency"`
Download float64 `json:"Download"` Download float64 `json:"Download"`
Upload float64 `json:"Upload"` Upload float64 `json:"Upload"`
ServerPort string `json:"ServerPort"` ServerPort string `json:"ServerPort"`
BareMetalSSHPort string `json:"BareMetalSSHPort"` BareMetalSSHPort string `json:"BareMetalSSHPort"`
NAT bool `json:"NAT"` NAT bool `json:"NAT"`
EscapeImplementation string `json:"EscapeImplementation"` EscapeImplementation string `json:"EscapeImplementation"`
ProxyServer bool `json:"ProxyServer"` ProxyServer bool `json:"ProxyServer"`
UnSafeMode bool `json:"UnSafeMode"` UnSafeMode bool `json:"UnSafeMode"`
PublicKey string `json:"PublicKey"` PublicKey string `json:"PublicKey"`
CustomInformation string `json:"CustomInformation"` CustomInformation string `json:"CustomInformation"`
//CustomInformationKey []byte `json:"CustomInformationKey"` //CustomInformationKey []byte `json:"CustomInformationKey"`
} }
type IP struct { type IP struct {
Query string Query string
} }
var Test = false
// ReadIpTable Read data from Ip tables from json file // ReadIpTable Read data from Ip tables from json file
func ReadIpTable() (*IpAddresses, error) { func ReadIpTable() (*IpAddresses, error) {
// Get Path from config // Get Path from config
config, err := config.ConfigInit(nil, nil) config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
jsonFile, err := os.Open(config.IPTable) jsonFile, err := os.Open(config.IPTable)
// if we os.Open returns an error then handle it // if we os.Open returns an error then handle it
if err != nil { if err != nil {
return nil, err return nil, err
} }
// defer the closing of our jsonFile so that we can parse it later on // defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close() defer jsonFile.Close()
// read our opened xmlFile as a byte array. // read our opened xmlFile as a byte array.
byteValue, _ := ioutil.ReadAll(jsonFile) byteValue, _ := ioutil.ReadAll(jsonFile)
// we initialize our Users array // we initialize our Users array
var ipAddresses IpAddresses var ipAddresses IpAddresses
// we unmarshal our byteArray which contains our // we unmarshal our byteArray which contains our
// jsonFile's content into 'users' which we defined above // jsonFile's content into 'users' which we defined above
json.Unmarshal(byteValue, &ipAddresses) json.Unmarshal(byteValue, &ipAddresses)
var PublicIP IpAddress var PublicIP IpAddress
ipv6, err := GetCurrentIPV6() ipv6, err := GetCurrentIPV6()
if err != nil { if err != nil {
return nil, err return nil, err
} }
ip, err := CurrentPublicIP() ip, err := CurrentPublicIP()
if err != nil { if err != nil {
return nil, err return nil, err
} }
PublicIP.Ipv4 = ip PublicIP.Ipv4 = ip
PublicIP.Ipv6 = ipv6 PublicIP.Ipv6 = ipv6
PublicIP.ServerPort = config.ServerPort PublicIP.ServerPort = config.ServerPort
PublicIP.Name = config.MachineName PublicIP.Name = config.MachineName
PublicIP.NAT = config.BehindNAT PublicIP.NAT = config.BehindNAT
PublicIP.EscapeImplementation = "None" PublicIP.EscapeImplementation = "None"
// Updates current machine IP address to the IP table // Updates current machine IP address to the IP table
ipAddresses.IpAddress = append(ipAddresses.IpAddress, PublicIP) ipAddresses.IpAddress = append(ipAddresses.IpAddress, PublicIP)
//before writing to iptable ensures the duplicates are removed //before writing to iptable ensures the duplicates are removed
if err = ipAddresses.RemoveDuplicates(); err != nil { if err = ipAddresses.RemoveDuplicates(); err != nil {
return nil, err return nil, err
} }
return &ipAddresses, nil return &ipAddresses, nil
} }
// WriteIpTable Write to IP table json file // WriteIpTable Write to IP table json file
func (i *IpAddresses) WriteIpTable() error { func (i *IpAddresses) WriteIpTable() error {
//before writing to iptable ensures the duplicates are removed //before writing to iptable ensures the duplicates are removed
if err := i.RemoveDuplicates(); err != nil { if err := i.RemoveDuplicates(); err != nil {
return err return err
} }
file, err := json.MarshalIndent(i, "", " ") file, err := json.MarshalIndent(i, "", " ")
if err != nil { if err != nil {
return err return err
} }
// Get Path from config // Get Path from config
config, err := config.ConfigInit(nil, nil) config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(config.IPTable, file, 0644) err = ioutil.WriteFile(config.IPTable, file, 0644)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// PrintIpTable Print Ip table data for Cli // PrintIpTable Print Ip table data for Cli
func PrintIpTable() error { func PrintIpTable() error {
table, err := ReadIpTable() table, err := ReadIpTable()
// //
if err != nil { if err != nil {
return err return err
} }
// //
//for i := 0; i < len(table.IpAddress); i++ { //for i := 0; i < len(table.IpAddress); i++ {
// fmt.Printf("\nMachine Name: %s\nIP Address: %s\nIPV6: %s\nLatency: %s\nServerPort: %s\nbehindNAT: %s\nEscapeImplementation: %s\n-----------"+ // fmt.Printf("\nMachine Name: %s\nIP Address: %s\nIPV6: %s\nLatency: %s\nServerPort: %s\nbehindNAT: %s\nEscapeImplementation: %s\n-----------"+
// "-----------------\n", table.IpAddress[i].Name, table.IpAddress[i].Ipv4, table.IpAddress[i].Ipv6, // "-----------------\n", table.IpAddress[i].Name, table.IpAddress[i].Ipv4, table.IpAddress[i].Ipv6,
// table.IpAddress[i].Latency, table.IpAddress[i].ServerPort, table.IpAddress[i].NAT, table.IpAddress[i].EscapeImplementation) // table.IpAddress[i].Latency, table.IpAddress[i].ServerPort, table.IpAddress[i].NAT, table.IpAddress[i].EscapeImplementation)
//} //}
PrettyPrint(table) PrettyPrint(table)
return nil return nil
} }
// RemoveDuplicates This is a temporary fix current functions failing to remove // RemoveDuplicates This is a temporary fix current functions failing to remove
// Duplicate IP addresses from local IP table // Duplicate IP addresses from local IP table
func (table *IpAddresses) RemoveDuplicates() error { func (table *IpAddresses) RemoveDuplicates() error {
var NoDuplicates IpAddresses var NoDuplicates IpAddresses
for i, _ := range table.IpAddress { for i, _ := range table.IpAddress {
Exists := false Exists := false
for k := range NoDuplicates.IpAddress { for k := range NoDuplicates.IpAddress {
// Statements checked for // Statements checked for
// - duplicate IPV4 addresses [<IPV4>:<Port No>] // - duplicate IPV4 addresses [<IPV4>:<Port No>]
// - duplicate IPV6 addresses [<IPV6>] // - duplicate IPV6 addresses [<IPV6>]
// - Node is behind NAT and no escape implementation provided // - Node is behind NAT and no escape implementation provided
if (NoDuplicates.IpAddress[k].Ipv4 != "" && NoDuplicates.IpAddress[k].Ipv4 == table.IpAddress[i].Ipv4 && if (NoDuplicates.IpAddress[k].Ipv4 != "" && NoDuplicates.IpAddress[k].Ipv4 == table.IpAddress[i].Ipv4 &&
NoDuplicates.IpAddress[k].ServerPort == table.IpAddress[i].ServerPort) || NoDuplicates.IpAddress[k].ServerPort == table.IpAddress[i].ServerPort) ||
(NoDuplicates.IpAddress[k].Ipv6 != "" && NoDuplicates.IpAddress[k].Ipv6 == table.IpAddress[i].Ipv6) { (NoDuplicates.IpAddress[k].Ipv6 != "" && NoDuplicates.IpAddress[k].Ipv6 == table.IpAddress[i].Ipv6) {
Exists = true Exists = true
break break
} }
} }
if table.IpAddress[i].NAT && table.IpAddress[i].EscapeImplementation == "None" { if table.IpAddress[i].NAT && table.IpAddress[i].EscapeImplementation == "None" {
Exists = true Exists = true
} }
if Exists { if Exists {
continue continue
} }
NoDuplicates.IpAddress = append(NoDuplicates.IpAddress, table.IpAddress[i]) NoDuplicates.IpAddress = append(NoDuplicates.IpAddress, table.IpAddress[i])
} }
table.IpAddress = NoDuplicates.IpAddress table.IpAddress = NoDuplicates.IpAddress
return nil return nil
} }
// CurrentPublicIP Get Current Public IP address // CurrentPublicIP Get Current Public IP address
func CurrentPublicIP() (string, error) { func CurrentPublicIP() (string, error) {
req, err := http.Get("http://ip-api.com/json/") // Get configs
if err != nil { Config, err := config.ConfigInit(nil, nil)
return "", err if err != nil {
} return "", err
defer req.Body.Close() }
// If test mode is on then return local address
if Config.Test {
return "0.0.0.0", nil
}
body, err := ioutil.ReadAll(req.Body) req, err := http.Get("http://ip-api.com/json/")
if err != nil { if err != nil {
return "", err return "", err
} }
defer req.Body.Close()
var ip IP body, err := ioutil.ReadAll(req.Body)
json.Unmarshal(body, &ip) if err != nil {
return "", err
}
var ip IP
json.Unmarshal(body, &ip)
return ip.Query, nil
return ip.Query, nil
} }
// GetCurrentIPV6 gets the current IPV6 address based on the interface // GetCurrentIPV6 gets the current IPV6 address based on the interface
// specified in the config file // specified in the config file
func GetCurrentIPV6() (string, error) { func GetCurrentIPV6() (string, error) {
Config, err := config.ConfigInit(nil, nil) Config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return "", err return "", err
} }
// Fix in future release // Fix in future release
//byNameInterface, err := net.InterfaceByName(Config.NetworkInterface) //byNameInterface, err := net.InterfaceByName(Config.NetworkInterface)
//if err != nil { //if err != nil {
// return "",err // return "",err
//} //}
//addresses, err := byNameInterface.Addrs() //addresses, err := byNameInterface.Addrs()
//if err != nil { //if err != nil {
// return "",err // return "",err
//} //}
//if addresses[1].String() == "" { //if addresses[1].String() == "" {
// return "",errors.New("IPV6 address not detected") // return "",errors.New("IPV6 address not detected")
//} //}
//IP,_,err := net.ParseCIDR(addresses[Config.NetworkInterfaceIPV6Index].String()) //IP,_,err := net.ParseCIDR(addresses[Config.NetworkInterfaceIPV6Index].String())
//if err != nil { //if err != nil {
// return "",err // return "",err
//} //}
return Config.IPV6Address, nil return Config.IPV6Address, nil
} }
// ViewNetworkInterface This function is created to view the network interfaces available // ViewNetworkInterface This function is created to view the network interfaces available
func ViewNetworkInterface() error { func ViewNetworkInterface() error {
ifaces, err := net.Interfaces() ifaces, err := net.Interfaces()
if err != nil { if err != nil {
return err return err
} }
for _, i := range ifaces { for _, i := range ifaces {
addrs, err := i.Addrs() addrs, err := i.Addrs()
if err != nil { if err != nil {
return err return err
} }
for index, a := range addrs { for index, a := range addrs {
switch v := a.(type) { switch v := a.(type) {
case *net.IPAddr: case *net.IPAddr:
fmt.Printf("(%v) %v : %s (%s)\n", index, i.Name, v, v.IP.DefaultMask()) fmt.Printf("(%v) %v : %s (%s)\n", index, i.Name, v, v.IP.DefaultMask())
case *net.IPNet: case *net.IPNet:
fmt.Printf("(%v) %v : %s \n", index, i.Name, v) fmt.Printf("(%v) %v : %s \n", index, i.Name, v)
} }
} }
} }
return nil return nil
} }
// Ip4or6 Helper function to check if the IP address is IPV4 or // Ip4or6 Helper function to check if the IP address is IPV4 or
// IPV6 (https://socketloop.com/tutorials/golang-check-if-ip-address-is-version-4-or-6) // IPV6 (https://socketloop.com/tutorials/golang-check-if-ip-address-is-version-4-or-6)
func Ip4or6(s string) string { func Ip4or6(s string) string {
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
switch s[i] { switch s[i] {
case '.': case '.':
return "version 4" return "version 4"
case ':': case ':':
return "version 6" return "version 6"
} }
} }
return "version 6" return "version 6"
} }
func PrettyPrint(data interface{}) { func PrettyPrint(data interface{}) {
var p []byte var p []byte
// var err := error // var err := error
p, err := json.MarshalIndent(data, "", "\t") p, err := json.MarshalIndent(data, "", "\t")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
fmt.Printf("%s \n", p) fmt.Printf("%s \n", p)
} }
func GenerateHashSHA256(text string) []byte { func GenerateHashSHA256(text string) []byte {
h := sha256.New() h := sha256.New()
h.Write([]byte(text)) h.Write([]byte(text))
return h.Sum(nil) return h.Sum(nil)
} }
// ValidateHashSHA256 CustomInformationKey the text and check if the text and // ValidateHashSHA256 CustomInformationKey the text and check if the text and
@@ -287,12 +300,12 @@ func GenerateHashSHA256(text string) []byte {
// SHA256 is the current hashing algorthm // SHA256 is the current hashing algorthm
// used. // used.
func ValidateHashSHA256(text string, Hash []byte) bool { func ValidateHashSHA256(text string, Hash []byte) bool {
h := sha256.New() h := sha256.New()
h.Write([]byte(text)) h.Write([]byte(text))
textHash := h.Sum(nil) textHash := h.Sum(nil)
if bytes.Equal(textHash, Hash) { if bytes.Equal(textHash, Hash) {
return true return true
} }
return false return false
} }

View File

@@ -1,136 +1,137 @@
package p2p package p2p
import ( import (
"github.com/Akilan1999/p2p-rendering-computation/config" "github.com/Akilan1999/p2p-rendering-computation/config"
) )
// SpeedTest Runs a speed test and does updates IP tables accordingly // SpeedTest Runs a speed test and does updates IP tables accordingly
func (ip *IpAddresses) SpeedTest() error { func (ip *IpAddresses) SpeedTest() error {
//temp variable to store elements IP addresses and other information //temp variable to store elements IP addresses and other information
// of IP addresses that are pingable // of IP addresses that are pingable
var ActiveIP IpAddresses var ActiveIP IpAddresses
// Index to remove from struct // Index to remove from struct
for _, value := range ip.IpAddress { for _, value := range ip.IpAddress {
var err error var err error
//if len(ip.IpAddress) == 1 { //if len(ip.IpAddress) == 1 {
// i = 0 // i = 0
//} //}
// Ping Test // Ping Test
err = value.PingTest() err = value.PingTest()
if err != nil { if err != nil {
continue continue
} }
//Upload Speed Test //Upload Speed Test
//err = value.UploadSpeed() //err = value.UploadSpeed()
//if err != nil { //if err != nil {
// return err // return err
//} //}
// //
//err = value.DownloadSpeed() //err = value.DownloadSpeed()
//if err != nil { //if err != nil {
// return err // return err
//} //}
//Set value to the list //Set value to the list
ActiveIP.IpAddress = append(ActiveIP.IpAddress, value) ActiveIP.IpAddress = append(ActiveIP.IpAddress, value)
} }
ip.IpAddress = ActiveIP.IpAddress ip.IpAddress = ActiveIP.IpAddress
err := ip.WriteIpTable() err := ip.WriteIpTable()
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// SpeedTestUpdatedIPTable Called when ip tables from httpclient/server is also passed on // SpeedTestUpdatedIPTable Called when ip tables from httpclient/server is also passed on
func (ip *IpAddresses) SpeedTestUpdatedIPTable() error { func (ip *IpAddresses) SpeedTestUpdatedIPTable() error {
Config, err := config.ConfigInit(nil, nil) Config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return err return err
} }
targets, err := ReadIpTable() targets, err := ReadIpTable()
if err != nil { if err != nil {
return err return err
} }
// Checks if baremetal mode and unsafe mode // Checks if baremetal mode and unsafe mode
// is enabled. If it is enabled it adds the // is enabled. If it is enabled it adds the
// the propagated public key to the list. // the propagated public key to the list.
AddPublicKey := false AddPublicKey := false
if Config.BareMetal && Config.UnsafeMode { if Config.BareMetal && Config.UnsafeMode {
AddPublicKey = true AddPublicKey = true
} }
// To ensure struct has no duplicates IP addresses // To ensure struct has no duplicates IP addresses
//DoNotRead := targets //DoNotRead := targets
// Appends all IP addresses // Appends all IP addresses
for i, _ := range targets.IpAddress { for i, _ := range targets.IpAddress {
//To ensure that there are no duplicate IP addresses //To ensure that there are no duplicate IP addresses
Exists := false Exists := false
for k := range ip.IpAddress { for k := range ip.IpAddress {
if AddPublicKey && ip.IpAddress[k].PublicKey != "" { if AddPublicKey && ip.IpAddress[k].PublicKey != "" {
// This function call (AddAuthorisationKey) is inefficient but to be optimised later on. // This function call (AddAuthorisationKey) is inefficient but to be optimised later on.
// This is because when if the user is running on as unsafe mode the authorization file // This is because when if the user is running on as unsafe mode the authorization file
// is opened from the SSH directory and then iterates through every single SSH entry // is opened from the SSH directory and then iterates through every single SSH entry
// to find out if the SSH entry exists or not. This will incur multiple CPU cycles // to find out if the SSH entry exists or not. This will incur multiple CPU cycles
// for no reason. A better approach would be to have been to store the states on memory and only // for no reason. A better approach would be to have been to store the states on memory and only
// add when needed based on the memory location. This is something is to be discussed // add when needed based on the memory location. This is something is to be discussed
// and look upon later on. // and look upon later on.
AddAuthorisationKey(ip.IpAddress[k].PublicKey) AddAuthorisationKey(ip.IpAddress[k].PublicKey)
} }
// Checks if both the IPV4 addresses are the same or the IPV6 address is not targets.IpAddress[i].CustomInformation = ip.IpAddress[k].CustomInformation
// an empty string and IPV6 address are the same // Checks if both the IPV4 addresses are the same or the IPV6 address is not
if (ip.IpAddress[k].Ipv4 == targets.IpAddress[i].Ipv4 && targets.IpAddress[i].NAT) || (targets.IpAddress[i].Ipv6 != "" && ip.IpAddress[k].Ipv6 == targets.IpAddress[i].Ipv6) { // an empty string and IPV6 address are the same
Exists = true if (ip.IpAddress[k].Ipv4 == targets.IpAddress[i].Ipv4 && targets.IpAddress[i].NAT) || (targets.IpAddress[i].Ipv6 != "" && ip.IpAddress[k].Ipv6 == targets.IpAddress[i].Ipv6) {
break Exists = true
} break
} }
}
// If the struct exists then continues // If the struct exists then continues
if Exists { if Exists {
continue continue
} }
ip.IpAddress = append(ip.IpAddress, targets.IpAddress[i]) ip.IpAddress = append(ip.IpAddress, targets.IpAddress[i])
} }
err = ip.SpeedTest() err = ip.SpeedTest()
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// LocalSpeedTestIpTable Runs speed test in iptables locally only // LocalSpeedTestIpTable Runs speed test in iptables locally only
func LocalSpeedTestIpTable() error { func LocalSpeedTestIpTable() error {
targets, err := ReadIpTable() targets, err := ReadIpTable()
if err != nil { if err != nil {
return err return err
} }
err = targets.SpeedTest() err = targets.SpeedTest()
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// Helper function to remove element from an array of a struct // Helper function to remove element from an array of a struct

View File

@@ -1,478 +1,477 @@
package server package server
import ( import (
b64 "encoding/base64" b64 "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/Akilan1999/p2p-rendering-computation/client/clientIPTable" "github.com/Akilan1999/p2p-rendering-computation/client/clientIPTable"
"github.com/Akilan1999/p2p-rendering-computation/config" "github.com/Akilan1999/p2p-rendering-computation/config"
"github.com/Akilan1999/p2p-rendering-computation/p2p" "github.com/Akilan1999/p2p-rendering-computation/p2p"
"github.com/Akilan1999/p2p-rendering-computation/p2p/frp" "github.com/Akilan1999/p2p-rendering-computation/p2p/frp"
"github.com/Akilan1999/p2p-rendering-computation/server/docker" "github.com/Akilan1999/p2p-rendering-computation/server/docker"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os/user" "os/user"
"strconv" "strconv"
"time" "time"
) )
type ReverseProxy struct { type ReverseProxy struct {
IPAddress string IPAddress string
Port string Port string
} }
// ReverseProxies Reverse to the map such as ReverseProxies[<domain nane>]ReverseProxy type // ReverseProxies Reverse to the map such as ReverseProxies[<domain nane>]ReverseProxy type
var ReverseProxies map[string]ReverseProxy var ReverseProxies map[string]ReverseProxy
func Server() (*gin.Engine, error) { func Server() (*gin.Engine, error) {
r := gin.Default() r := gin.Default()
// "The make function allocates and initializes a hash map data // "The make function allocates and initializes a hash map data
//structure and returns a map value that points to it. //structure and returns a map value that points to it.
//The specifics of that data structure are an implementation //The specifics of that data structure are an implementation
//detail of the runtime and are not specified by the language itself." //detail of the runtime and are not specified by the language itself."
// source: https://go.dev/blog/maps // source: https://go.dev/blog/maps
ReverseProxies = make(map[string]ReverseProxy) ReverseProxies = make(map[string]ReverseProxy)
//Get Server port based on the config file //Get Server port based on the config file
config, err := config.ConfigInit(nil, nil) config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// update IPTable with new port and ip address and update ip table // update IPTable with new port and ip address and update ip table
var ProxyIpAddr p2p.IpAddress var ProxyIpAddr p2p.IpAddress
var lowestLatencyIpAddress p2p.IpAddress var lowestLatencyIpAddress p2p.IpAddress
// Gets default information of the server // Gets default information of the server
r.GET("/server_info", func(c *gin.Context) { r.GET("/server_info", func(c *gin.Context) {
c.JSON(http.StatusOK, ServerInfo()) c.JSON(http.StatusOK, ServerInfo())
}) })
// Speed test with 50 mbps // Speed test with 50 mbps
r.GET("/50", func(c *gin.Context) { r.GET("/50", func(c *gin.Context) {
// Get Path from config // Get Path from config
c.File(config.SpeedTestFile) c.File(config.SpeedTestFile)
}) })
// Route build to do a speed test // Route build to do a speed test
r.GET("/upload", func(c *gin.Context) { r.GET("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file") file, _ := c.FormFile("file")
// Upload the file to specific dst. // Upload the file to specific dst.
// c.SaveUploadedFile(file, dst) // c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
}) })
//Gets Ip Table from server node //Gets Ip Table from server node
r.POST("/IpTable", func(c *gin.Context) { r.POST("/IpTable", func(c *gin.Context) {
// Getting IPV4 address of client // Getting IPV4 address of client
//var ClientHost p2p.IpAddress //var ClientHost p2p.IpAddress
// //
//if p2p.Ip4or6(c.ClientIP()) == "version 6" { //if p2p.Ip4or6(c.ClientIP()) == "version 6" {
// ClientHost.Ipv6 = c.ClientIP() // ClientHost.Ipv6 = c.ClientIP()
//} else { //} else {
// ClientHost.Ipv4 = c.ClientIP() // ClientHost.Ipv4 = c.ClientIP()
//} //}
// Variable to store IP table information // Variable to store IP table information
var IPTable p2p.IpAddresses var IPTable p2p.IpAddresses
// Receive file from POST request // Receive file from POST request
body, err := c.FormFile("json") body, err := c.FormFile("json")
if err != nil { if err != nil {
c.String(http.StatusOK, fmt.Sprint(err)) c.String(http.StatusOK, fmt.Sprint(err))
} }
// Open file // Open file
open, err := body.Open() open, err := body.Open()
if err != nil { if err != nil {
c.String(http.StatusOK, fmt.Sprint(err)) c.String(http.StatusOK, fmt.Sprint(err))
} }
// Open received file // Open received file
file, err := ioutil.ReadAll(open) file, err := ioutil.ReadAll(open)
if err != nil { if err != nil {
c.String(http.StatusOK, fmt.Sprint(err)) c.String(http.StatusOK, fmt.Sprint(err))
} }
json.Unmarshal(file, &IPTable) json.Unmarshal(file, &IPTable)
//Add Client IP address to IPTable struct //Add Client IP address to IPTable struct
//IPTable.IpAddress = append(IPTable.IpAddress, ClientHost) //IPTable.IpAddress = append(IPTable.IpAddress, ClientHost)
// Runs speed test to return only servers in the IP table pingable // Runs speed test to return only servers in the IP table pingable
err = IPTable.SpeedTestUpdatedIPTable() err = IPTable.SpeedTestUpdatedIPTable()
if err != nil { if err != nil {
c.String(http.StatusOK, fmt.Sprint(err)) c.String(http.StatusOK, fmt.Sprint(err))
} }
// Reads IP addresses from ip table // Reads IP addresses from ip table
IpAddresses, err := p2p.ReadIpTable() IpAddresses, err := p2p.ReadIpTable()
if err != nil { if err != nil {
c.String(http.StatusOK, fmt.Sprint(err)) c.String(http.StatusOK, fmt.Sprint(err))
} }
c.JSON(http.StatusOK, IpAddresses) c.JSON(http.StatusOK, IpAddresses)
}) })
// Starts docker container in server // Starts docker container in server
r.GET("/startcontainer", func(c *gin.Context) { r.GET("/startcontainer", func(c *gin.Context) {
// Get Number of ports to open and whether to use GPU or not // Get Number of ports to open and whether to use GPU or not
Ports := c.DefaultQuery("ports", "0") Ports := c.DefaultQuery("ports", "0")
GPU := c.DefaultQuery("GPU", "false") GPU := c.DefaultQuery("GPU", "false")
ContainerName := c.DefaultQuery("ContainerName", "") ContainerName := c.DefaultQuery("ContainerName", "")
BaseImage := c.DefaultQuery("BaseImage", "") BaseImage := c.DefaultQuery("BaseImage", "")
PublicKey := c.DefaultQuery("PublicKey", "") PublicKey := c.DefaultQuery("PublicKey", "")
var PortsInt int var PortsInt int
if PublicKey == "" { if PublicKey == "" {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", "Publickey not passed")) c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", "Publickey not passed"))
} }
PublicKeyDecoded, err := b64.StdEncoding.DecodeString(PublicKey) PublicKeyDecoded, err := b64.StdEncoding.DecodeString(PublicKey)
if err != nil { if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
} }
// Convert Get Request value to int // Convert Get Request value to int
fmt.Sscanf(Ports, "%d", &PortsInt) fmt.Sscanf(Ports, "%d", &PortsInt)
fmt.Println(string(PublicKeyDecoded[:])) // Creates container and returns-back result to
// access container
resp, err := docker.BuildRunContainer(PortsInt, GPU, ContainerName, BaseImage, string(PublicKeyDecoded[:]))
// Creates container and returns-back result to if err != nil {
// access container c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
resp, err := docker.BuildRunContainer(PortsInt, GPU, ContainerName, BaseImage, string(PublicKeyDecoded[:])) }
if err != nil { // Ensures that FRP is triggered only if a proxy address is provided
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) if ProxyIpAddr.Ipv4 != "" && c.Request.Host != "localhost:"+config.ServerPort && c.Request.Host != "0.0.0.0:"+config.ServerPort {
} resp, err = frp.StartFRPCDockerContainer(ProxyIpAddr.Ipv4, lowestLatencyIpAddress.ServerPort, resp)
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
}
// Ensures that FRP is triggered only if a proxy address is provided c.JSON(http.StatusOK, resp)
if ProxyIpAddr.Ipv4 != "" && c.Request.Host != "localhost:"+config.ServerPort && c.Request.Host != "0.0.0.0:"+config.ServerPort { })
resp, err = frp.StartFRPCDockerContainer(ProxyIpAddr.Ipv4, lowestLatencyIpAddress.ServerPort, resp)
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
}
c.JSON(http.StatusOK, resp) //Remove container
}) r.GET("/RemoveContainer", func(c *gin.Context) {
ID := c.DefaultQuery("id", "0")
if err := docker.StopAndRemoveContainer(ID); err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
c.String(http.StatusOK, "success")
})
//Remove container //Show images available
r.GET("/RemoveContainer", func(c *gin.Context) { r.GET("/ShowImages", func(c *gin.Context) {
ID := c.DefaultQuery("id", "0") resp, err := docker.ViewAllContainers()
if err := docker.StopAndRemoveContainer(ID); err != nil { if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
} }
c.String(http.StatusOK, "success") c.JSON(http.StatusOK, resp)
}) })
//Show images available // Request for port no from Server with address
r.GET("/ShowImages", func(c *gin.Context) { r.GET("/FRPPort", func(c *gin.Context) {
resp, err := docker.ViewAllContainers() port, err := frp.StartFRPProxyFromRandom()
if err != nil { if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
} }
c.JSON(http.StatusOK, resp)
})
// Request for port no from Server with address c.String(http.StatusOK, strconv.Itoa(port))
r.GET("/FRPPort", func(c *gin.Context) { })
port, err := frp.StartFRPProxyFromRandom()
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
c.String(http.StatusOK, strconv.Itoa(port)) r.GET("/MAPPort", func(c *gin.Context) {
}) Ports := c.DefaultQuery("port", "0")
DomainName := c.DefaultQuery("domain_name", "")
url, _, err := MapPort(Ports, DomainName)
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
r.GET("/MAPPort", func(c *gin.Context) { c.String(http.StatusOK, url)
Ports := c.DefaultQuery("port", "0") })
DomainName := c.DefaultQuery("domain_name", "")
url, _, err := MapPort(Ports, DomainName)
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
c.String(http.StatusOK, url) r.GET("/AddProxy", func(c *gin.Context) {
}) DomainName := c.DefaultQuery("domain_name", "")
ip_address := c.DefaultQuery("ip_address", "")
Ports := c.DefaultQuery("port", "")
r.GET("/AddProxy", func(c *gin.Context) { if DomainName == "" || ip_address == "" || Ports == "" {
DomainName := c.DefaultQuery("domain_name", "") c.String(http.StatusInternalServerError, fmt.Sprintf("All get parameters npt provided"+
ip_address := c.DefaultQuery("ip_address", "") " do ensure domain_name, ip_Address and port no is provided"))
Ports := c.DefaultQuery("port", "") }
if DomainName == "" || ip_address == "" || Ports == "" { err := SaveRegistration(DomainName, ip_address+":"+Ports)
c.String(http.StatusInternalServerError, fmt.Sprintf("All get parameters npt provided"+ if err != nil {
" do ensure domain_name, ip_Address and port no is provided")) c.String(http.StatusInternalServerError, fmt.Sprintf(err.Error()))
} }
err := SaveRegistration(DomainName, ip_address+":"+Ports) //_, ok := ReverseProxies[DomainName]
if err != nil { //// To check if the subdomain entry exists
c.String(http.StatusInternalServerError, fmt.Sprintf(err.Error())) //if ok {
} // c.String(http.StatusInternalServerError, fmt.Sprintf("The domain entry already exists as a reverse"+
// " proxy entry"))
//}
//
//// added proxy as a map entry
//ReverseProxies[DomainName] = ReverseProxy{IPAddress: ip_address, Port: Ports}
c.String(http.StatusOK, "Sucess")
//_, ok := ReverseProxies[DomainName] })
//// To check if the subdomain entry exists
//if ok {
// c.String(http.StatusInternalServerError, fmt.Sprintf("The domain entry already exists as a reverse"+
// " proxy entry"))
//}
//
//// added proxy as a map entry
//ReverseProxies[DomainName] = ReverseProxy{IPAddress: ip_address, Port: Ports}
c.String(http.StatusOK, "Sucess")
}) //r.GET("/RemoveProxy", func(c *gin.Context) {
// DomainName := c.DefaultQuery("domain_name", "")
//
// _, ok := ReverseProxies[DomainName]
// if !ok {
// c.String(http.StatusInternalServerError, fmt.Sprintf("Domain name does exist in entries of proxies"))
// } else {
// delete(ReverseProxies, DomainName)
// }
//
//})
//r.GET("/RemoveProxy", func(c *gin.Context) { // If there is a proxy port specified
// DomainName := c.DefaultQuery("domain_name", "") // then starts the FRP server
// //if config.FRPServerPort != "0" {
// _, ok := ReverseProxies[DomainName] // go frp.StartFRPProxyFromRandom()
// if !ok { //}
// c.String(http.StatusInternalServerError, fmt.Sprintf("Domain name does exist in entries of proxies"))
// } else {
// delete(ReverseProxies, DomainName)
// }
//
//})
// If there is a proxy port specified // Remove nodes currently not pingable
// then starts the FRP server clientIPTable.RemoveOfflineNodes()
//if config.FRPServerPort != "0" {
// go frp.StartFRPProxyFromRandom()
//}
// Remove nodes currently not pingable table, err := p2p.ReadIpTable()
clientIPTable.RemoveOfflineNodes()
table, err := p2p.ReadIpTable() // TODO check if IPV6 or Proxy port is specified
// if not update current entry as proxy address
// with appropriate port on IP Table
if config.BehindNAT {
if err != nil {
return nil, err
}
// TODO check if IPV6 or Proxy port is specified var lowestLatency int64
// if not update current entry as proxy address // random large number
// with appropriate port on IP Table lowestLatency = 10000000
if config.BehindNAT {
if err != nil {
return nil, err
}
var lowestLatency int64 for i, _ := range table.IpAddress {
// random large number // Checks if the ping is the lowest and if the following node is acting as a proxy
lowestLatency = 10000000 //if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && table.IpAddress[i].ProxyPort != "" {
if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && !table.IpAddress[i].NAT {
lowestLatency = table.IpAddress[i].Latency.Milliseconds()
lowestLatencyIpAddress = table.IpAddress[i]
}
}
for i, _ := range table.IpAddress { // If there is an identified node
// Checks if the ping is the lowest and if the following node is acting as a proxy if lowestLatency != 10000000 {
//if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && table.IpAddress[i].ProxyPort != "" { serverPort, err := frp.GetFRPServerPort("http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort)
if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && !table.IpAddress[i].NAT { if err != nil {
lowestLatency = table.IpAddress[i].Latency.Milliseconds() return nil, err
lowestLatencyIpAddress = table.IpAddress[i] }
} // Create 3 second delay to allow FRP server to start
} time.Sleep(1 * time.Second)
// Starts FRP as a client with
proxyPort, err := frp.StartFRPClientForServer(lowestLatencyIpAddress.Ipv4, serverPort, config.ServerPort, "")
if err != nil {
return nil, err
}
// If there is an identified node // updating with the current proxy address
if lowestLatency != 10000000 { ProxyIpAddr.Ipv4 = lowestLatencyIpAddress.Ipv4
serverPort, err := frp.GetFRPServerPort("http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort) ProxyIpAddr.ServerPort = proxyPort
if err != nil { ProxyIpAddr.Name = config.MachineName
return nil, err ProxyIpAddr.NAT = false
} ProxyIpAddr.ProxyServer = false
// Create 3 second delay to allow FRP server to start ProxyIpAddr.EscapeImplementation = "FRP"
time.Sleep(1 * time.Second)
// Starts FRP as a client with
proxyPort, err := frp.StartFRPClientForServer(lowestLatencyIpAddress.Ipv4, serverPort, config.ServerPort, "")
if err != nil {
return nil, err
}
// updating with the current proxy address if config.BareMetal {
ProxyIpAddr.Ipv4 = lowestLatencyIpAddress.Ipv4 _, SSHPort, err := MapPort("22", "")
ProxyIpAddr.ServerPort = proxyPort if err != nil {
ProxyIpAddr.Name = config.MachineName return nil, err
ProxyIpAddr.NAT = false }
ProxyIpAddr.ProxyServer = false ProxyIpAddr.BareMetalSSHPort = SSHPort
ProxyIpAddr.EscapeImplementation = "FRP" }
if config.BareMetal { //ProxyIpAddr.CustomInformationKey = p2p.GenerateHashSHA256(config.IPTableKey)
_, SSHPort, err := MapPort("22", "") // write information back to the IP Table
if err != nil { }
return nil, err
}
ProxyIpAddr.BareMetalSSHPort = SSHPort
}
//ProxyIpAddr.CustomInformationKey = p2p.GenerateHashSHA256(config.IPTableKey) } else {
// write information back to the IP Table ProxyIpAddr.Ipv4, err = p2p.CurrentPublicIP()
} if err != nil {
fmt.Println(err)
}
} else { ProxyIpAddr.ServerPort = config.ServerPort
ProxyIpAddr.Ipv4, err = p2p.CurrentPublicIP() ProxyIpAddr.Name = config.MachineName
if err != nil { ProxyIpAddr.NAT = false
fmt.Println(err) if config.ProxyPort != "" {
} ProxyIpAddr.ProxyServer = true
ProxyIpAddr.ServerPort = config.ServerPort }
ProxyIpAddr.Name = config.MachineName ProxyIpAddr.EscapeImplementation = ""
ProxyIpAddr.NAT = false if config.BareMetal {
if config.ProxyPort != "" { ProxyIpAddr.BareMetalSSHPort = "22"
ProxyIpAddr.ProxyServer = true }
}
ProxyIpAddr.EscapeImplementation = ""
if config.BareMetal {
ProxyIpAddr.BareMetalSSHPort = "22"
}
} }
// Get machine username // Get machine username
currentUser, err := user.Current() currentUser, err := user.Current()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Add username p2prc binary is being run under // Add username p2prc binary is being run under
ProxyIpAddr.MachineUsername = currentUser.Username ProxyIpAddr.MachineUsername = currentUser.Username
ProxyIpAddr.UnSafeMode = config.UnsafeMode ProxyIpAddr.UnSafeMode = config.UnsafeMode
// Adds the public key information to the IPTable // Adds the public key information to the IPTable
// improving transmission of IPTables without the // improving transmission of IPTables without the
// entire public key is future work to be improved // entire public key is future work to be improved
// in the P2PRC protocol level (Improving by adding // in the P2PRC protocol level (Improving by adding
// UDP with TCP reliability layer). // UDP with TCP reliability layer).
ProxyIpAddr.PublicKey, err = config.GetPublicKey() ProxyIpAddr.PublicKey, err = config.GetPublicKey()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// append the following to the ip table // append the following to the ip table
table.IpAddress = append(table.IpAddress, ProxyIpAddr) table.IpAddress = append(table.IpAddress, ProxyIpAddr)
// Writing results to the IPTable // Writing results to the IPTable
err = table.WriteIpTable() err = table.WriteIpTable()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// update ip table // update ip table
go func() error { go func() error {
err := clientIPTable.UpdateIpTableListClient() err := clientIPTable.UpdateIpTableListClient()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return err return err
} }
return nil return nil
}() }()
if config.ProxyPort != "" { if config.ProxyPort != "" {
go ProxyRun(config.ProxyPort) go ProxyRun(config.ProxyPort)
} }
// Run gin server on the specified port // Run gin server on the specified port
go r.Run(":" + config.ServerPort) go r.Run(":" + config.ServerPort)
return r, nil return r, nil
} }
func MapPort(port string, domainName string) (string, string, error) { func MapPort(port string, domainName string) (string, string, error) {
// if server address is provided to do call RESTAPI to remotely open port. // if server address is provided to do call RESTAPI to remotely open port.
//if serverAddress != "" { //if serverAddress != "" {
// requestURL := fmt.Sprintf("http://%v/MAPPort?port=%v&domain_name=%v", serverAddress, port, domainName) // requestURL := fmt.Sprintf("http://%v/MAPPort?port=%v&domain_name=%v", serverAddress, port, domainName)
// req, err := http.NewRequest(http.MethodGet, requestURL, nil) // req, err := http.NewRequest(http.MethodGet, requestURL, nil)
// if err != nil { // if err != nil {
// return "", "", err // return "", "", err
// } // }
// //
// res, err := http.DefaultClient.Do(req) // res, err := http.DefaultClient.Do(req)
// if err != nil { // if err != nil {
// return "", "", err // return "", "", err
// } // }
// //
// resBody, err := io.ReadAll(res.Body) // resBody, err := io.ReadAll(res.Body)
// if err != nil { // if err != nil {
// return "", "", err // return "", "", err
// } // }
// //
// _, Exposedport, err := net.SplitHostPort(string(resBody)) // _, Exposedport, err := net.SplitHostPort(string(resBody))
// if err != nil { // if err != nil {
// return "", "", err // return "", "", err
// } // }
// //
// return string(resBody), Exposedport, nil // return string(resBody), Exposedport, nil
//} //}
//Get Server port based on the config file //Get Server port based on the config file
config, err := config.ConfigInit(nil, nil) config, err := config.ConfigInit(nil, nil)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
// update IPTable with new port and ip address and update ip table // update IPTable with new port and ip address and update ip table
var ProxyIpAddr p2p.IpAddress var ProxyIpAddr p2p.IpAddress
var lowestLatencyIpAddress p2p.IpAddress var lowestLatencyIpAddress p2p.IpAddress
clientIPTable.RemoveOfflineNodes() clientIPTable.RemoveOfflineNodes()
table, err := p2p.ReadIpTable() table, err := p2p.ReadIpTable()
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
var lowestLatency int64 var lowestLatency int64
// random large number // random large number
lowestLatency = 10000000 lowestLatency = 10000000
for i, _ := range table.IpAddress { for i, _ := range table.IpAddress {
// Checks if the ping is the lowest and if the following node is acting as a proxy // Checks if the ping is the lowest and if the following node is acting as a proxy
//if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && table.IpAddress[i].ProxyPort != "" { //if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && table.IpAddress[i].ProxyPort != "" {
if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && !table.IpAddress[i].NAT { if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && !table.IpAddress[i].NAT {
// Filter based on nodes with proxy enabled // Filter based on nodes with proxy enabled
if domainName != "" && table.IpAddress[i].ProxyServer { if domainName != "" && table.IpAddress[i].ProxyServer {
lowestLatency = table.IpAddress[i].Latency.Milliseconds() lowestLatency = table.IpAddress[i].Latency.Milliseconds()
lowestLatencyIpAddress = table.IpAddress[i] lowestLatencyIpAddress = table.IpAddress[i]
continue continue
} }
lowestLatency = table.IpAddress[i].Latency.Milliseconds() lowestLatency = table.IpAddress[i].Latency.Milliseconds()
lowestLatencyIpAddress = table.IpAddress[i] lowestLatencyIpAddress = table.IpAddress[i]
} }
} }
// If there is an identified node // If there is an identified node
if lowestLatency != 10000000 { if lowestLatency != 10000000 {
serverPort, err := frp.GetFRPServerPort("http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort) serverPort, err := frp.GetFRPServerPort("http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
// Create 3 second delay to allow FRP server to start // Create 3 second delay to allow FRP server to start
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
// Starts FRP as a client with // Starts FRP as a client with
proxyPort, err := frp.StartFRPClientForServer(lowestLatencyIpAddress.Ipv4, serverPort, port, "") proxyPort, err := frp.StartFRPClientForServer(lowestLatencyIpAddress.Ipv4, serverPort, port, "")
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
// Doing the proxy mapping for the domain name // Doing the proxy mapping for the domain name
if domainName != "" { if domainName != "" {
fmt.Println("http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort + "/AddProxy?port=" + proxyPort + "&domain_name=" + domainName + "&ip_address=" + lowestLatencyIpAddress.Ipv4) fmt.Println("http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort + "/AddProxy?port=" + proxyPort + "&domain_name=" + domainName + "&ip_address=" + lowestLatencyIpAddress.Ipv4)
URL := "http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort + "/AddProxy?port=" + proxyPort + "&domain_name=" + domainName + "&ip_address=" + lowestLatencyIpAddress.Ipv4 URL := "http://" + lowestLatencyIpAddress.Ipv4 + ":" + lowestLatencyIpAddress.ServerPort + "/AddProxy?port=" + proxyPort + "&domain_name=" + domainName + "&ip_address=" + lowestLatencyIpAddress.Ipv4
//} else { //} else {
// URL = "http://" + IP + ":" + serverPort + "/server_info" // URL = "http://" + IP + ":" + serverPort + "/server_info"
//} //}
http.Get(URL) http.Get(URL)
//SaveRegistration(domainName, lowestLatencyIpAddress.Ipv4+":"+proxyPort) //SaveRegistration(domainName, lowestLatencyIpAddress.Ipv4+":"+proxyPort)
} }
// updating with the current proxy address // updating with the current proxy address
ProxyIpAddr.Ipv4 = lowestLatencyIpAddress.Ipv4 ProxyIpAddr.Ipv4 = lowestLatencyIpAddress.Ipv4
ProxyIpAddr.ServerPort = proxyPort ProxyIpAddr.ServerPort = proxyPort
ProxyIpAddr.Name = config.MachineName ProxyIpAddr.Name = config.MachineName
ProxyIpAddr.NAT = false ProxyIpAddr.NAT = false
ProxyIpAddr.EscapeImplementation = "FRP" ProxyIpAddr.EscapeImplementation = "FRP"
//ProxyIpAddr.CustomInformationKey = p2p.GenerateHashSHA256(config.IPTableKey) //ProxyIpAddr.CustomInformationKey = p2p.GenerateHashSHA256(config.IPTableKey)
} else { } else {
return "", "", errors.New("proxy IP not found") return "", "", errors.New("proxy IP not found")
} }
return ProxyIpAddr.Ipv4 + ":" + ProxyIpAddr.ServerPort, ProxyIpAddr.ServerPort, nil return ProxyIpAddr.Ipv4 + ":" + ProxyIpAddr.ServerPort, ProxyIpAddr.ServerPort, nil
} }

BIN
wasm/p2prc.wasm Executable file

Binary file not shown.