Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add socket connection between nodes #99

Open
wants to merge 15 commits into
base: dev
Choose a base branch
from
Open

feat: add socket connection between nodes #99

wants to merge 15 commits into from

Conversation

yoxira
Copy link
Member

@yoxira yoxira commented Feb 3, 2025

https://trello.com/c/5Jt28f7V/63-feat-socket-support-node-node

Description

  • The node chooses peers to connect to in the same way as for HTTP broadcasting—randomly, to avoid centralization.
  • Every 30 minutes, the node attempts to replace 20% of its WebSocket connections.
  • Received transactions affect the peer's health check.
  • Nodes authenticate own ip by providing nonce.

Config

Added new required config properties:

{
  "peers": {
    // ....
    "options": {
      "maxWsConnections": 15 // maximum number of peers to receive transactions from
      // ...
    }
  },
  "wsNode": {
    "enabled": true, // enable broadcasting to other nodes using ws
    "maxConnections": 25 // maximum number of peers to broadcast transactions to
  }
}

API

The GET /api/peers now returns info about connection type for incoming (syncing) and outcoming (broadcasting) transactions:

{
  "peers": [
    {
      "isBroadcastingViaSocket": false,
      "syncProtocol": "ws" // or 'http'
    },
  ]
}

@yoxira yoxira added the feature label Feb 3, 2025
@yoxira yoxira requested review from bludnic and adamant-al February 5, 2025 09:43
@yoxira yoxira marked this pull request as ready for review February 5, 2025 09:59
@@ -77,6 +77,7 @@
"blackList": []
},
"options": {
"maxWsConnections": 15,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't better to move it to wsNode config and use more descriptive naming, like:

"wsNode": {
  "enabled": true,
  "maxIncomingConnections": 15, // maximum number of peers to receive transactions from
  "maxOutgoingConnections": 25, // maximum number of peers to broadcast transactions to
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really confusing to have options.maxWsConnections and wsNode.maxConnections.

const maxReconnectDelay = 60000;
const defaultReconnectionDelay = 5000;

function TransportWsApi(modules, library, options) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a JSDoc to describe the purpose of TransportWsApi?

Copy link
Member

@adamant-al adamant-al Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also JSDoc for prototype's methods below.

@@ -606,6 +604,21 @@ d.run(function () {
});
}],

/**
* Listens for new transacttions using websocket and links peers to the websocket server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: transacttions -> transactions

Peers.prototype.getByNonce = function (nonce) {
for (const [peerString, peer] of Object.entries(__private.peers)) {
if (peer.nonce === nonce) {
return __private.peers[peerString];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return __private.peers[peerString];
return peer;

Same behaviour?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, you can use Object.values instead of Object.entries

@adamant-al
Copy link
Member

Please elaborate:

Received transactions affect the peer's health check.

@adamant-al
Copy link
Member

Please elaborate:

Nodes authenticate own ip by providing nonce.

Does REST node exchange uses the same authentication? What's it for?
Does the node get its nonce once and forever, updates it on restart, updates it on some event?
Is the node's nonce stays the same across all nodes?

@adamant-al
Copy link
Member

adamant-al commented Feb 20, 2025

(Updated, see the comment below #99 (comment))

The GET /api/peers now returns info about connection type for incoming (syncing) and outcoming (broadcasting) transactions:

{
  "peers": [
    {
      "isBroadcastingViaSocket": false,
      "syncProtocol": "ws" // or 'http'
    },
  ]
}

If syncProtocol is "ws", it means the node accepts new txs both via ws and http? Maybe then something like "syncProtocol": "wsAndHttp" // or 'http'?

@adamant-al
Copy link
Member

What is the limit [of number of peers for broadcasting] for REST connections?
It's hard coded in constants.js file:
maxPeers: 100

Why then we have peers.options.limits.max in the default config?

Can you please explain what are peers.options, broadcasts and transactions params from config?

@adamant-al
Copy link
Member

The GET /api/peers now returns info about connection type for incoming (syncing) and outcoming (broadcasting) transactions:

{
  "peers": [
    {
      "isBroadcastingViaSocket": false,
      "syncProtocol": "ws" // or 'http'
    },
  ]
}

If syncProtocol is "ws", it means the node accepts new txs both via ws and http? Maybe then something like "syncProtocol": "wsAndHttp" // or 'http'?

I see that current implementation assumes that a node is connected either via ws, or http.

One of reasons while adding wsNode is redundancy. It means some nodes may connect with ws AND http. Imaging that a network has low amount of nodes, and all of them ws-enabled.

Can we improve that some nodes will connect both ws and http?

const maxReconnectDelay = 60000;
const defaultReconnectionDelay = 5000;

function TransportWsApi(modules, library, options) {
Copy link
Member

@adamant-al adamant-al Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also JSDoc for prototype's methods below.

const { io } = require('socket.io-client');
const Peer = require('../../logic/peer.js');

const maxReconnectDelay = 60000;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider moving it to config


// Connect to multiple peers
self.getRandomPeers(self.maxConnections, (err, peers) => {
if (err || !peers.length) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preferred to log this event / debug

// Find a new peer to replace it
self.getRandomPeer((err, newPeer) => {
if (err || !newPeer) {
self.logger.debug('WebSocket: Failed to find replacement peer');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'WebSocket: Failed to find replacement' ⟶ Add 'for peer ws://${peer.ip}:${peer.port} + err || no peers'?

};

TransportWsApi.prototype.updatePeers = function() {
const self = this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preferred to log this event / debug

};

TransportWsApi.prototype.rotatePeers = function () {
const self = this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preferred to log this event / debug

return;
}

self.logger.debug(`Rotating ${newPeers.length} out of ${totalConnections} peers.`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to clarify logging everywhere that it's about ws.

E.g., [wsNode] Rotating ${newPeers.length} out of ${totalConnections} peers.

TransportWsApi.prototype.startRotation = function() {
this.rotationInterval = setInterval(() => {
this.rotatePeers();
}, 1000 * 60 * 30); // 30 minutes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make const

* Changes the connection type to ws
* @param {Peer} peer
*/
Peers.prototype.switchToWs = function (peer) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment about redundancy
#99 (comment)

@yoxira
Copy link
Member Author

yoxira commented Feb 24, 2025

Does REST node exchange uses the same authentication? What's it for?

The authentication is needed to link the incoming WebSocket connection to the existing peer. REST provides port instead.

Does the node get its nonce once and forever, updates it on restart, updates it on some event?

A nonce is generated on app start and never changes.

Is the node's nonce stays the same across all nodes?

Yes

Please elaborate:

Received transactions affect the peer's health check.

If a node receives an invalid transaction over WebSocket, the peer will be removed from the peer list, as it would be using REST.

@yoxira
Copy link
Member Author

yoxira commented Feb 24, 2025

I see that current implementation assumes that a node is connected either via ws, or http.

A node can be connected using both ws and http at the same time. It's up to a broadcasting node to decide whether to double-send the transactions or not. In the current implementation, a node doesn't send the transactions twice at all, while the old nodes always do.

I suggest still preferring nodes with http-only connections when broadcasting, but also choosing some % of ws-connected nodes to broadcast to using http.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants