added base simulation

This commit is contained in:
2025-04-29 23:28:33 +01:00
parent d79c28c32f
commit 9f4cb9af02
7 changed files with 313 additions and 0 deletions

Binary file not shown.

143
Simulation/library.py Normal file
View File

@@ -0,0 +1,143 @@
import ctypes
from ctypes import Structure, c_char_p, c_int, cdll
from dataclasses import dataclass, astuple
import time
import json
from urllib.parse import urlparse
from typing import List
import subprocess
import uuid
import dacite
import os
import sqlalchemy
import dataclasses
from typing import Union
import schedule
import threading
import requests
import subprocess
# p2prc.Init("")
# Start running schedule
schedule.run_pending()
# # Create Sqlite database to track processes
# engine=sqlalchemy.create_engine(f'sqlite:///homeserver.db')
# Global variable
# A global variable will be populated on runtime.
# It is read from P2PRC directly or from a local
# database.
# Node information
# Generated using: https://jsonformatter.org/json-to-python
@dataclass
class Node:
Name: str
MachineUsername: str
IPV4: str
IPV6: str
Latency: int
Download: int
Upload: int
ServerPort: str
BareMetalSSHPort: str
NAT: bool
EscapeImplementation: str
ProxyServer: bool
UnSafeMode: bool
PublicKey: str
CustomInformation: str
@dataclass
class IPAddress:
ip_address: List[Node]
# ----------------------------------------------------------------------------
# ----------------------------- Helper functions -----------------------------
# ----------------------------------------------------------------------------
p2prc = None
def LoadDLL(envPath=""):
global p2prc
# os.environ["P2PRC"] = envPath
print(envPath)
# env = "P2PRC=" + envPath
os.system("sh setEnv.sh " + envPath)
# subprocess.call("sh setEnv.sh " + envPath)
p2prc = ctypes.CDLL("SharedObjects/p2prc.so")
def StartServer():
# Starting P2PRC as a server mode
p2prc.Server()
def Init():
p2prc.Init("")
# Class to create string to pass as string function
# parameter to shared object file
class go_string(Structure):
_fields_ = [
("p", c_char_p),
("n", c_int)]
# Ensuring local port can escape NAT and responds
# with the public ip and port no.
# If the domain name is specified then the public IP
# can be used as an A name entry.
def P2PRCMapPort(port="",domainname="",serveraddress=""):
port = go_string(c_char_p(port.encode('utf-8')), len(port))
domainname = go_string(c_char_p(domainname.encode('utf-8')), len(domainname))
serveraddress = go_string(c_char_p(serveraddress.encode('utf-8')), len(serveraddress))
# Defining the response type of the GoLang function
# function
p2prc.MapPort.restype = c_char_p
# Calling the Go function
address = p2prc.MapPort(port,domainname,serveraddress)
res = str(address).strip("b'")
return res
# Lists all avaliable nodes in the network
def ListNodes():
# View IP Table information
p2prc.ViewIPTable.restype = c_char_p
ipTable = p2prc.ViewIPTable()
# View IP Table as
ipTableObject = json.loads((str(ipTable).strip("b'")))
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

6
Simulation/requests Normal file
View File

@@ -0,0 +1,6 @@
dacite
schedule
sqlalchemy
dacite
schedule
sqlalchemy

View File

@@ -0,0 +1,4 @@
dacite
schedule
sqlalchemy
requests

1
Simulation/setEnv.sh Normal file
View File

@@ -0,0 +1 @@
export P2PRC=$1

145
Simulation/simulation.py Normal file
View File

