Commit Graph

83 Commits

Author SHA1 Message Date
Fintan Halpenny 1f4fcc5e6a radicle: move to `signature` crate
The motivation of this change is to move away from tying signing to be
specifically for ed25519. The reason being that the protocol will want move
towards two different kinds of signing – the node signing artifacts, and an
author signing artifacts.

This change captures the former, node signing, by introducing a `Device` type
that is the `NodeId` – a PublicKey underneath the hood – and a signing
mechanism. This allows the replacement of the `Signer` trait being used – which
always assumed a `PublicKey`. Instead, the `Device` is constructed with
`NodeId`.

In `radicle-cob`, a signer is expected to implement
`signature::Signer<ExtendedSignature>`, and everywhere in `radicle`,
`radicle-node`, `radicle-cli`, and `radicle-remote-helper` is expected the
signer is expected to implement `signature::Signer<Signature>`.

A `Device` implements both of these but only requires
`signature::Signer<Signature>` to do so – since an `ExtendedSignature` is
essentially `(PublicKey, Signature)`, and the `NodeId` of the `Device` can be
used.
2025-05-22 09:09:57 +02:00
Fintan Halpenny a90aabb1fe node: rate limiter for channel reads
This introduces rate limits for the `ChannelReader` to limit DDoS attacks and
attempts to upload repositories that are larger than a node is will to permit.

The limiter sets the total number of bytes it is will to accept in a single
exchange, defaulting to 500MB. This means that initial fetches will prevent
large repositories, but is plenty for new packfile data to be sent in subsequent
fetch exchanges.

The limit can be configured within the node's config file, under the limits.
2025-04-25 20:27:51 +02:00
Fintan Halpenny e412168be3
node: do not fail on `set_head`
The failure to set a HEAD ref, due to quourum, during a fetch should not fail
the fetch entirely.

Instead, log the success and failure, or return an error in other cases.

In fact, any failure to set the head could have resulted in notifications not
being sent, nor caching to occur.
2024-11-18 12:32:40 +01:00
Fintan Halpenny f9c3523183
e2e: improve flake in missing_delegate_default_branch
The `missing_delegate_default_branch` test was flakey and would fail checking if
Bob had the `rid` in storage.

This can be improved by waiting to ensure that Bob has a `RefsFetched` event
before performing the check.
2024-11-11 11:36:09 +01:00
Fintan Halpenny eb095c109b
fetch: allow missing default branch
When fetching, the validation logic would prune a delegate if it was missing the
default branch. Originally, this was done to prevent any broken state, but later
it was realised that this can be too restrictive and would not allow nodes to
recover into an improved state.

Remove this check so thtat nodes can still fetch from each other when the
default branch is missing.

An e2e test is added to show that nodes can still fetch from each other and
still receive other references, even when a delegate is missing their default
branch.
2024-11-06 16:47:01 +01:00
Fintan Halpenny 29e681210b
node: test multiple offline inits
Ensure that when a node initialises multiple repositories, those
repositories can successfully be fetched by another peer once they
connect.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-07-22 10:48:10 +02:00
Fintan Halpenny d56d619f56
fetch: remove `.keep` files
A `.keep` file in the `objects/pack` directory acts as a lock to ensure that
operations like `git gc` do not clean up `.idx` files while a fetch is being
performed. It is safe to remove the `.keep` file once a fetch has completed and
a reference points to a given object in the packfile.

To ensure this happens in `radicle-fetch`, a newtype `Keepfile` is introduced.
It uses special `Drop` semantics by attempting to remove the file when the
object is dropped -- logging a warning if it fails to do so.

`FetchState` is augmented to keep a set of the `Keepfile`s. This ensures that
when the fetch completes, either successfully or with a failure, when
`FetchState` is dropped then so are the `Keepfile`s.

We ensure that the expected behaviour of removing the `.keep`files occurs by
adding a check in `e2e::test_replication` to check Alice's repository's packs.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-05-16 15:47:14 +02:00
cloudhead a9b94b0ad6
node: New relay configuration for nodes
Relay now has three values: "always", "never" and "auto".

The first two operate like the previous `true`/`false`. The
new (default) value turns relaying on for nodes with public (external)
addresses and off for nodes without. This should reduce redundant traffic on
the network, that was causing the rate limiters to trigger.
2024-05-02 11:45:29 +02:00
cloudhead d77b5d5abe
node: Add 'disconnect' command to handle 2024-04-23 09:52:25 +02:00
cloudhead 7e13e0759f
node: Simplify handling of fetch completion
* Don't branch based on whether it was a user-requested fetch or not.
* Only announce inventory on clones of public repos.
2024-04-03 14:25:02 +02:00
cloudhead 0bcec941ee
node: Change behavior on conflicting connections
Instead of dropping the existing connection(s), we drop the same
connection on both sides, by using a simple rule that yields the same
result whether the node sees the connection as inbound or outbound.

The trick is to use something that both nodes agree on, for instance
whos public key is greater than the other, and use that information
to close the same connection on both sides.
2024-03-25 12:48:27 +01:00
cloudhead d771ce8522
node: Make `Storage::refresh` private 2024-03-21 12:40:58 +01:00
cloudhead 0e880e12e6
node: Cache node inventory
For large seeds with many repos, it's expensive to traverse the
inventory everytime the node connects to a new peer, for example.

To improve the situation, we cache the inventory per `Storage` instance,
and add a `refresh` method to refresh the inventory.

This method is called when the node is told to synchronize its
inventory.
2024-03-21 12:40:58 +01:00
Fintan Halpenny bb4ed84b95
node: duplicate fetching logic respects RefsAt
Two concurrent fetches can occur from the same peer, while also being
for a different set of references. This was seen in the
`test_outdated_sigrefs` test. Alice would have an ongoing fetch from
Eve, and concurrently, Eve will create a new issue -- thus creating a
new `RefsAt` message -- and Alice would attempt to fetch this.

The previous logic would only check that Alice is fetching from Eve
and ignore the new updates -- meaning the test could fail. However, if
the `RefsAt` are checked then the other fetch can be recognised as
new, and queue it.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-03-09 13:43:08 +01:00
cloudhead 9576a6496b
node: Various small fixes 2024-02-12 11:37:31 +01:00
cloudhead c94df2d5cd
node: Make inventory announcements more robust
Allow some margin of error when sending multiple inventory announcements
at a time. Previously, they might end up with the same timestamp, so
only the first announcement would be processed by nodes. Now, we ensure
that the timestamp is updated.
2023-12-20 15:44:02 +01:00
Fintan Halpenny 8eac221f4c
node: Properly handle dequeued fetch results
When a peer is performing a background and foreground fetch from two
different peers, the background fetch result was being used as the
result for the foreground fetch. This results in inconsistencies,
since the foreground peer has new updates from themselves.

The reason for this issue was the fetches that were queued were not
correctly being handled, and the other peer's results were being
returned instead.

This fixes the queueing and dequeueing by ensuring that the queued
fetch occurs, passing the correct result channel. This fetch will then
be subsequently dequeued correctly and the foreground fetch obtains
the correct result.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-12-19 15:02:14 +01:00
Fintan Halpenny a6ba1b0103
node: inspect outdated sigrefs test
This test is flakey and requires further investigation. Some extra
assertions are added to ensure that the expected behaviour is
happening upon fetching. It is unknown if the fetch queue is behaving
properly, and it's suspected that some existing fetch results might be
used for queued fetches.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-12-19 15:02:14 +01:00
Fintan Halpenny e894b6512d
node: prevent fetch response if no repo policy
If a node had removed a seeding policy and another node fetched from
it, then it would perform the fetch.

Instead, check the policy during the authorization step and prevent
the fetch is the resulting policy is `Block`.

Note that the policy will be `Block` iff the `Block` policy was
specifically set or the `Block` is the default policy for the node.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-12-13 14:19:46 +01:00
cloudhead 6ca7da2768
radicle: Rename many more methods using new naming 2023-12-06 10:02:00 +01:00
cloudhead 1b026ae0ee
node: Rename node `tracking` module 2023-12-06 10:00:45 +01:00
cloudhead e65595c4c7
node: Rename commands with new "seed" nomenclature 2023-12-06 09:56:58 +01:00
cloudhead f453090e27
cli: Consolidate policy/scope variants
* Policy::Track -> Policy::Allow
* Scope::Trusted -> Scope::Followed
2023-12-06 09:56:58 +01:00
Fintan Halpenny 511165bfc5
fetch: prevent missing default branch
If a delegate is missing the default branch for a project certain
computations will fail, e.g. getting the canonical mainline branch.

One prevention for corrupting data is refusing to fetch a delegate
that does not have the default branch, when the repository is a
project. It is assumed that the repository is not a project if calling
`Doc::project` returns an error, since the error variants are not
found and parsing errors.

This is only checked in the case of a delegate since non-delegates are
safe to created COBs that don't require the default branch,
e.g. creating an issue.
2023-11-30 13:50:39 +01:00
Fintan Halpenny 703a6d8025
fetch: Ignore sigrefs that are behind
Previously, if a delegate's namespace was fetched and its rad/sigrefs
was behind the current state, then it would fail to perform the fetch.

Instead, when the ancestry path is checked the outcome can be used
during the update:
1. If the tip is equal, then the update can be marked as skipped.
2. If the tip is behind, then the update can be rejected, iff the
   policy does not allow it.
3. If the tip is ahead, then the update is a fast-forward.
4. If the tip is diverged, then the update results in a failure, iff
   the policy does not allow it.

The protocol also prunes any delegates's updates when the `rad/sigrefs`
is behind and returns an error if they are diverged.

For any non-delegates, it will simply prune them.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2023-11-09 13:12:21 +01:00
Fintan Halpenny ea7f26dbdf
radicle: wire up radicle-fetch
Wire up the `radicle-fetch` crate to the fetch logic in the
`radicle-node` worker.

Any required traits for storage, identity, tracking, and transport
used by `radicle-fetch`.

The worker adds a new module to run the `git upload-pack` process and
pipes the `stdout`/`stdin` to the respective channels.

The `Worker` type required adding a `FetchConfig` to easily allow the
configuration of fetches and passing the signer and tracking store
location through to the fetch `Handle`.

The channels code is adapted to have a new writer that always flushes
when a call to `write_all` is made. This is necessary since the
Gitoxide code never calls `flush`, and so it will hang since no data
is sent until `flush` is called.

Since `UserInfo` is used to set up a newly cloned repository, this
changed some of the SHA1 hashes output in the CLI test examples. It
was also necessary to update the CLI's clone code to be deterministic
in its output for tests.

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 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 b503aa6be4
radicle: validations
Instead of returning the unsigned references, the validation of a
repository's remotes returns the set of validation errors. The caller
is then expected to handle these validation errors appropriately, for
example, by logging them and re-raising an error.

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
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
cloudhead fae518bca2
node: Improve `rad node connect` feedback
Only return once the connection is established or failed.

It's still not perfect, as several failure scenarios are not handled,
but it's an improvement.
2023-08-09 12:49:15 +02:00
cloudhead e888398519
node: Populate address book with bootstrap nodes
As a starting point, we populate the address book (if empty) with some
bootstrap nodes which are trusted.

To prevent these nodes being connected to during tests (including e2e
tests), we create a new constructor for `Config` that is used in tests.

Note that a `cfg(test)` check is not enough given that e2e tests don't
have that set.
2023-08-09 12:39:33 +02:00
Alexis Sellier 41bff1b3bb
node: Make user connections persistent
While the node is running, connections made via the `Connect` command
should be retried.
2023-07-26 12:52:11 +02:00
Alexis Sellier eb701be033
node: Make node aliases required
This is a fairly substantial change, which adds a new configuration file
to the user's `$RAD_HOME` that includes node configuration, including
the alias, and also makes node aliases required.

When running `rad auth`, the user is now prompted for an alias, which
defaults to `$USER`.

When running `rad self`, the alias is now shown.

If the user runs radicle without a config file, the defaults are loaded,
and `$USER` is used as the alias.
2023-07-11 18:08:59 +02:00
Alexis Sellier 1a1e63d998
node: Implement `sessions` command
Returns session information through the control socket.

