public Node(NetworkConfig Configuration) { // Assign configuration Globals = Configuration; // Setup logger Logger = new Logger() { LogFile = Globals.LOG_FILE, LogLevel = Globals.LOG_LEVEL, CustomPrefix = Globals.CUSTOM_PREFIX, InfoColor = Globals.INFO_COLOR, ImportantColor = Globals.IMPORTANT_COLOR, DebugColor = Globals.DEBUG_COLOR, WarningColor = Globals.WARNING_COLOR, ErrorColor = Globals.ERROR_COLOR }; // Show ascii art, strictly vanity if (!string.IsNullOrEmpty(Globals.ASCII_ART)) { Logger?.Important(Globals.ASCII_ART); } Logger.ShowPrefix = true; // Setup our blockchain handler Logger?.WriteLine("Setting up blockchain handler..."); Blockchain = new BlockchainStorage(); // Create our P2P server Logger?.WriteLine("Setting up P2P server..."); P2pServer = new P2pServer(Globals.P2P_MAX_PEER_CONNECTIONS); // Assign P2P event handlers Logger?.WriteLine("Assigning P2P callbacks..."); AssignCallbacks(); // Setup our API server Logger?.WriteLine("Setting up API server..."); ApiServer = new ApiServer(Globals.API_MAX_WORKERS) { Logger = Logger }; // Create an API context Logger?.WriteLine("Assigning API method context..."); ApiServer.AssignMethodContext(new ApiMethods(this)); // Setup our peer list Logger?.WriteLine("Setting up local peer list..."); SetupPeerList(); // Assign a unique identifier Logger?.WriteLine("Generating identifier..."); Id = SecureRandom.Integer <ulong>(); }
/// <summary> /// Initializes this node with the specified network configuration /// </summary> /// <param name="Configuration">A class containing all global information this node needs to operate</param> public Node(NodeConfig Configuration) { // Assign configuration Globals = Configuration; // Generate identifier Id = SecureRandom.Integer <ulong>(); // Setup and start logger Logger = new Logger() { LogFile = Globals.LOG_FILE, LogLevel = Globals.LOG_LEVEL, CustomPrefix = Globals.CUSTOM_PREFIX, ImportantColor = Globals.IMPORTANT_COLOR, InfoColor = Globals.INFO_COLOR, ErrorColor = Globals.ERROR_COLOR, WarningColor = Globals.WARNING_COLOR, DebugColor = Globals.DEBUG_COLOR }; // Setup blockchain cache Blockchain = new BlockchainCache() { Logger = Logger }; // Create our P2P server P2pServer = new P2pServer(Globals.P2P_WORKERS, Globals.P2P_MAX_PEER_CONNECTIONS) { ConnectionTimeout = Globals.P2P_CONNECTION_TIMEOUT }; // Setup our API server ApiServer = new ApiServer(Globals.API_WORKERS, Globals.API_PASSWORD) { Logger = Logger }; // Setup done, set node to stopped Stopped = true; }
// Start server on specified port public void Start(int Port = GlobalsConfig.P2P_DEFAULT_PORT) { // Create a new TCP listener and start listening try { // Set internals this.Port = Port; PeerId = SecureRandom.Integer<ulong>(); // Start listener Listener = new TcpListener(IPAddress.Parse("127.0.0.1"), Port); Listener.Start(); Running = true; // Raise server start event OnStart?.Invoke(this, EventArgs.Empty); } catch { // Failed to start the server for some reason or another Logger.Log(Level.FATAL, "Failed to start P2P server on port {0}, port may be in use", Port); OnError?.Invoke("Failed to start P2P server", EventArgs.Empty); } // Create a levin protocol context Context = new LevinProtocol(this); // Start request handling thread IncomingRequestThread = new Thread(ProcessIncomingRequests); IncomingRequestThread.Start(); // Start request handling thread OutgoingRequestThread = new Thread(ProcessOutgoingRequests); OutgoingRequestThread.Start(); // Start connection handling thread PeerConnectionThread = new Thread(PeerConnection); PeerConnectionThread.Start(); }
// Attempts to connect to a new peer on the peer candidate list private void DiscoverNewPeer() { // Do nothing if not running if (Stopped) { return; } // Check if discovery is enabled if (!Globals.P2P_DISCOVERY_ENABLED) { return; } // Check if discovery is already active if (DiscoveryActive) { return; } DiscoveryActive = true; // Check if the peer list has space for a new peer if (GetPeerCount() >= Globals.P2P_MAX_PEER_CONNECTIONS) { return; } // Get the peer candidate list List <PeerCandidate> Candidates = GetPeerCandidates(); // Check if there are candidates in the list if (Candidates.Count == 0) { return; } // Setup variables int TryCount = 0; int RandomCount = 0; int MaxIndex = Math.Min(Candidates.Count - 1, 50); List <int> TriedIndexes = new List <int>(); // Loop to try a selection of candidates while (RandomCount < (MaxIndex + 1) * 3 && TryCount < 10 && !Stopped) { // Increment random count and get net random index RandomCount++; int RandomIndex = SecureRandom.Integer(0, MaxIndex + 1); RandomIndex = (RandomIndex * RandomIndex * RandomIndex) / (MaxIndex * MaxIndex); // Check if this index has been tried previously if (TriedIndexes.Contains(RandomIndex)) { continue; } // Add this index to the tried indexes list TriedIndexes.Add(RandomIndex); // Get the peer candidate PeerCandidate Peer = Candidates[RandomIndex]; // Increment try count TryCount++; // Check if this peer is already connected if (IsPeerConnected(Peer)) { continue; } // TODO - check if we are allowing remote connections?? // Check if this peer has previously been blacklists if (IsPeerAllowed(Peer)) { return; } // Attempt to add this peer candidate Logger.Debug($"[{TryCount}/10] Trying candidate #{RandomIndex} {Peer.Address}:{Peer.Port}, last seen {GetTimeDelta(Peer.LastSeen)} seconds ago"); if (!AddPeer(Peer.Address, (int)Peer.Port)) { // Add to failed connection list AddRecentlyTriedPeer(Peer); } } // Discovery process finished DiscoveryActive = false; }
/// <summary> /// Initializes this node with the specified network configuration /// </summary> /// <param name="Configuration">A class containing all global information this node needs to operate</param> public Node(NetworkConfig Configuration) { // Assign configuration Globals = Configuration; // Setup logger Logger = new Logger() { LogFile = Globals.LOG_FILE, LogLevel = Globals.LOG_LEVEL, CustomPrefix = Globals.CUSTOM_PREFIX, InfoColor = Globals.INFO_COLOR, ImportantColor = Globals.IMPORTANT_COLOR, DebugColor = Globals.DEBUG_COLOR, WarningColor = Globals.WARNING_COLOR, ErrorColor = Globals.ERROR_COLOR }; // Show ascii art, strictly vanity if (!string.IsNullOrEmpty(Globals.ASCII_ART)) { Logger?.Important(Globals.ASCII_ART); } Logger.ShowPrefix = true; // Setup our database Logger?.WriteLine("Setting up local storage..."); if (!Directory.Exists(Globals.DATABASE_DIRECTORY)) { Logger?.WriteLine("Creating directories..."); Directory.CreateDirectory(Globals.DATABASE_DIRECTORY); } switch (Globals.DATABASE_TYPE) { case DatabaseType.SQLITE: DatabaseLocation = CombinePath(Globals.DATABASE_DIRECTORY, Globals.DATABASE_LOCATION); Database = new Sqlite(DatabaseLocation); break; default: throw new ArgumentException("Invalid or non-specified database type"); } // Setup our blockchain handler Logger?.WriteLine("Setting up blockchain handler..."); Blockchain = new Blockchain() { Logger = Logger }; // Create our P2P server Logger?.WriteLine("Setting up P2P server..."); P2pServer = new P2pServer(Globals.P2P_MAX_PEER_CONNECTIONS); // Assign P2P event handlers Logger?.WriteLine("Assigning P2P callbacks..."); AssignCallbacks(); // Setup our API server Logger?.WriteLine("Setting up API server..."); ApiServer = new ApiServer(Globals.API_MAX_WORKERS) { Logger = Logger }; // Create an API context Logger?.WriteLine("Assigning API method context..."); ApiServer.AssignMethodContext(new ApiMethods(this)); // Setup our peer list Logger?.WriteLine("Setting up local peer list..."); SetupPeerList(); // Assign a unique identifier Logger?.WriteLine("Generating identifier..."); Id = SecureRandom.Integer <ulong>(); }