Commit Graph

167 Commits

Author SHA1 Message Date
cloudhead f6aa46a283
cli: Try to connect to seeds specified as options
When fetching with a `--seed` specified on the CLI, try to connect to it
if not already connected.
2024-11-05 12:18:12 +01:00
cloudhead c6a8a00055
Update `rust-toolchain` to 1.80
Fix new clippy warnings.
2024-08-06 14:37:21 +02:00
cloudhead aac23dde0c
node: Add a max capacity to fetch queues 2024-07-16 19:58:44 +02:00
cloudhead 3c1c35f259
node: Have multiple fetch queues
In the current design it's possible for one peer to fill the fetch queue
so that fetches from other peers are delayed.

To improve fairness, we move to a queue per peer. We then try to dequeue
from all peers in a random order for good measure.
2024-07-16 17:11:46 +02:00
cloudhead bbb292c8e6
node: Don't panic on connection logic error
It seems like the underlying network library has a bug, and transport
handovers don't always happen.
2024-06-26 11:31:07 +02:00
cloudhead 47c9f792e6
node: Handle connection state discrepancies
There are cases where the service state doesn't match the state
of the underlying wire protocol. We remedy this by transitioning peers
to a "connected" state when a message is received, if they are in a
"connecting" state.

The long term solution to this will likely be to merge the service and
protocol layer so that there are no inconsistencies.

The other case happens when a persistent peer is in "disconnected" state
and attempts an inbound connection to us.
2024-06-18 15:55:28 +02:00
cloudhead 484cf0220c
node: Don't panic if we can't decode announcements
Bubble up the error instead.

Also, as part of the migration, delete existing announcements that won't
decode properly.
2024-06-13 21:38:30 +02:00
cloudhead 76edc0c523
node: Add agent and version to node announcement
1. Adds a user-agent string to the node announcement. This lets us keep
   track of what software version everyone is running. Especially useful
   to see what percentage of the network has upgraded to a new release.

  For old nodes, this defaults to the string `/radicle/`.

  The user agent format is roughly based on BIP 0014.

2. Advertize protocol version in node announcement.

  By advertizing the protocol version, nodes can decide to only connect to
  peers with a compatible version. The default and current version is `1`.

  We bundle these two changes as they both modify the node announcement
  and node database.

3. Swap the order of the fields in announcements to make the message
   extensible.
2024-06-13 11:28:09 +02:00
cloudhead 0834e0fc7d
node: Implement staggered broadcast for gossip
We use the *staggered broadcast* technique when relaying gossip messages
to reduce message amplification. In our basic simulation, it brings
amplification down from 3.5 to 1.5.

The idea is simply to accumulate gossip messages and relay them after a fixed
interval of time. This has two effects:

1) Old messages that haven't been sent yet are replaced by newer ones,
   so only one message is sent after the delay.
2) Because nodes have their own intervals, messages are not all sent at
   the same time, which reduces the chance of messages crossing paths.

For now we only turn this on for inventory messages, since this is the
least likely to be noticed by users, and also the most problematic
currently.
2024-05-24 16:44:22 +02:00
cloudhead 30c9b0db0b
cli: Add `rad node debug` command 2024-05-21 16:00:30 +02:00
cloudhead 9a82aae993
node: Reset connection attempts when stable
Instead of resetting the number of attempts on connection, reset it when
the connection is stable, ie. after some time has passed.

This prevents connection retries from happening in succession when a
peer is disconnected during the initial handshake, as is the case with
invalid subscribe timestamps.
2024-05-07 14:49:02 +02:00
cloudhead 7484d737d5
node: Re-use inventory message timestamps
If our inventory hasn't updated, don't issue a new inventory
announcement, simply use the previous message.

This avoids identical inventories propagating over the network and using
up resources.
2024-05-07 14:49:02 +02:00
cloudhead afe13b9e47
node: Don't rate-limit persistent peers 2024-05-01 11:06:28 +02:00
cloudhead eece90e9f7
node: Fix naming/spelling of "limitter" module 2024-04-30 14:24:09 +02:00
cloudhead a815640a3b
node: Always try to relay node announcements
They are cached and will often be old. They should still be relayed.
2024-04-29 12:28:30 +02:00
cloudhead 70d2e1a0db
node: Improve `Timestamp` conversion safety
Remove `Timestamp::from(u64)` instance, since not all `u64`s are valid
Timestamps, and add `try_from` instead.
2024-04-26 10:33:34 +02:00
cloudhead 2b771921d5
node: Type-safe timestamps
Use a struct to represent timestamps, to improve type safety.
2024-04-24 12:19:40 +02:00
cloudhead cdebbe5bb7
node: Improved message logging 2024-04-23 10:41:43 +02:00
cloudhead e55c86f1ce
node: Improve fetch logging 2024-04-04 18:41:53 +02:00
cloudhead 6ff4efebed
node: Remove namespaces from fetch request
It was unused..
2024-04-04 18:22:19 +02:00
cloudhead f2fe0242e1
node: Always check refs status before fetching
We were only checking it on dequeue.

