Commit Graph

169 Commits

Author SHA1 Message Date
cloudhead 09f796234d
radicle: Compute root OID for older remotes
For older clients, `refs/rad/root` won't be set. In that case, compute
it locally based on `refs/rad/id`.
2024-10-31 17:34:03 +01:00
Fintan Halpenny de1958fab0 radicle: refactor doc
The aim of this change is to make the `Doc` type more safe to use by approaching
the design via [Parse don't validate][[0]] approach.

The problem with the previous approach was that all field were `pub` and thus a
`Doc<Verified>` could easily be mutated and serialized. Granted, the code that
used the serialization would tend to verify the `Doc` first, however, this
approach *ensures* that only a verified `Doc` can be serialized. It also meant
that trying to add new data that would follow the parse approach would require
more generic parameters on top of the existing `PhantomData` parameter, i.e. we
need to do something like: `Doc<RawField, V> -> Doc<ValidField, V>`.

The new approach splits the type into two separate types: `RawDoc` and `Doc`.
The former is allowed to be mutated at will, and uses types that are less
strict. The latter is the valid type that can only be constructed by validating
a `RawDoc` (or the `initial` constructor). The `Doc` type's fields can then only
be accessed by read-only methods.

Solves the problems above by only allowing mutations to `RawDoc`, as well as,
new fields being added to `RawDoc` which are then validated via
`RawDoc::verified`.

[0]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
2024-10-22 14:58:12 +01:00
cloudhead f83c116742
radicle: Fix clippy warnings around `unwrap`
The policy should be that we don't use unwraps outside of test code,
unless we have a good reason to.
2024-10-21 16:42:06 +02:00
cloudhead 989edacd56
Include new `rad/root` in signed refs
We ensure that a `rad/root` ref is included in the signed refs file
under `rad/sigrefs` for all remotes. This prevents a certain kind of
"grafting" attack where signed refs can be copied between repositories,
by having the peer sign over the identity root together with the data refs.

When verifying signed refs, we ensure that the ref is present and points
to an identity branch root that matches the repository identity containing
the signed refs.

Alternatives: lots of alternatives were considered, but this one doesn't
introduce any changes to the signing. The `rad/id/root` name was
considered but is invalid due to `rad/id`.
2024-10-21 16:32:49 +02:00
cloudhead 24066c2600
radicle: Test the signed refs grafting attack 2024-10-21 16:32:49 +02:00
cloudhead 855327d303
cob: Change APIs to take URIs for embeds
To facilitate edit actions, take URIs instead of the actual blobs. This
means API callers don't have to load all the blobs just for them to be
re-hashed when an edit action is submitted.

There are some peculiarities when dealing with the `Identity` COB since
the embed is the identity document itself. We handle that special case.
2024-10-14 12:51:36 +02:00
Alexis Sellier 729a6e057e
cobs: Fix COB drafts to work correctly 2024-10-01 15:59:23 +02:00
cloudhead 74336f961a
cli: Write comments into draft patch review
We tidy things up with an explicit `Brain` type that stores the accepted
state of the code.

Review comments are added to the patch draft.
2024-07-24 16:46:24 +02:00
Fintan Halpenny 54d23545ff
radicle: port quorum code to Canonical
Remove the old `quorum` function in favour of using the newly introduced
`Canonical` code. Port over the tests to use the `Canonical` type. The change is
minimised by introducing a helper function called `quorum` in the test module
that acts like the old function.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-07-03 15:10:43 +01:00
cloudhead 6f91f2fb8f
clippy: Fix a few warnings 2024-06-20 15:10:39 +02:00
Fintan Halpenny 3260046c67
radicle: fix diverging quorum
When calculating a quorum there was a case where two heads that were equal would
result in double-counting, allowing the quorum to pass a threshold higher than
the expected votes.

To prevent this, each head is immediately counted as a direct vote. Then, when
comparing the head to the rest of the set, if they are equal that iteration
would be skipped. This is because the merge base of two equal commits is that
commit, resulting in the double-counting.

Note that the `skip` can also skip the current head, so `i + 1` is used.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-06-20 14:57:00 +02:00
cloudhead b24f8a30d6
node: Update inventory when unseeding
Previously, our advertized inventory was not always correct: when we
stopped seeding a repository, it was still advertised as part of our
inventory until node restart, because the `Storage` type doesn't have
access to seeding policies.

In this change, we remove the concept of inventory from `Storage` and
make the authoritative place for it the routing table in our database.

To make this work, we have to add our node to the database on profile
creation, to not violate the foreign key constraint on the routing
table. Hence, the tests are changed to include our alias which is now
always available.
2024-06-12 15:36:34 +02:00
cloudhead f78e5a4281
node: Update sync status for private repos
Only public repos were being updated on node initialization.
2024-05-21 13:32:40 +02:00
cloudhead 25c6660a59
node: Use inventory cache for checking missing
Use the cache when checking whether we're missing an inventory.
This is a lot faster.
2024-05-13 16:37:02 +02:00
cloudhead cce46a3d8b
Try to improve performance of repository open
By using `open_ext` with `BARE`, we defer loading of the Git config,
which has been shown to be expensive.
2024-05-13 16:37:02 +02:00
cloudhead fe5757d122
Tidy up environment variables
* Try to use constants instead of strings
* Move crypto seed override out of radicle-crypto
* Use GIT_COMMITTER_DATE for commit overrides
* Use RAD_LOCAL_TIME as general time override
* Allow variables to be used in release mode
2024-04-26 10:38:54 +02:00
cloudhead 6be77ca9c3
radicle: Implement `remote_refs_at`
Implement this function on `RemoteRepository`, and move `repositories`
to the trait level.

This function will be used to quickly get the `RefsAt` of all repository
remotes.
2024-04-05 21:59:28 +02:00
Fintan Halpenny cd9b46fe51
radicle: fault tolerant thresholds
There are two areas where we can be more tolerant of delegate namespaces
being missing or not validating:
1. Calculating the canonical HEAD
2. Fetching from a remote

In 1. the protocol is tolerant in that if the local node does not have
the default branch for a delegate, it will still attempt to use any of
the delegates it does have to reach the threshold.

This is made safe by ensuring that if the threshold is being updated
then the node performing the update must have a threshold of delegates
locally in their storage. It also made safe by 2.

In 2. the protocol is tolerant by allowing delegates to be missing
from the serving side, as long as they can still meet a threshold of
delegates. This is further tolerant, when validating the received
data, a threshold of delegates are valid to consider the fetch
successful -- otherwise it will fail.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-04-03 14:30:05 +02:00
cloudhead 991505ec9d
radicle: Switch error type for repository methods
This simplifies error handling for the next commit.
2024-04-03 14:25:02 +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 6288dd0a24
radicle: Load inventory lazily
Instead of loading the inventory cache on storage open, load it
when the `inventory` function is called and the cache is `None`.
2024-03-21 18:03:18 +01:00
cloudhead d771ce8522
node: Make `Storage::refresh` private 2024-03-21 12:40:58 +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
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
cloudhead d7c88f12ab
radicle: Fix some doc comment issues 2024-03-12 16:13:08 +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 49584f4e73
radicle: pinned repositories configuration
Introduce pinned repositories to the `profile::Config`, i.e.
`config.json`.

This is used for pinning repositories in the httpd `/projects` handle.

To allow returning all repositories, an `all` query parameter is also
included.

This is non-backwards compatible change, since we introduce the `show`
query parameter which, if not supplied, will default to `pinned`.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-01-24 10:55:27 +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
Fintan Halpenny 5dbd9bb2a3
radicle: ensure clean up on rad::init error
It is possible that there can be errors while configuring a Radicle
`Repository` during `rad::init`, for example:
- configuring working copy remote fails
- signing references fails
- setting the identity head fails
- setting the project head fails

If any of these occur, a best effort is made to remove the
`Repository` and the working copy remote, so that the caller may fix
any issues and retry.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-01-19 11:35:14 +01:00
cloudhead 7169e97bd5
cli: Tell the user when canonical head is updated
This is useful especially in multi-delegate repos, since patches can be
merged without the head being updated.
2024-01-05 12:09:30 +01:00
cloudhead 6ca7da2768
radicle: Rename many more methods using new naming 2023-12-06 10:02:00 +01:00
Fintan Halpenny 5b1cdbaa4a
radicle: remove repo iff there's no local rad/sigrefs
If the local peer does not have any `rad/sigrefs` then there is no
risk of any data corruption. In this case the repository can be
removed entirely.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-30 10:48:14 +01:00
Fintan Halpenny 5f96429005
radicle: remove `WriteStorage::remove`
The `remove` method is no longer being used, with `clean` taking its
place.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-30 10:48:14 +01:00
Fintan Halpenny d23120ca8b
radicle: add clean method
Add a `clean` method to remove the remotes of a repository that are
not the local peer nor the delegates.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-24 10:48:05 +00:00
cloudhead 53013f306f
cli: Improve `rad ls`
Distinguish between repos that are simply tracked, vs. repos that are
cloned (and thus forked). By default, we don't show repos that don't
have a local fork.
2023-11-23 13:51:47 +01:00
cloudhead d4ec2176bb
Update Rust toolchain to 1.74 2023-11-17 16:58:24 +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
Fintan Halpenny ac849f1fd5
radicle: add ReadRepository::contains
It can be useful to be able to check if a repository contains a given
commit. Add a `contains` method to the `ReadRepository` trait to
handle this.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-17 16:09:42 +01:00
cloudhead cdcc7cfaf9
node: Emit events for gossip messages 2023-11-17 14:56:42 +01:00
Fintan Halpenny e71f07e592
radicle: temporary repository helper
It's possible that a temporary directory can be created on a different
mount, and when moved during a clone, creates an I/O error.

To prevent this, add a method to `Storage` which creates a temporary
directory and `Repository` with the path in the form
`<rid>.lock`.

This `.lock` path name can easily be skipped when listing
repositories. Further tooling can be added for detecting leftover
`.lock` directories if, for some reason, they were not cleaned up by
the `tempfile` crate.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-16 12:01:10 +00:00
Fintan Halpenny fbc5c5a027
radicle: Check signed refs are unchanged
Instead of creating the tree to check if `rad/sigrefs` has changed, it
is possible to check that the signature of the signed refs being
updated is the same as the old one. If so, then there are no
changes. Otherwise, there must be changes and the update can continue
to create the necessary Git objects.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-16 11:05:41 +01:00
cloudhead d88cce484a
radicle: Simplify canonical head quorum
Instead of trying to handle cases where delegate branches diverge, we
simply throw an error in those cases, which prevents the canonical head
from updating.

This simplifies the code a lot, and forces the user to take action when
such an event occures.
2023-11-10 13:42:16 +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
Fintan Halpenny 61b06553b4
radicle: use git::raw::Error for reference_oid
The minimal error for the `ReadRepository::reference_oid` method is
`git::raw::Error`.

Change the error type from `git::ext::Error` to `git::raw::Error`.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-09 17:47:07 +01:00
Fintan Halpenny 82a53e3a53
radicle: UserInfo for Repository::create
The fetch V2 improvements introduced a `UserInfo` type that is used to
set the Git config's name and email when cloning a repository. This
`UserInfo` can also be used when creating a new `Repository`.

The `Storage` type now takes a `UserInfo` so that it can be passed to
`Repository::create`, for creating a new repository.

For testing, this requires adding a new `fixtures::user` so that
testing `Storage` instances can be easily constructed.

It also means that the `radicle-cli` tests all changed with new SHAs
since the name and email are used as part of the hashing input.

N.b. the order of the timelines while testing the identity cob
changed. It's not clear why that is.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2023-11-09 16:43:34 +01:00
Fintan Halpenny 981172aa4f
radicle-fetch
The `radicle-fetch` crate introduces a method for fetching from a
server using Git protocol V2, which provides the implementation
control over performing a sequence of fetches and validation over
domain data.

The protocol is built on top of the suite of Gitoxide crates[[0]]. It
builds wrappers on top of Gitoxide's refdb, odb, and transport layers
-- as well as taking the ls-refs and fetch code straight from the code
base to simplify for the needs of this crate.

[0]: https://github.com/Byron/gitoxide

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 87187d47e5
radicle: add remove to WriteStorage
Add the ability to remove a repository, given its RID.

This is useful in the case of attempting to clone a repository, and if
that process fails -- for example, if the repository is private --
then it's necessary to clean up the initially created repository.

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 f1f32d8574
radicle: return verified doc
The `identity_doc_at` and `identity_doc` methods on `ReadRepository`
returned the unverified identity document. Every usage of these
methods would always verify the document after.

Simplify the code by calling verified in the methods and directly return the
verified identity document.

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