Compare commits
111 Commits
python-bin
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 36930be8e4 | |||
| be1f3308ca | |||
| b3fcc10cd8 | |||
|
|
301a87e758 | ||
| c9f4c8b5c7 | |||
| 10b3c6bae3 | |||
| d4e73e9d34 | |||
| 4c7deb948f | |||
| 54a675e0c0 | |||
| 3c3cfac5e2 | |||
| 6a24da74ef | |||
| 0fe1b0c622 | |||
| 7d3fc9b25f | |||
|
|
6281b0f532 | ||
| 1711b01979 | |||
| 438b4d0424 | |||
| 22c5d61082 | |||
| a1bf13cac3 | |||
| 616bf76bda | |||
| 539bceee7d | |||
| 69b9ea3527 | |||
| 27f4615cd1 | |||
| 01e19d9a8c | |||
| aafc0ebdd7 | |||
| 9de0b1b985 | |||
| 4c12d74789 | |||
| f06df58a8a | |||
| 30004d59c6 | |||
| 4c8cde8857 | |||
| 994c52faf0 | |||
| 5e65381377 | |||
| ff66d55153 | |||
| 50983dbb19 | |||
| 189edd73b1 | |||
| 27670f2408 | |||
| b301a54fba | |||
| 05e29aab0d | |||
| b883feb825 | |||
| 5059444d26 | |||
| be3be96987 | |||
| 682424074f | |||
| 9d0ec3eba4 | |||
| bf87217b9a | |||
| e4638232d3 | |||
| fb6c3ab091 | |||
| a59657a93b | |||
| 30ea1f810a | |||
| fe72625266 | |||
| d2030b7d74 | |||
| 73a283a545 | |||
| 623cfffb6a | |||
| 3669fdfad2 | |||
| f39fab04ce | |||
| 12ff0ceb37 | |||
| 99c62a8dca | |||
| ce5c80a10e | |||
| 46ef4e1db9 | |||
| 14743f7f9b | |||
| a461c2da64 | |||
| 99c9c41528 | |||
| 0e260cf8e4 | |||
| 02e58493f5 | |||
| a46ec3575c | |||
| 2d55643dd9 | |||
| 1ac0647d46 | |||
| a07a412c72 | |||
| e04e918328 | |||
| 0bb949a1e2 | |||
| cf6611087a | |||
| b1a69189d3 | |||
| ad76773111 | |||
| ec46f74c4e | |||
| 3fb225676f | |||
| 0e86459122 | |||
| 77ebb76838 | |||
| 85806ab035 | |||
| 9d92ecb202 | |||
| 5fa625160e | |||
| 403ad72ddb | |||
| 6446681719 | |||
| d50534e270 | |||
| 8a2dfb7b5b | |||
| 0f4f52cb8b | |||
| cd8bb2df8c | |||
| fa5b7a27d5 | |||
| 21f0fc79c1 | |||
| bebe2a02a0 | |||
| 7b0b2404b2 | |||
| 724daf5eda | |||
| c262e612e1 | |||
| 257d91a432 | |||
| 42cf9bd028 | |||
| 7c34c5385a | |||
| d79c28c32f | |||
|
|
7b0d36873b | ||
| 2283736cc6 | |||
| 51b253df15 | |||
| b9f5dec9de | |||
| 30b464449c | |||
| ffd711f448 | |||
| 0d01d70c2b | |||
| 6b1edd3d8a | |||
| 5cf1e144aa | |||
|
|
f605e2e06b | ||
| 873caa911c | |||
| 73a470e201 | |||
| 805514edfa | |||
|
|
5e3a4e8b7a | ||
| 5f8407a37f | |||
| a6939ec35e | |||
|
|
c445ac88c4 |
49
.github/workflows/pages.yml
vendored
49
.github/workflows/pages.yml
vendored
@@ -1,49 +0,0 @@
|
||||
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["main", "master"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
- name: Build with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1
|
||||
with:
|
||||
source: ./
|
||||
destination: ./_site
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -31,6 +31,7 @@ server/docker/containers/
|
||||
|
||||
# generic folder to ignore
|
||||
export/
|
||||
exports/
|
||||
|
||||
# Any testing file
|
||||
*test
|
||||
|
||||
@@ -142,6 +142,24 @@ func MapPort(Port string, DomainName string, ServerAddress string) *C.char {
|
||||
return C.CString(Address.EntireAddress)
|
||||
}
|
||||
|
||||
//export CustomInformation
|
||||
func CustomInformation(CustomInformation string) *C.char {
|
||||
err := abstractions.AddCustomInformation(CustomInformation)
|
||||
if err != nil {
|
||||
return C.CString(err.Error())
|
||||
}
|
||||
return C.CString("Success")
|
||||
}
|
||||
|
||||
//export AddRootNode
|
||||
func AddRootNode(IP string, Port string) *C.char {
|
||||
err := abstractions.AddRootNode(IP, Port)
|
||||
if err != nil {
|
||||
return C.CString(err.Error())
|
||||
}
|
||||
return C.CString("Success")
|
||||
}
|
||||
|
||||
// --------------------------------- Controlling Server ----------------------------------------
|
||||
|
||||
//export Server
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|--------------|-----------------------------------------|
|
||||
| Copyright | Copyright (C) 2006-2024 John MacFarlane |
|
||||
| License | GNU GPL, version 2 or above |
|
||||
| Maintainer | John MacFarlane \<jgm@berkeley.edu\> |
|
||||
| Stability | alpha |
|
||||
| Portability | portable |
|
||||
| Safe Haskell | Safe-Inferred |
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
module Paths_p2prc where
|
||||
import Data.Version
|
||||
version :: Version; version = makeVersion [0,1,0,0]
|
||||
getDataDir :: IO FilePath; getDataDir = return "/Users/akilan/.mcabal/mhs-0.12.3.0/packages/p2prc-0.1.0.0/data"
|
||||
66
Bindings/Haskell/flake.lock
generated
66
Bindings/Haskell/flake.lock
generated
@@ -18,23 +18,41 @@
|
||||
"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": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"p2prc",
|
||||
"p2prc-main",
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"p2prc",
|
||||
"p2prc-main",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733668782,
|
||||
"narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=",
|
||||
"lastModified": 1745875161,
|
||||
"narHash": "sha256-0YkWCS13jpoo3+sX/3kcgdxBNt1VZTmvF+FhZb4rFKI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "514283ec89c39ad0079ff2f3b1437404e4cba608",
|
||||
"rev": "2cbd7fdd6eeab65c494cc426e18f4e4d2a5e35c0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -57,11 +75,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1735834308,
|
||||
"narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=",
|
||||
"lastModified": 1748026106,
|
||||
"narHash": "sha256-6m1Y3/4pVw1RWTsrkAK2VMYSzG4MMIj7sqUy7o8th1o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6df24922a1400241dae323af55f30e4318a6ca65",
|
||||
"rev": "063f43f2dbdef86376cc29ad646c45c46e93234c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -71,28 +89,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"p2prc": {
|
||||
"p2prc-main": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"gomod2nix": "gomod2nix",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 0,
|
||||
"narHash": "sha256-f4e4JcQzBS+R+8HqJbQVeecInm2ggInJuSSymj6gtQo=",
|
||||
"path": "/nix/store/hmk8v0lxifsscll054qsdnzxqfql1998-source",
|
||||
"narHash": "sha256-5qbaxawuLsg8iu9076zlAbLtTFSNJaTz7WERS5rWRxU=",
|
||||
"path": "/nix/store/lg694r23fzc6v5ai3qqcszq1fp44djg2-source",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"path": "/nix/store/hmk8v0lxifsscll054qsdnzxqfql1998-source",
|
||||
"path": "/nix/store/lg694r23fzc6v5ai3qqcszq1fp44djg2-source",
|
||||
"type": "path"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"p2prc": "p2prc",
|
||||
"utils": "utils"
|
||||
"p2prc-main": "p2prc-main"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
@@ -124,24 +142,6 @@
|
||||
"repo": "default",
|
||||
"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",
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
{
|
||||
description = "Nix flake for P2PRC Haskell library";
|
||||
|
||||
inputs = {
|
||||
p2prc.url = "../../";
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
p2prc-main.url = "../../";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, utils, p2prc }: utils.lib.eachDefaultSystem (system:
|
||||
outputs = { nixpkgs, flake-utils, p2prc-main, ... }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
|
||||
packages.default = pkgs.haskellPackages.callPackage ./project.nix {};
|
||||
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
cabal2nix
|
||||
cabal-install
|
||||
haskell.compiler.ghc96
|
||||
ghc
|
||||
zlib.dev
|
||||
p2prc.outputs.packages.${system}.default
|
||||
p2prc-main.packages.${system}.default
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ module API
|
||||
( P2PRCapi(..)
|
||||
, MapPortRequest(..)
|
||||
, p2prcAPI
|
||||
, P2PRCommands(..)
|
||||
)
|
||||
where
|
||||
|
||||
@@ -13,6 +14,7 @@ import System.Process ( ProcessHandle )
|
||||
|
||||
import Data.Aeson ( FromJSON )
|
||||
|
||||
|
||||
import Error
|
||||
( IOEitherError
|
||||
)
|
||||
@@ -20,19 +22,21 @@ import Error
|
||||
import JSON
|
||||
( IPAddressTable(..)
|
||||
, MapPortResponse(..)
|
||||
, P2prcConfig
|
||||
, P2PRCConfig
|
||||
)
|
||||
|
||||
import CLI
|
||||
( StdInput(..)
|
||||
, CLIOpt(..)
|
||||
, eitherErrDecode
|
||||
, p2PrcCmdName
|
||||
, p2prcCmdName
|
||||
, eitherExecProcess
|
||||
, eitherExecProcessParser
|
||||
, spawnProcP2Prc
|
||||
, spawnProcP2PRC
|
||||
)
|
||||
|
||||
-- import System.Environment (lookupEnv)
|
||||
|
||||
|
||||
|
||||
-- | Lower level P2PRC Haskell api that exposes basic functionality necessary to joint the network.
|
||||
@@ -41,21 +45,32 @@ data P2PRCapi
|
||||
= MkP2PRCapi
|
||||
{ startServer :: IOEitherError ProcessHandle
|
||||
-- ^ Start server
|
||||
, execInitConfig :: IOEitherError P2prcConfig
|
||||
-- , execInitConfig :: IOEitherError P2PRCConfig
|
||||
-- ^ Instantiate server configuration
|
||||
, execListServers :: IOEitherError IPAddressTable
|
||||
-- ^ List servers in network
|
||||
, execMapPort :: MapPortRequest -> IOEitherError MapPortResponse
|
||||
-- ^ 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.
|
||||
data MapPortRequest =
|
||||
MkMapPortRequest -- ^ P2PRC's port allocation request value
|
||||
Int -- ^ TCP socket number
|
||||
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.
|
||||
@@ -92,16 +107,22 @@ 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" #-}
|
||||
p2prcAPI :: P2PRCapi
|
||||
p2prcAPI =
|
||||
p2prcAPI = let
|
||||
|
||||
execTemp :: Maybe MapPortRequest -> PassCustomInformation -> IOEitherError String
|
||||
execTemp mp ci = undefined
|
||||
|
||||
|
||||
in
|
||||
MkP2PRCapi
|
||||
{ startServer = spawnProcP2Prc p2PrcCmdName [ MkOptAtomic "--s" ]
|
||||
{ startServer = spawnProcP2PRC p2prcCmdName [ MkOptAtomic "-s" ]
|
||||
|
||||
, execListServers =
|
||||
execProcP2PrcParser [ MkOptAtomic "--ls" ] MkEmptyStdInput
|
||||
execProcP2PRCParser [ MkOptAtomic "--ls" ] MkEmptyStdInput
|
||||
|
||||
, execMapPort =
|
||||
\ (MkMapPortRequest portNumber domainName) ->
|
||||
execProcP2PrcParser
|
||||
execProcP2PRCParser
|
||||
[ MkOptTuple
|
||||
( "--mp"
|
||||
, show portNumber
|
||||
@@ -113,39 +134,44 @@ p2prcAPI =
|
||||
]
|
||||
MkEmptyStdInput
|
||||
|
||||
, execInitConfig = do
|
||||
, execMaybeMapPortAndAddCustomInformation = execTemp
|
||||
, execAddCustomInformation = execTemp Nothing
|
||||
|
||||
confInitRes <- execProcP2Prc [ MkOptAtomic "--dc" ] MkEmptyStdInput
|
||||
-- , execInitConfig = do
|
||||
|
||||
case confInitRes of
|
||||
(Right _) -> do
|
||||
-- confInitRes <- execProcP2PRC [ MkOptAtomic "--dc" ] MkEmptyStdInput
|
||||
|
||||
-- TODO: get config file name dynamically
|
||||
--
|
||||
currDirectory <- getCurrentDirectory
|
||||
-- case confInitRes of
|
||||
-- (Right _) -> do
|
||||
|
||||
-- TODO: change values before loading file
|
||||
let fname = currDirectory ++ "/config.json" :: FilePath
|
||||
-- maybeValue <- lookupEnv "P2PRC"
|
||||
|
||||
-- -- TODO: get config file name dynamically
|
||||
-- --
|
||||
-- currDirectory <- getCurrentDirectory
|
||||
|
||||
-- -- TODO: change values before loading file
|
||||
-- let fname = currDirectory ++ "/config.json" :: FilePath
|
||||
|
||||
|
||||
-- TODO: read config check if file exists
|
||||
configContent <- readFile fname
|
||||
-- -- TODO: read config check if file exists
|
||||
-- configContent <- readFile fname
|
||||
|
||||
pure $ eitherErrDecode configContent
|
||||
-- pure $ eitherErrDecode configContent
|
||||
|
||||
(Left err) -> pure $ Left err
|
||||
-- (Left err) -> pure $ Left err
|
||||
|
||||
}
|
||||
|
||||
|
||||
where
|
||||
|
||||
execProcP2PrcParser ::
|
||||
execProcP2PRCParser ::
|
||||
FromJSON a =>
|
||||
[CLIOpt] -> StdInput -> IOEitherError a
|
||||
|
||||
execProcP2PrcParser = eitherExecProcessParser p2PrcCmdName
|
||||
execProcP2PRCParser = eitherExecProcessParser p2prcCmdName
|
||||
-- TODO: GHC question, why does it scope down instead staying generic
|
||||
|
||||
execProcP2Prc = eitherExecProcess p2PrcCmdName
|
||||
-- execProcP2PRC = eitherExecProcess p2prcCmdName
|
||||
|
||||
|
||||
@@ -87,8 +87,8 @@ optsToCLI = concatMap _optToCLI
|
||||
_optToCLI (MkOptTuple (o, v)) = [o, v]
|
||||
|
||||
|
||||
spawnProcP2Prc :: CLICmd -> [CLIOpt] -> IOEitherError ProcessHandle
|
||||
spawnProcP2Prc cmd opts =
|
||||
spawnProcP2PRC :: CLICmd -> [CLIOpt] -> IOEitherError ProcessHandle
|
||||
spawnProcP2PRC cmd opts =
|
||||
do
|
||||
let prc = proc cmd $ optsToCLI opts
|
||||
|
||||
@@ -115,6 +115,6 @@ eitherErrorDecode esa =
|
||||
|
||||
-- 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"
|
||||
p2PrcCmdName :: String
|
||||
p2PrcCmdName = "p2p-rendering-computation"
|
||||
p2prcCmdName :: String
|
||||
p2prcCmdName = "p2p-rendering-computation"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
module Engine
|
||||
( runP2PRC
|
||||
, fn
|
||||
)
|
||||
where
|
||||
|
||||
@@ -18,7 +19,7 @@ import System.Process ( terminateProcess )
|
||||
import API
|
||||
( P2PRCapi(..)
|
||||
, MapPortRequest(..)
|
||||
, p2prcAPI
|
||||
, p2prcAPI, P2PRCommands
|
||||
)
|
||||
|
||||
|
||||
@@ -58,14 +59,10 @@ import API
|
||||
@
|
||||
-}
|
||||
runP2PRC
|
||||
:: MapPortRequest -- ^ TCP Port Request
|
||||
:: [P2PRCommands] -- ^ TCP Port Request
|
||||
-> IO ()
|
||||
runP2PRC
|
||||
( MkMapPortRequest
|
||||
portNumber
|
||||
domainName
|
||||
)
|
||||
=
|
||||
runP2PRC [] = putStrLn ""
|
||||
runP2PRC nonEmpty@(_:_) =
|
||||
let
|
||||
|
||||
--
|
||||
@@ -95,9 +92,10 @@ runP2PRC
|
||||
|
||||
( MkP2PRCapi
|
||||
{ startServer = startServer
|
||||
, execInitConfig = execInitConfig
|
||||
-- , execInitConfig = execInitConfig
|
||||
, execListServers = execListServers
|
||||
, execMapPort = execMapPort
|
||||
, execAddCustomInformation = _
|
||||
}
|
||||
) = p2prcAPI
|
||||
|
||||
@@ -105,18 +103,19 @@ runP2PRC
|
||||
let
|
||||
|
||||
|
||||
configValue <- execInitConfig
|
||||
-- configValue <- execInitConfig
|
||||
|
||||
-- TODO: get name of host server from config json
|
||||
|
||||
print configValue
|
||||
putStrLn "\n\n\n"
|
||||
-- print configValue
|
||||
-- putStrLn "\n\n\n"
|
||||
|
||||
eitherStartProcessHandle <- startServer
|
||||
|
||||
|
||||
case eitherStartProcessHandle of
|
||||
(Right startProcessHandle) -> do
|
||||
(Right startProcessHandle) ->
|
||||
do
|
||||
|
||||
let sleepNSecs i = threadDelay (i * 1000000)
|
||||
|
||||
@@ -125,7 +124,8 @@ runP2PRC
|
||||
outputStr <- execListServers
|
||||
print outputStr
|
||||
|
||||
mapPortOut <- execMapPort $ MkMapPortRequest portNumber domainName
|
||||
-- mapPortOut <- execMapPort $ MkMapPortRequest portNumber domainName
|
||||
mapPortOut <- execMapPort $ MkMapPortRequest undefined undefined
|
||||
|
||||
case mapPortOut of
|
||||
(Right v) -> print v
|
||||
@@ -159,3 +159,7 @@ runP2PRC
|
||||
when (toLower c /= 'q') $ exitOnQ exitF
|
||||
exitF
|
||||
|
||||
|
||||
|
||||
fn :: undefined
|
||||
fn = undefined
|
||||
@@ -1,7 +1,7 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module JSON
|
||||
( P2prcConfig(..)
|
||||
( P2PRCConfig(..)
|
||||
, IPAddressTable(..)
|
||||
, IPAddress(..)
|
||||
, ServerInfo(..)
|
||||
@@ -38,11 +38,11 @@ instance FromJSON MapPortResponse where
|
||||
|
||||
|
||||
|
||||
{-# WARNING P2prcConfig "This type is unstable at the moment due to the P2PRC's library error handling bug. For more information visit: https://github.com/Akilan1999/p2p-rendering-computation/issues/114#issuecomment-2474737015" #-}
|
||||
{-# WARNING P2PRCConfig "This type is unstable at the moment due to the P2PRC's library error handling bug. For more information visit: https://github.com/Akilan1999/p2p-rendering-computation/issues/114#issuecomment-2474737015" #-}
|
||||
|
||||
-- | This represents the server configuration that defines its attributes and behaviours in the network, as well as, the location of the runtime persistence artifacts.
|
||||
newtype P2prcConfig
|
||||
= MkP2prConfig
|
||||
newtype P2PRCConfig
|
||||
= MkP2PRCConfig
|
||||
{ machineName :: String -- ^ Machine Name
|
||||
} deriving Show
|
||||
|
||||
@@ -96,13 +96,13 @@ newtype P2prcConfig
|
||||
-- to have a dedicated port field
|
||||
|
||||
|
||||
instance FromJSON P2prcConfig where
|
||||
instance FromJSON P2PRCConfig where
|
||||
parseJSON (Object o) = do
|
||||
|
||||
machineName <- o .: "MachineName"
|
||||
|
||||
pure
|
||||
$ MkP2prConfig
|
||||
$ MkP2PRCConfig
|
||||
{ machineName=machineName
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,10 @@ module P2PRC
|
||||
-- ** Interface data types
|
||||
-- | This section gives an overview on the runtime and host machine interfaces.
|
||||
, P2PRCapi(..)
|
||||
, P2prcConfig(..)
|
||||
, P2PRCConfig(..)
|
||||
, MapPortRequest(..)
|
||||
, MapPortResponse(..)
|
||||
, P2PRCommands(..)
|
||||
|
||||
-- ** Primitive data types
|
||||
-- | These types represent the core data that is communicated between requests and the runtime.
|
||||
@@ -76,13 +77,14 @@ import JSON
|
||||
, ServerInfo(..)
|
||||
, IPAddress(..)
|
||||
, MapPortResponse(..)
|
||||
, P2prcConfig(..)
|
||||
, P2PRCConfig(..)
|
||||
)
|
||||
|
||||
import API
|
||||
( MapPortRequest(..)
|
||||
, P2PRCapi(..)
|
||||
, p2prcAPI
|
||||
, P2PRCommands(..)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
z
|
||||
13
Bindings/Haskell/project.nix
Normal file
13
Bindings/Haskell/project.nix
Normal 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";
|
||||
}
|
||||
@@ -3,10 +3,14 @@ module Main where
|
||||
import P2PRC
|
||||
( runP2PRC
|
||||
, MapPortRequest(MkMapPortRequest)
|
||||
, P2PRCommands(MapPort)
|
||||
)
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
main =let
|
||||
in
|
||||
runP2PRC
|
||||
( MkMapPortRequest 8080 "jose.akilan.io"
|
||||
)
|
||||
[
|
||||
( MapPort $ MkMapPortRequest 8080 "jose.akilan.io")
|
||||
]
|
||||
|
||||
|
||||
@@ -44,9 +44,9 @@ class Node:
|
||||
Upload: int
|
||||
ServerPort: str
|
||||
BareMetalSSHPort: str
|
||||
NAT: str
|
||||
NAT: bool
|
||||
EscapeImplementation: str
|
||||
ProxyServer: str
|
||||
ProxyServer: bool
|
||||
UnSafeMode: bool
|
||||
PublicKey: str
|
||||
CustomInformation: str
|
||||
@@ -98,5 +98,32 @@ def ListNodes():
|
||||
dat: IPAddress = dacite.from_dict(IPAddress,ipTableObject)
|
||||
return dat
|
||||
|
||||
# Add a root node to P2RRC and overwrites all other nodes.
|
||||
# To be only added before the network started and with
|
||||
# the intention of a fresh protocol.
|
||||
def AddRootNode(ip="", port="") -> bool:
|
||||
ip = go_string(c_char_p(ip.encode('utf-8')), len(ip))
|
||||
port = go_string(c_char_p(port.encode('utf-8')), len(port))
|
||||
|
||||
p2prc.AddRootNode.restype = c_char_p
|
||||
res = p2prc.AddRootNode(ip, port)
|
||||
if str(res).strip("b'") == "Success":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
||||
# python function to pass-through custom
|
||||
# information to interpret which can be
|
||||
# interpreted as a DSL.
|
||||
def AddCustomInformation(message="") -> bool:
|
||||
message = go_string(c_char_p(message.encode('utf-8')), len(message))
|
||||
|
||||
p2prc.CustomInformation.restype = c_char_p
|
||||
status = p2prc.CustomInformation(message)
|
||||
if str(status).strip("b'") == "Success":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,3 +5,10 @@ if __name__ == "__main__":
|
||||
P2PRCNodes = ListNodes()
|
||||
# Print nodes in the network
|
||||
print(P2PRCNodes)
|
||||
|
||||
# Add custom information to the network
|
||||
if AddCustomInformation("Test"):
|
||||
print("It worked")
|
||||
|
||||
if AddRootNode("0.0.0.0", "8081"):
|
||||
print("It worked for adding root node")
|
||||
|
||||
BIN
Docs/Colored-On-Light-Image.png
Normal file
BIN
Docs/Colored-On-Light-Image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 276 KiB |
820
Docs/index.html
820
Docs/index.html
File diff suppressed because it is too large
Load Diff
202
Docs/index.org
202
Docs/index.org
@@ -3,16 +3,20 @@
|
||||
#+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
|
||||
[[./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]]
|
||||
|
||||
*** Source code: https://github.com/Akilan1999/p2p-rendering-computation
|
||||
|
||||
* Introduction
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: chapter1-introduction
|
||||
:END:
|
||||
|
||||
** Abstract
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: abstract
|
||||
: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
|
||||
@@ -178,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
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: using-basic-commands
|
||||
@@ -352,6 +460,14 @@ p2prc --mp <port no to map> --dn <domain name to link Mapped port against> --ra
|
||||
|
||||
--------------
|
||||
|
||||
*** Add root node
|
||||
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
|
||||
the intention of a fresh instance.
|
||||
#+begin_example
|
||||
p2prc --arn --ip <root node ip address> -p <root node port no>
|
||||
#+end_example
|
||||
|
||||
* P2P Module Implementation
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: p2p-module-implementation
|
||||
@@ -363,7 +479,7 @@ to determine upload and download speeds. This valuable information enables users
|
||||
servers with optimal performance, enhancing their overall network experience.
|
||||
|
||||
#+caption: UML diagram of P2P module
|
||||
[[file:images/p2pmoduleArch.png]]
|
||||
[[file:./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
|
||||
@@ -656,7 +772,24 @@ called from other programming languages.
|
||||
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...]]
|
||||
**** 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
|
||||
:PROPERTIES:
|
||||
@@ -678,18 +811,28 @@ configuration file was generated using JSON format.
|
||||
|
||||
#+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",
|
||||
"DockerContainers": "/Users/akilan/Documents/p2p-rendering-computation/server/docker/containers/",
|
||||
"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",
|
||||
"IPV6Address": "",
|
||||
"PluginPath": "/Users/akilan/Documents/p2p-rendering-computation/plugin/deploy",
|
||||
"TrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/trackcontainers.json",
|
||||
"ServerPort": "8088",
|
||||
"ProxyPort": "",
|
||||
"GroupTrackContainersPath": "/Users/akilan/Documents/p2p-rendering-computation/client/trackcontainers/grouptrackcontainers.json",
|
||||
"FRPServerPort": "True",
|
||||
"BehindNAT": "True",
|
||||
"FRPServerPort": 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
|
||||
}
|
||||
#+end_src
|
||||
@@ -771,10 +914,48 @@ func main() {
|
||||
}
|
||||
}
|
||||
#+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
|
||||
** Self host within 5 minutes any program
|
||||
|
||||
@@ -783,6 +964,7 @@ func main() {
|
||||
|
||||
|
||||
- Video tutorial:
|
||||
#+attr_html: :class video
|
||||
[[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
|
||||
|
||||
1
Docs/kill-docs.sh
Normal file
1
Docs/kill-docs.sh
Normal file
@@ -0,0 +1 @@
|
||||
kill -9 $(lsof -t -i:8083)
|
||||
1
Docs/run-docs.sh
Normal file
1
Docs/run-docs.sh
Normal file
@@ -0,0 +1 @@
|
||||
go run staticServer.go &
|
||||
28
Docs/staticServer.go
Normal file
28
Docs/staticServer.go
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Serve is a very simple static file server in go
|
||||
Usage:
|
||||
|
||||
-p="8100": port to serve on
|
||||
-d=".": the directory of static files to host
|
||||
|
||||
Navigating to http://localhost:8100 will display the index.html or directory
|
||||
listing file.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
port := flag.String("p", "8083", "port to serve on")
|
||||
directory := flag.String("d", ".", "the directory of static file to host")
|
||||
flag.Parse()
|
||||
|
||||
http.Handle("/", http.FileServer(http.Dir(*directory)))
|
||||
|
||||
log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
|
||||
log.Fatal(http.ListenAndServe(":"+*port, nil))
|
||||
}
|
||||
@@ -23,3 +23,7 @@ h4,h5,h6{
|
||||
color: #b96c29;
|
||||
font-weight:300;
|
||||
}
|
||||
|
||||
.video {
|
||||
box-shadow: 10px 10px 25px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
3
Makefile
3
Makefile
@@ -14,6 +14,9 @@ sharedObjects:
|
||||
python:
|
||||
sh build-python-package.sh
|
||||
|
||||
haskell:
|
||||
sh build-haskell.sh
|
||||
|
||||
clean:
|
||||
go clean -modcache
|
||||
rm -fr .go-build vendor result*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
> [!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]
|
||||
> 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
|
||||
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
|
||||
[](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
|
||||
|
||||
### 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 -->
|
||||
<!-- 1. [Introduction](#Introduction)
|
||||
|
||||
132
Simulation/simulation.sh
Normal file
132
Simulation/simulation.sh
Normal 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
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package abstractions
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"github.com/Akilan1999/p2p-rendering-computation/client"
|
||||
"github.com/Akilan1999/p2p-rendering-computation/client/clientIPTable"
|
||||
@@ -86,3 +85,24 @@ func ViewIPTable() (table *p2p.IpAddresses, err error) {
|
||||
func UpdateIPTable() (err error) {
|
||||
return clientIPTable.UpdateIpTableListClient()
|
||||
}
|
||||
|
||||
// AddCustomInformation allows to pass custom information
|
||||
// through the network to which can be listened on
|
||||
// all peers in the network to execute a task.
|
||||
func AddCustomInformation(information string) error {
|
||||
return clientIPTable.AddCustomInformationToIPTable(information)
|
||||
}
|
||||
|
||||
// AddRootNode Adds root node to the network by using defaults except for
|
||||
// ip address and port no. Supports only IPV4 as of now.
|
||||
func AddRootNode(rootIP string, portNo string) error {
|
||||
var rootNode []p2p.IpAddress
|
||||
|
||||
rootNode = append(rootNode, p2p.IpAddress{
|
||||
Name: "",
|
||||
Ipv4: rootIP,
|
||||
ServerPort: portNo,
|
||||
NAT: false,
|
||||
})
|
||||
return generate.GenerateIPTableFile(rootNode)
|
||||
}
|
||||
|
||||
BIN
artwork/.DS_Store
vendored
BIN
artwork/.DS_Store
vendored
Binary file not shown.
BIN
artwork/p2prc-logos/.DS_Store
vendored
BIN
artwork/p2prc-logos/.DS_Store
vendored
Binary file not shown.
BIN
artwork/p2prc-logos/P2PRCHaskell.png
Normal file
BIN
artwork/p2prc-logos/P2PRCHaskell.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 922 KiB |
31
build-haskell.sh
Normal file
31
build-haskell.sh
Normal 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"
|
||||
@@ -31,7 +31,7 @@ func AddCustomInformationToIPTable(text string) error {
|
||||
if found {
|
||||
table.WriteIpTable()
|
||||
// update IPTable after modified entry
|
||||
go UpdateIpTableListClient()
|
||||
UpdateIpTableListClient()
|
||||
} else {
|
||||
return errors.New("start server with p2prc -s as the server is currently not running")
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ func UpdateIpTableListClient() error {
|
||||
|
||||
Exists := false
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -136,11 +136,12 @@ func UpdateIpTableListClient() error {
|
||||
}
|
||||
|
||||
w.Add(1)
|
||||
if Addresses.IpAddress[j].Ipv6 != "" {
|
||||
go UpdateIpTable(Addresses.IpAddress[j].Ipv6, Addresses.IpAddress[j].ServerPort, &w)
|
||||
} else if Addresses.IpAddress[j].Ipv4 != currentIPV4 {
|
||||
//if Addresses.IpAddress[j].Ipv6 != "" {
|
||||
// go UpdateIpTable(Addresses.IpAddress[j].Ipv6, Addresses.IpAddress[j].ServerPort, &w)
|
||||
//} 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()
|
||||
|
||||
//Appends server1 IP address to variable DoNotRead
|
||||
|
||||
133
cmd/action.go
133
cmd/action.go
@@ -2,8 +2,10 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Akilan1999/p2p-rendering-computation/abstractions"
|
||||
"github.com/Akilan1999/p2p-rendering-computation/client"
|
||||
"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/p2p"
|
||||
"github.com/Akilan1999/p2p-rendering-computation/plugin"
|
||||
@@ -87,11 +89,7 @@ var CliAction = func(ctx *cli.Context) error {
|
||||
// Displays all images available on the server side
|
||||
if ViewImages != "" {
|
||||
imageRes, err := client.ViewContainers(ViewImages)
|
||||
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
client.PrettyPrint(imageRes)
|
||||
standardOutput(err, imageRes)
|
||||
}
|
||||
|
||||
// Function called to stop and remove server from Docker
|
||||
@@ -100,9 +98,7 @@ var CliAction = func(ctx *cli.Context) error {
|
||||
fmt.Println("provide container ID via --ID or --id")
|
||||
} else {
|
||||
err := client.RemoveContianer(RemoveVM, ID)
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,58 +115,40 @@ var CliAction = func(ctx *cli.Context) error {
|
||||
// Calls function to do Api call to start the container on the server side
|
||||
imageRes, err := client.StartContainer(CreateVM, PortsInt, GPU, ContainerName, BaseImage)
|
||||
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
client.PrettyPrint(imageRes)
|
||||
standardOutput(err, imageRes)
|
||||
}
|
||||
|
||||
//Call if specs flag is called
|
||||
if Specs != "" {
|
||||
specs, err := client.GetSpecs(Specs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Pretty print
|
||||
client.PrettyPrint(specs)
|
||||
standardOutput(err, specs)
|
||||
}
|
||||
|
||||
//Sets default paths to the config file
|
||||
if SetDefaultConfig {
|
||||
_, err := generate.SetDefaults("P2PRC", false, nil, false)
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
|
||||
//If the network interface flag is called
|
||||
// Then all the network interfaces are displayed
|
||||
if NetworkInterface {
|
||||
err := p2p.ViewNetworkInterface()
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
|
||||
// If the view plugin flag is called then display all
|
||||
// plugins available
|
||||
if ViewPlugin {
|
||||
plugins, err := plugin.DetectPlugins()
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
client.PrettyPrint(plugins)
|
||||
standardOutput(err, plugins)
|
||||
}
|
||||
|
||||
// If the flag Tracked Container is called or the flag
|
||||
// --tc
|
||||
if TrackedContainers {
|
||||
err, trackedContainers := client.ViewTrackedContainers()
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
client.PrettyPrint(trackedContainers)
|
||||
standardOutput(err, trackedContainers)
|
||||
}
|
||||
|
||||
//Executing plugin when the plugin flag is called
|
||||
@@ -179,14 +157,11 @@ var CliAction = func(ctx *cli.Context) error {
|
||||
// To execute plugin requires the container ID or group ID provided when being executed
|
||||
if ID != "" {
|
||||
err := plugin.CheckRunPlugin(ExecutePlugin, ID)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("Success")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("provide container ID via --ID or --id")
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
//else {
|
||||
// fmt.Println("provide container ID via --ID or --id")
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
@@ -194,10 +169,7 @@ var CliAction = func(ctx *cli.Context) error {
|
||||
// Creates new group and outputs JSON file
|
||||
if CreateGroup {
|
||||
group, err := client.CreateGroup()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.PrettyPrint(group)
|
||||
standardOutput(err, group)
|
||||
}
|
||||
|
||||
// Actions to be performed when the
|
||||
@@ -208,26 +180,14 @@ var CliAction = func(ctx *cli.Context) error {
|
||||
// --rmcgroup --id <contianer id>
|
||||
if RemoveContainerGroup && ID != "" {
|
||||
group, err := client.RemoveContainerGroup(ID, Group)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
client.PrettyPrint(group)
|
||||
}
|
||||
standardOutput(err, group)
|
||||
} else if ID != "" { // Add container to group based on group ID provided
|
||||
// --id <Container ID>
|
||||
group, err := client.AddContainerToGroup(ID, Group)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
client.PrettyPrint(group)
|
||||
}
|
||||
standardOutput(err, group)
|
||||
} else { // View all information about current group
|
||||
group, err := client.GetGroup(Group)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
client.PrettyPrint(group)
|
||||
}
|
||||
standardOutput(err, group)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,63 +196,58 @@ var CliAction = func(ctx *cli.Context) error {
|
||||
// --rmgroup
|
||||
if RemoveGroup != "" {
|
||||
err := client.RemoveGroup(RemoveGroup)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("Group Removed")
|
||||
}
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
|
||||
// Execute Function to view all groups
|
||||
if Groups {
|
||||
groups, err := client.ReadGroup()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
client.PrettyPrint(groups)
|
||||
}
|
||||
standardOutput(err, groups)
|
||||
}
|
||||
|
||||
//--------------------------------
|
||||
|
||||
if PullPlugin != "" {
|
||||
err := plugin.DownloadPlugin(PullPlugin)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("Success")
|
||||
}
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
|
||||
if RemovePlugin != "" {
|
||||
err := plugin.DeletePlugin(RemovePlugin)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("Success")
|
||||
}
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
|
||||
if AddMetaData != "" {
|
||||
err := clientIPTable.AddCustomInformationToIPTable(AddMetaData)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("Success")
|
||||
}
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
|
||||
if MAPPort != "" {
|
||||
address, err := client.MAPPort(MAPPort, DomainName, RemoteAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
standardOutput(err, address)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
client.PrettyPrint(address)
|
||||
|
||||
// Action called to add root node to network
|
||||
if AddRootNode && IP != "" && Ports != "" {
|
||||
err := abstractions.AddRootNode(IP, Ports)
|
||||
standardOutput(err, nil)
|
||||
}
|
||||
|
||||
// display configuration
|
||||
if ViewConfig {
|
||||
config, err := Config.ConfigInit(nil, nil)
|
||||
standardOutput(err, config)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func standardOutput(err error, i interface{}) {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else if i == nil {
|
||||
fmt.Println("Success")
|
||||
} else {
|
||||
client.PrettyPrint(i)
|
||||
}
|
||||
}
|
||||
|
||||
24
cmd/flags.go
24
cmd/flags.go
@@ -41,6 +41,9 @@ var (
|
||||
PullPlugin string
|
||||
RemovePlugin string
|
||||
AddMetaData string
|
||||
AddRootNode bool
|
||||
IP string
|
||||
ViewConfig bool
|
||||
)
|
||||
|
||||
var AppConfigFlags = []cli.Flag{
|
||||
@@ -272,4 +275,25 @@ var AppConfigFlags = []cli.Flag{
|
||||
EnvVars: []string{"ADDMETADATA"},
|
||||
Destination: &AddMetaData,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "AddRootNode",
|
||||
Aliases: []string{"arn"},
|
||||
Usage: "Adds initial root node to talk to",
|
||||
EnvVars: []string{"ADDROOTNODE"},
|
||||
Destination: &AddRootNode,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "IPAddress",
|
||||
Aliases: []string{"ip"},
|
||||
Usage: "IP address",
|
||||
EnvVars: []string{"IP"},
|
||||
Destination: &IP,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ViewConfig",
|
||||
Aliases: []string{"vc"},
|
||||
Usage: "View config file",
|
||||
EnvVars: []string{"VC"},
|
||||
Destination: &ViewConfig,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ type Config struct {
|
||||
ServerPort string
|
||||
ProxyPort string
|
||||
GroupTrackContainersPath string
|
||||
FRPServerPort string
|
||||
BehindNAT string
|
||||
FRPServerPort bool
|
||||
BehindNAT bool
|
||||
IPTableKey string
|
||||
PublicKeyFile string
|
||||
PrivateKeyFile string
|
||||
@@ -38,7 +38,9 @@ type Config struct {
|
||||
KeyFile string
|
||||
BareMetal bool
|
||||
UnsafeMode bool
|
||||
Test bool
|
||||
CustomConfig interface{}
|
||||
ConfigPath string
|
||||
//NetworkInterface string
|
||||
//NetworkInterfaceIPV6Index int
|
||||
}
|
||||
|
||||
@@ -78,9 +78,9 @@ func SetDefaults(envName string, forceDefault bool, CustomConfig interface{}, No
|
||||
Defaults.GroupTrackContainersPath = defaultPath + "client/trackcontainers/grouptrackcontainers.json"
|
||||
Defaults.ServerPort = "8088"
|
||||
Defaults.ProxyPort = ""
|
||||
Defaults.FRPServerPort = "True"
|
||||
Defaults.FRPServerPort = true
|
||||
Defaults.CustomConfig = CustomConfig
|
||||
Defaults.BehindNAT = "True"
|
||||
Defaults.BehindNAT = true
|
||||
Defaults.DockerRunLogs = "/tmp/"
|
||||
// Generate a random key to be added to IPTable
|
||||
Defaults.IPTableKey = String(12)
|
||||
@@ -95,6 +95,8 @@ func SetDefaults(envName string, forceDefault bool, CustomConfig interface{}, No
|
||||
Defaults.PrivateKeyFile = defaultPath + "p2prc.privateKey"
|
||||
Defaults.BareMetal = false
|
||||
Defaults.UnsafeMode = false
|
||||
Defaults.Test = false
|
||||
Defaults.ConfigPath = defaultPath + "config.json"
|
||||
|
||||
// Generate certificate files for SSL
|
||||
err = GenerateCertificate()
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"github.com/Akilan1999/p2p-rendering-computation/p2p"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
@@ -54,7 +53,8 @@ func GenerateCertificate() error {
|
||||
|
||||
//flag.Parse()
|
||||
|
||||
host, err = p2p.CurrentPublicIP()
|
||||
//host, err = p2p.CurrentPublicIP()
|
||||
host = "0.0.0.0"
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -45,9 +45,9 @@ func GenerateIPTableFile(rootNodes []p2p.IpAddress) (err error) {
|
||||
if len(rootNodes) <= 0 {
|
||||
rootnode.Name = "Node1"
|
||||
rootnode.ServerPort = "8078"
|
||||
rootnode.NAT = "False"
|
||||
rootnode.NAT = false
|
||||
rootnode.Ipv4 = "217.76.63.222"
|
||||
rootnode.ProxyServer = "True"
|
||||
rootnode.ProxyServer = true
|
||||
|
||||
rootnodes.IpAddress = append(rootnodes.IpAddress, rootnode)
|
||||
} else {
|
||||
|
||||
12
flake.lock
generated
12
flake.lock
generated
@@ -28,11 +28,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733668782,
|
||||
"narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=",
|
||||
"lastModified": 1745875161,
|
||||
"narHash": "sha256-0YkWCS13jpoo3+sX/3kcgdxBNt1VZTmvF+FhZb4rFKI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "514283ec89c39ad0079ff2f3b1437404e4cba608",
|
||||
"rev": "2cbd7fdd6eeab65c494cc426e18f4e4d2a5e35c0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -43,11 +43,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1735834308,
|
||||
"narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=",
|
||||
"lastModified": 1748460289,
|
||||
"narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6df24922a1400241dae323af55f30e4318a6ca65",
|
||||
"rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
122
flake.nix
122
flake.nix
@@ -1,30 +1,58 @@
|
||||
{
|
||||
description = "P2PRC nix flake";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
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";
|
||||
inputs = {
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, gomod2nix }:
|
||||
(flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ gomod2nix.overlays.default ];
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
flake-utils = {
|
||||
url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
# pkgs = nixpkgs.legacyPackages.${system};
|
||||
gomod2nix = {
|
||||
url = "github:nix-community/gomod2nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.flake-utils.follows = "flake-utils";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
gomod2nix,
|
||||
...
|
||||
}:
|
||||
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 = callPackage ./. { };
|
||||
packages.default = p2prcDefault;
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
@@ -36,6 +64,66 @@
|
||||
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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
7
nix/overlays/bindings.nix
Normal file
7
nix/overlays/bindings.nix
Normal 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
190
nix/templates/haskell/flake.lock
generated
Normal 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
|
||||
}
|
||||
48
nix/templates/haskell/flake.nix
Normal file
48
nix/templates/haskell/flake.nix
Normal 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
|
||||
'';
|
||||
};
|
||||
}
|
||||
));
|
||||
}
|
||||
@@ -29,9 +29,9 @@ type IpAddress struct {
|
||||
Upload float64 `json:"Upload"`
|
||||
ServerPort string `json:"ServerPort"`
|
||||
BareMetalSSHPort string `json:"BareMetalSSHPort"`
|
||||
NAT string `json:"NAT"`
|
||||
NAT bool `json:"NAT"`
|
||||
EscapeImplementation string `json:"EscapeImplementation"`
|
||||
ProxyServer string `json:"ProxyServer"`
|
||||
ProxyServer bool `json:"ProxyServer"`
|
||||
UnSafeMode bool `json:"UnSafeMode"`
|
||||
PublicKey string `json:"PublicKey"`
|
||||
CustomInformation string `json:"CustomInformation"`
|
||||
@@ -42,6 +42,8 @@ type IP struct {
|
||||
Query string
|
||||
}
|
||||
|
||||
var Test = false
|
||||
|
||||
// ReadIpTable Read data from Ip tables from json file
|
||||
func ReadIpTable() (*IpAddresses, error) {
|
||||
// Get Path from config
|
||||
@@ -161,7 +163,7 @@ func (table *IpAddresses) RemoveDuplicates() error {
|
||||
}
|
||||
}
|
||||
|
||||
if table.IpAddress[i].NAT == "True" && table.IpAddress[i].EscapeImplementation == "None" {
|
||||
if table.IpAddress[i].NAT && table.IpAddress[i].EscapeImplementation == "None" {
|
||||
Exists = true
|
||||
}
|
||||
|
||||
@@ -178,6 +180,16 @@ func (table *IpAddresses) RemoveDuplicates() error {
|
||||
|
||||
// CurrentPublicIP Get Current Public IP address
|
||||
func CurrentPublicIP() (string, error) {
|
||||
// Get configs
|
||||
Config, err := config.ConfigInit(nil, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// If test mode is on then return local address
|
||||
if Config.Test {
|
||||
return "0.0.0.0", nil
|
||||
}
|
||||
|
||||
req, err := http.Get("http://ip-api.com/json/")
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -193,6 +205,7 @@ func CurrentPublicIP() (string, error) {
|
||||
json.Unmarshal(body, &ip)
|
||||
|
||||
return ip.Query, nil
|
||||
|
||||
}
|
||||
|
||||
// GetCurrentIPV6 gets the current IPV6 address based on the interface
|
||||
|
||||
@@ -93,9 +93,10 @@ func (ip *IpAddresses) SpeedTestUpdatedIPTable() error {
|
||||
// and look upon later on.
|
||||
AddAuthorisationKey(ip.IpAddress[k].PublicKey)
|
||||
}
|
||||
targets.IpAddress[i].CustomInformation = ip.IpAddress[k].CustomInformation
|
||||
// Checks if both the IPV4 addresses are the same or the IPV6 address is not
|
||||
// an empty string and IPV6 address are the same
|
||||
if (ip.IpAddress[k].Ipv4 == targets.IpAddress[i].Ipv4 && targets.IpAddress[i].NAT == "True") || (targets.IpAddress[i].Ipv6 != "" && ip.IpAddress[k].Ipv6 == targets.IpAddress[i].Ipv6) {
|
||||
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) {
|
||||
Exists = true
|
||||
break
|
||||
}
|
||||
|
||||
@@ -141,8 +141,6 @@ func Server() (*gin.Engine, error) {
|
||||
// Convert Get Request value to int
|
||||
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[:]))
|
||||
@@ -255,7 +253,7 @@ func Server() (*gin.Engine, error) {
|
||||
// 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 == "True" {
|
||||
if config.BehindNAT {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -267,7 +265,7 @@ func Server() (*gin.Engine, error) {
|
||||
for i, _ := range table.IpAddress {
|
||||
// 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].NAT != "" {
|
||||
if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && !table.IpAddress[i].NAT {
|
||||
lowestLatency = table.IpAddress[i].Latency.Milliseconds()
|
||||
lowestLatencyIpAddress = table.IpAddress[i]
|
||||
}
|
||||
@@ -291,8 +289,8 @@ func Server() (*gin.Engine, error) {
|
||||
ProxyIpAddr.Ipv4 = lowestLatencyIpAddress.Ipv4
|
||||
ProxyIpAddr.ServerPort = proxyPort
|
||||
ProxyIpAddr.Name = config.MachineName
|
||||
ProxyIpAddr.NAT = "False"
|
||||
ProxyIpAddr.ProxyServer = "False"
|
||||
ProxyIpAddr.NAT = false
|
||||
ProxyIpAddr.ProxyServer = false
|
||||
ProxyIpAddr.EscapeImplementation = "FRP"
|
||||
|
||||
if config.BareMetal {
|
||||
@@ -312,11 +310,12 @@ func Server() (*gin.Engine, error) {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
ProxyIpAddr.ServerPort = config.ServerPort
|
||||
ProxyIpAddr.Name = config.MachineName
|
||||
ProxyIpAddr.NAT = "False"
|
||||
ProxyIpAddr.NAT = false
|
||||
if config.ProxyPort != "" {
|
||||
ProxyIpAddr.ProxyServer = "True"
|
||||
ProxyIpAddr.ProxyServer = true
|
||||
}
|
||||
ProxyIpAddr.EscapeImplementation = ""
|
||||
if config.BareMetal {
|
||||
@@ -425,9 +424,9 @@ func MapPort(port string, domainName string) (string, string, error) {
|
||||
for i, _ := range table.IpAddress {
|
||||
// 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].NAT != "" {
|
||||
if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && !table.IpAddress[i].NAT {
|
||||
// Filter based on nodes with proxy enabled
|
||||
if domainName != "" && table.IpAddress[i].ProxyServer == "True" {
|
||||
if domainName != "" && table.IpAddress[i].ProxyServer {
|
||||
lowestLatency = table.IpAddress[i].Latency.Milliseconds()
|
||||
lowestLatencyIpAddress = table.IpAddress[i]
|
||||
continue
|
||||
@@ -466,7 +465,7 @@ func MapPort(port string, domainName string) (string, string, error) {
|
||||
ProxyIpAddr.Ipv4 = lowestLatencyIpAddress.Ipv4
|
||||
ProxyIpAddr.ServerPort = proxyPort
|
||||
ProxyIpAddr.Name = config.MachineName
|
||||
ProxyIpAddr.NAT = "False"
|
||||
ProxyIpAddr.NAT = false
|
||||
ProxyIpAddr.EscapeImplementation = "FRP"
|
||||
|
||||
//ProxyIpAddr.CustomInformationKey = p2p.GenerateHashSHA256(config.IPTableKey)
|
||||
|
||||
32
style.css
32
style.css
@@ -1,32 +0,0 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
span#discordJoin {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* Dark mode */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* General Page Changing */
|
||||
body {
|
||||
background: #121212; /* Background Color */
|
||||
color: #FFFFFF; /* Text Color */
|
||||
}
|
||||
|
||||
a:link{ /* Unvisited Link */
|
||||
color: #03DAC5;
|
||||
}
|
||||
|
||||
a:visited { /* Visited Link */
|
||||
color: #BB86FC;
|
||||
}
|
||||
|
||||
a:hover { /* Mouse over Link */
|
||||
color: #018786;
|
||||
}
|
||||
|
||||
span#discordJoin { /* Text Color Change */
|
||||
color: #cc0227;
|
||||
}
|
||||
}
|
||||
BIN
wasm/p2prc.wasm
Executable file
BIN
wasm/p2prc.wasm
Executable file
Binary file not shown.
Reference in New Issue
Block a user