Also simplifies some of the logic by centralizing the check for fetching
our own refs.
2024-04-04 18:10:49 +02:00
cloudhead 256c620d31
node: Use refs cache to decide on fetching
When deciding whether or not to fetch a repo based on a refs
announcement, we need to know whether our signed refs are stale or not,
compared to the ones in the announcement. This can be quite expensive as
it requires multiple reads from Git's ODB, which often results in
packfile loading.

To remedy this, we introduce a refs cache in our node database that is
consulted when an announcement is received.

We also remove the logic that generates `info` messages, since they are
rarely needed and introduced more complexity.

Additionally, we revert commit 3ad2b4431f,
which introduced logic to minimze wants and haves with additional logic,
since it potentially also increases the load on the ODB.
2024-03-29 16:00:33 +01:00
cloudhead ada492f699
node: Improve logging more 2024-03-22 18:00:48 +01:00
cloudhead 2bcb03b021
node: Re-think inventory update code
Instead of re-loading the full inventory on `rad init`, we simply add
the new repository to the in-memory cache.

We also keep track of whether a fetch was a full clone, to know when
to update our routing table.
2024-03-21 12:40:58 +01:00
Fintan Halpenny 3ad2b4431f
fetch: only send changed wants and haves
This change limits the amount of `wants` and `haves` data that is sent
to the serving side of a fetch.

When a `RefsAt` announcement is sent, the fetching peer can calculate
the difference between the `rad/sigrefs` they're aware of -- if it
exists -- and the newly advertised `rad/sigrefs`. This means they can
efficiently ask for the `wants` and `haves` of references that have
changed -- saving some data sent over to the serving side.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-03-12 13:01:03 +01:00
Fintan Halpenny 4b268adf49
node: downgrade log statements
Announcement are logged at the debug level and are very frequent. This
causes a lot of noisy logging in a node.

Downgrade the log levels for these messages from debug to trace.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-03-11 11:28:38 +01:00
cloudhead 091f7b7e98
node: Don't limit LAN addresses
These are defacto trusted.
2024-02-26 13:42:06 +01:00
cloudhead 286c639e26
node: Make sure node is "active" when connected
This ensures `last_active` is never `0` for connected peers.
2024-02-12 11:37:31 +01:00
cloudhead d6d90c6fdc
node: Track peer latency
This is useful for a bunch of different network optimizations we're
going to be working on.
2024-02-12 11:36:35 +01:00
cloudhead 15d1709831
node: Ensure dequeued fetches are correct
Dequeued fetches were always fetching all refs, instead of fetching the
specific refs that were initially requested. We fix that by storing the
refs to fetch in the fetch queue.
2024-01-31 13:40:13 +01:00
cloudhead f87dfba9cd
Rename `Id` to `RepoId`
Rename the type to make it clearer and consistent with `NodeId`.
2024-01-22 15:51:44 +01:00
cloudhead ebdaf0edd9
General dependency update
Left out some of the `gix-*` dependencies to be done separately after
review.
2024-01-18 13:30:26 +01:00
cloudhead 6f7c2dca91
node: Introduce connection penalty system
Previously, we might ban a node if we're having trouble connecting to
it, but this could be due to internet connectivity issues.

We introduce a penalty score for nodes, that increases if there are
connection issues or the node misbehaves, and decreases on successful
connections.

When connected to new nodes, we sort them so that low-penalty nodes are
attempted first.
2023-12-15 16:00:09 +01:00
cloudhead 6b04eff34c
radicle: Consolidate databases
Instead of having a routing database, and an addresses database,
consolidate that in a single database called `node.db`.

We also simplify database access, by providing a single type for opening
the database, and use traits to access the various functions.

We create a new `seed::Store` that has seed-related methods split out of
the `address::Store`.