@@ -0,0 +1,145 @@
# This repo consists of the P2PRC simulation to ensure
# that we can test various nodes locally with just processes.
# Note: This implementation is for local nodes only.
from library import *
import os
import socket
from contextlib import closing
# Stores the base information
# required to know about a P2PRC node
@dataclass
class P2PRCNode:
Name: str
ConfigLocation: str
ServerPort: str
RunningState: bool
RootNodePort: str
@dataclass
class P2PRCNodes:
P2PRCNodes: List[P2PRCNode]
# Tracker for P2PRC nodes
PublicP2PRCNodes: Union[P2PRCNodes, None] = None
# Tracker of root Node
RootNode: P2PRCNode
# Sets up P2PRC with default configuration
def SetupP2PRCNode(name=""):
# Unset environment variables
os.environ.pop('P2PRC', None)
# Create Test folder
Dirname = "P2PRC-Test-" + name
try:
os.mkdir(Dirname)
print(f"Directory '{Dirname}' created successfully.")
except FileExistsError:
print(f"Directory '{Dirname}' already exists.")
KillProcedure()
# Set env variable for session
envPath = os.path.abspath(os.getcwd()) + "/" + Dirname
LoadDLL(envPath)
# Generate random 4
ServerPortNo = str(find_free_port())
# Initialise data class
p2prcNode = P2PRCNode(
Name = name,
ConfigLocation = envPath,
ServerPort = ServerPortNo,
RunningState = True,
RootNodePort = "",
)
# Adds P2PRC node to memory
AddP2PRCNodeToMemory(p2prcNode)
# Initialise P2PRC instance
Init()
# Add root node to ip table
AddRootNode("0.0.0.0",ServerPortNo)
# Change server port no on Config
ChangeFileValue(envPath + "/config.json", "ServerPort", ServerPortNo)
thread = threading.Thread(target = StartServer)
thread.start()
# Find free TCP port avaliable
# Source: https://stackoverflow.com/questions/1365265/on-localhost-how-do-i-pick-a-free-port-number
def find_free_port():
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.bind(('', 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return s.getsockname()[1]
# Standard function called after
# a simulation is terminated or
# when error exception is called
def KillProcedure():
RemoveAllP2PRCNodesFromMemoryAndConfigFile()
# -------------------------- helper functions -----------------------
def ChangeFileValue(file_path, key, new_value):
"""
Reads a JSON file, updates a key with a new value, and writes it back.
:param file_path: Path to the JSON file
:param key: Key in the JSON to update
:param new_value: New value to assign to the key
"""
# Read from the JSON file
with open(file_path, 'r') as file:
data = json.load(file)
# Update the key with the new value
data[key] = new_value
# Write the updated data back to the JSON file
with open(file_path, 'w') as file:
json.dump(data, file, indent=4)
# Adds the process dataclass the to
# the global variable PublicProcess.
def AddP2PRCNodeToMemory(p2prcNode: P2PRCNode):
global PublicP2PRCNodes
global RootNode
if PublicP2PRCNodes == None:
PublicP2PRCNodes = P2PRCNodes(P2PRCNodes=[p2prcNode])
p2prcNode.RootNodePort = p2prcNode.ServerPort
RootNode = p2prcNode
else:
p2prcNode.RootNodePort = RootNode.ServerPort
PublicP2PRCNodes.P2PRCNodes.append(p2prcNode)
# Remove all nodes test nodes generated
def RemoveAllP2PRCNodesFromMemoryAndConfigFile():
global PublicP2PRCNodes
if PublicP2PRCNodes is not None:
for node in PublicP2PRCNodes.P2PRCNodes:
config_path = node.ConfigLocation
# Safely remove the folder if it exists
if os.path.exists(config_path) and os.path.isdir(config_path):
try:
shutil.rmtree(config_path)
print(f"Deleted folder: {config_path}")
except Exception as e:
print(f"Failed to delete {config_path}: {e}")
# Clear the node list
PublicP2PRCNodes = None
print("All P2PRC nodes removed from memory.")
else:
print("No P2PRC nodes to remove.")
if __name__ == "__main__":
# Setting up 2 P2PRC nodes
SetupP2PRCNode(name="Test")
SetupP2PRCNode(name="Test1")

14
Simulation/test.py Normal file
View File

@@ -0,0 +1,14 @@
from library import *
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")