We also update `rad node status` to display sessions.
2023-07-04 00:26:31 +02:00
Alexis Sellier 0886ab0a3c
node: Get the failing fetch tests passing
We fix the failing fetch tests by:

1. Not verifying our own refs, unless we're cloning, since we're
   otherwise not fetching our own refs.
2. Always force-fetching `sigrefs` from remotes into the staging copy.
3. Making sure that sigref updates are fast-forward before transfering
   the remote into the production copy.
2023-05-03 16:16:34 +02:00
Alexis Sellier 1326774d4f
node: Add failing test for sigrefs fetch
Fetching sigrefs fails if the remote ref is an ancestor of the local ref.
2023-05-03 16:16:34 +02:00
Fintan Halpenny 024218014b
node: test case for outdated refs
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-04-28 14:26:47 +02:00
Fintan Halpenny 5a48cdf507
node: ensure that we can fetch with a non-existent tracked node
Ensure that non-existent tracking relationships do not affect
fetching.

This is tested by first performing a clone, by `bob` from `alice`,
after `bob` tracks a remote that does not exist. `alice` creates an
issue which is then fetched by `bob` -- to ensure the fetching
scenario is also tested.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-04-28 14:26:47 +02:00
Alexis Sellier 2598886018
node: Delete unsigned refs on fetch 2023-04-19 11:32:46 +02:00
Alexis Sellier 94bef61944
node: Return only remotes that were fetched from
Instead of returning the namespaces that we fetched with,
we return the remotes that were actually fetched from.

This can differ if some trusted peers were not available
on the remote node.

Previously, if that was the case, it would cause errors
since the remotes were looked up and that lookup failed.
2023-04-17 18:58:56 +02:00
Alexis Sellier 0e7ffcc916
node: Properly validate signed refs on fetch
With this change, we not only verify the signed refs before
the repository "transfer", but also validate that all refs in the
repo are signed and there is no discrepancy with the signed refs
file.
2023-04-15 00:17:17 +02:00
Alexis Sellier a05de1fbf3
node: Test fetch with large repository
This is for stress-testing the replication.
2023-04-14 13:15:52 +02:00
Alexis Sellier f4257eb953
node: Add setting for limiting fetch concurrency
Can be passed via `--limit-fetch-concurrency <num>`.
2023-04-14 13:15:52 +02:00
Alexis Sellier ea4294c79f
node: More realistic concurrent fetch test
This implements a test which can optionally use a lot more data by
setting `RAD_TEST_SCALE` to a higher value.
2023-04-14 13:15:52 +02:00
Alexis Sellier 31647521fa
node: Properly close worker channels
We need to send a 'close' event to the worker channel for it to exit
without an error.

Also updated and improved a bunch of the logs.
2023-04-14 13:15:52 +02:00
Alexis Sellier ed4c266a51
node: Don't prune owned refs
Owned refs were being pruned on fetch.
2023-04-03 22:09:41 +02:00
Fintan Halpenny adf6312a55
node: fetch trusted peers on clone
When a repository does not yet exist during a fetch, i.e. a clone,
then only delegates are being fetched.

Augment Namespaces::Many to hold the trusted and optional delegate peers
separately. Doing so allows the construction of the variant without
the repository existing, but tracking relationships existing.

This variant can then be used to build refspecs for both the trusted
peers and delegates when doing a cloning fetch.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-03-31 12:23:31 +01:00
Fintan Halpenny f78e010c55
node: e2e test simulating a peer migrating their key
Previously, if a peer created a project which was replicated and then
moved to another setup, ultimately deleting that project, then they
would not be able to fetch it again since their remote namespace would
be excluded from the fetch.

This test confirms that a peer can remove their project and clone it
again from a peer that has replicated the project. This is simulated
by directly removing the project directory in the test and fetching
the project from the other peer.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-03-31 12:19:37 +01:00
Fintan Halpenny 6e08f0be59
node: enable test_fetch_trusted_remotes
With the latest fetch updates, the test for fetching trusted remotes
can be enabled.

The caveat is that its expected behaviour is to fetch the delegate
upon a clone and fetch all trusted peers on a subsequent
fetch. Fetching all trusted peers is left as a follow-up task.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-03-31 12:19:36 +01:00