Note that tracking is still in its own database currently, as it isn't
ephemeral state. This makes it easy to delete the new `node.db` without
risk of losing any important information.

To avoid renaming all database methods, we create a `Stores` wrapper
that allows us to access routing, addresses, seeds etc. without type
ambiguity.

This is a breaking change and requires `addresses.db` and `routing.db`
to be deleted.
2023-11-30 13:00:59 +01:00
cloudhead 358324883c
node: Announce offline ref updates
Currently, if refs are updated while the node is stopped, these refs are
not immediately announced when the node starts, due to the node being
unaware of the updates.

To remedy this, on startup, we compare the refs we have in storage with
the refs we announced to the network. Since only our own refs could have
changed while the node was stopped, this change only applies to owned
refs. If we detect that storage has changed while the node was stopped,
we create ref announcements for the updated repos and store them in our
gossip database. These announcements will be included with other
historical gossip announcements when nodes connect to us.

To keep track of what was announced, we store our local node's sync
status in the database. This is also useful when using the `rad sync status`
command, as it tells us what oid our local node is at, and whether it
is aware of our local changes yet.

In addition to this, we make a small fix: previously, inventory and ref
announcements originating from our local node were not stored in the
announcement database. We ensure that they are via the new `announce`
method.
2023-11-20 17:54:36 +01:00
cloudhead d4ec2176bb
Update Rust toolchain to 1.74 2023-11-17 16:58:24 +01:00
Fintan Halpenny 5cb4ad4622
node: `info` announcements
An informational message for the protocol is useful for sending
messages to a node that is not required to be relayed.

This message is introduced by starting with an ACK message for a set
of references being synced. This fixes an issue for announcing to a
node that is already synced, since it can now ACK that it received the
message and replies that it is synced.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-17 16:29:29 +01:00
Fintan Halpenny 85d452b222
radicle: return RefsAt for announce_refs
It can be useful to return the `RefsAt` that was announced for this
node, for example, to compare it to node events.

Change the `Handle` method to return `RefsAt` so that when it is
called it returns the local node's `RefsAt`.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-17 16:29:02 +01:00
cloudhead 06946a2bf4
node: New fetch queueing system
The previous system allowed the same RID to be fetched from multiple
nodes concurrently, which caused issues, especially when cloning.

Worker code assumes that there is only one fetch per repository going on
at any given time. This change ensures that.

We keep a global fetch queue, instead of having a queue per session. We
also enqueue fetches if a session is at capacity. Finally, we allow
multiple fetch requests to receive results for the same fetch.
2023-11-17 14:56:42 +01:00
cloudhead ea4104dbd7
node: Only process messages from known nodes
Previously, we would process any valid inventory or refs announcement.
This could cause problems for keeping sync statuses, as we could get
a refs announcement from a node that isn't in our database.

In practice, there is no good reason why we should receive a refs or
inventory announcement before the announcer's node announcement, unless
nodes are not following the protocol.

Implementing this change meant that many of the tests needed updating,
as it wasn't assumed that a node announcement was needed before
receiving inventories etc. from a peer.

We also had to implement a basic form of node address banning to make
the tests pass after this change.
2023-11-13 16:15:57 +01:00
Fintan Halpenny dd16356e4e
node: fetch refs announcements
Ref announcements contain the namespace (NID) and the SHA of the new
`rad/sigrefs`. This information can be used by the fetch protocol to
fetch only those `Oid`s for those namespaces.

Add a new stage `SigrefsAnnouncement` that captures this behaviour by
only asking for the advertised `Oid`s and updating the corresponding
`rad/sigrefs` for each namespace. Note that this is a special case of
the `SpecialRefs` stage and so the protocol chooses between these two
by checking if there was announcement passed through. The following
stage, `DataRefs`, stays the same.

Note that this stage can only be activated via a `pull`, and `clone`
stays the same.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-09 17:47:10 +01:00
Fintan Halpenny d27cd0db0c
node: minimise refs announcement to key and Oid
The RefsAnnouncement previous announced its complete set of
`rad/sigrefs`. This was useful in that verification can occur on the
receiveing side immediately by verifying the refs and proceeding with
the fetch -- note that it did nothing with the ref information it
received.

This can be minimised to only advertising the namespace, i.e. the
NID, and the new SHA of the `rad/sigrefs`. This is represented by a
new message type, `RefsAt`, which contains the NID and the Oid.

For checking if an announcement is stale or fresh, the receiving
side can check the existence of the SHA in its Git repository.

The tests are appropriately modified to use NIDs and Oids.

To prepare for a follow-up change, the announcement data is also
threaded through for the fetch protocol to focus fetching only the
advertised set of Oids.

N.B.: because the refs aren't communicated, the verification happens
when fetching since the data needs to be fetched before
verifying. This is a trade-off with minimising the message payload for
verifying earlier.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-09 17:47:09 +01:00
cloudhead 947f1bfc01
node: Use variable subscribe backlog delta
Previously, we would always ask nodes for messages from one hour ago.
This meant that we could miss messages if our node was offline for more
than an hour. This change uses a variable backlog by checking the
timestamp of the last gossip message we received.
2023-11-08 15:10:11 +01:00
cloudhead f0754330c9
Update `sqlite` dependency to 0.32.0 2023-11-08 15:08:01 +01:00
cloudhead f9df360171
node: Persist historical gossip messages
Instead of storing received gossip messages in an in-memory `BTreeMap`,
we persist them to the database (`addresses.db`). There are two reasons
for this:

1. Node restarts do not wipe the state, meaning that it is much less
   likely that some gossip message will get lost before reaching all
   nodes.
2. This will allow us in the next commit to know how far behind we are
   after a restart, to be able to request the correct time range of
   gossip messages that we missed while offline.

Note that we now share the addresses.db file between two connections and
stores, which we don't do anywhere else. Having multiple connections to
the same database is fine, but the way we're having to do it is a bit
awkward. In a future patch, we could pull out the database schema from the
`address` module and rename the database to make things clearer.
2023-11-08 13:15:56 +01:00
Fintan Halpenny 75b52e28b2
radicle: separate repository validation
Make the validation logic customisable by extracting it out into a
separate trait that can be defined by other crates.

The Repository validation logic stays the same.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-06 12:06:13 +01:00
Fintan Halpenny ef6ae0fa84
radicle: move service tracking config to radicle::node
The `service::tracking` module has nothing specific to the
radicle-node code. This means that it can be moved to radicle under
the `node::tracking` module.

This makes it more resuable in other contexts, for example, for
fetching logic.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-06 12:06:13 +01:00
cloudhead 1d167581f2
cob: Introduce `xyz.radicle.id` COB
Introduces a new COB type to store repository identity documents.

The reason for this change is to:

1. Simplify the code, as the identity document logic resembled a COB,
   yet it had custom logic. This allows existing COB code to be used for
   identities.
2. Make identity document update logic more flexible, since COB actions
   can be added in the future.
3. Re-purpose existing tools around COBs to work on identities, eg. `rad
   cob`.
4. Unify the concept of an identity change proposal, with regular identity
   changes. This means we can remove the `id.proposal` COB in favor of
   using the `id` COB itself.

Notes
-----

* Each repository has one Identity COB.
* The `Proposal` COB has been repurposed into the `Identity` COB.
* Identity documents are stored as *embeds* inside the Identity COB
  actions.
* The action that contains new document versions is called `revision`,
  just like the Patches COB.
* The namespaced `rad/id` ref is a symbolic reference to that Identity
  COB.
* The canonical `rad/id` ref is a direct reference to the commit in the
  Identity COB that contains the latest *accepted* revision of the
  document.
* All commands for managing identities have been folded into `rad id`.
  Hence `rad delegate` and `rad edit` are removed.
* The concept of "rebasing" an identity document is gone.
* The `rad id` output has been updated to match the style of other
  commands.
* When a revision has enough signatures, it is automatically adopted as
  the current identity, there is no longer the need for a "commit"
  action.
* When an identity revision is proposed, and the current identity has a
  threshold of `1`, that identity is automatically accepted due to the
  above point.
* The idea of "verifying a peer's identity branch" no longer applies, as
  COBs cannot be verified one history at a time.
* Since the root commit of the Identity COB does not have a "Resource"
  to point to (it would have to point to itself, which is impossible),
  we've made the resource id optional for COBs.
2023-10-12 17:32:33 +02:00
Alexis Sellier a7cd7b25f5
node: Allow rate-limitting to be configurable 2023-09-18 11:14:19 +02:00
cloudhead 09a284f09c
node: Make fetch timeout configurable
When a fetch is user-requested, a timeout can be supplied that is passed
down to the worker.

When a fetch is service-requested, a default timeout is used.
2023-09-01 12:13:45 +02:00