Previously, trying to load `SignedRefs` for any given remote would
result in a fetch failure.
Teach the fetch to be more resilient by pruning remotes that result in
an error when loading `SignedRefs`, and add the error to the
validation failures.
Split up signed references into its read and write components.
On the write side:
- Preserve the old behavior of writing references to the blob `/refs`
and sign over the blob.
- Ensure `refs/rad/root` is contained in the `/refs` blob.
- Ensure `refs/rad/sigrefs` is *not* contained in the `/refs` blob.
- Introduce a new (internal) reference `refs/rad/sigrefs-parent`
so that no two `/refs` blob are equal, even if they contain
the same set of (non-internal) refs.
On the read side:
- Preserve the verification of the signature in `/signature` and
the reference `refs/rad/root` (if present).
- Fail verification of `refs/rad/root` is not present.
- Protect against replay attacks by walking the history of the
head of `refs/rad/sigrefs`, skipping interpretation of `/refs`
blobs in case they are identical to a previous `/refs` blob.
This is achieved by searching for repeated contents of the
`/signature` blob.
The reference `refs/rad/sigrefs-parent` is never read from or written to
the Git repository in storage.
The pre-existing implementation of signed references did not include
a nonce, thus duplicate but legitimate sets of references could not
be distinguished from maliciously replayed sets of references.
The new implementation uses `radicle-git-metadata` which is moved from
`dev-dependencies` to `dependencies`.
The format for IPv6 addresses was changed in
df8e4e6c88 to require '[' and ']'.
IPv6 addresses that were stored in the database in the past must be
migrated.
Most consumers of `Device` are interested in the public key of the
device, and `Keypair` is the trait from `signature` which captures
this, so implement it.
Further, for boxing a signer, introduce a new trait `BoxableSigner`
(to remain dyn-compatible) which additionally requires `Keypair`.
Ensure that the public interface of signed references does not leak its
implementation details.
This allows the evolution of the interface in a safer manner, and does
not leak implementation details to the rest of the crate or any
dependents.
Avoid leaking the types of the signed reference types by moving their
`Arbitrary` implementations to a `refs::arbitrary` sub-module.
Since `SignedRefsAt` require a correctly signed payload, a helper
constructor is added: `signed_refs_at`.
In turn, a function, `arbitrary::with_gen` is introduced so that this
constructor can be easily called with a `Gen` value.
radicle: refactor `Home::load`
- Refactor out getting the subdirectories to ensure they are the same
across `new` and `load`
- Return all missing directories in the error message
- Document `load` and make it `pub`
Configuration of database connections is not performed on `open`, which
leaves room for error (e.g. to miss specifying configuration).
Methods on `Profile` automatically supply the configuration of the
profile.
In testing code, just using the default configuration suffices.
Change the default value for the `synchronous` pragma from `FULL` to
`NORMAL`.
With this change, SQLite will not aggressively `fsync()` after every
transaction, so there is considerably less disk pressure as disk I/O
can be batched.
See <https://sqlite.org/pragma.html>:
> `WAL` mode is safe from corruption with `synchronous=NORMAL`, and
> probably `DELETE` mode is safe too on modern filesystems. `WAL` mode is
> always consistent with `synchronous=NORMAL`, but `WAL` mode does lose
> durability. A transaction committed in `WAL` mode with
> `synchronous=NORMAL` might roll back following a power loss or system
> crash.
> Transactions are durable across application crashes regardless of the
> synchronous setting or journal mode.
Also:
> You lose durability across power lose with synchronous `NORMAL` in `WAL`
> mode, but that is not important for most applications. Transactions
> are still atomic, consistent, and isolated, which are the most
> important characteristics in most use cases.
So, there is no risk of database corruption, and in the extreme
cases of sudden power loss or system crash, some transaction may
roll back.
See also <https://sqlite.org/wal.html>
Co-authored-by: Yorgos Saslis <yorgos.work@proton.me>
Instead of introducing our own names and aliases, directly model SQLite
pragmas with the defaults that they also take in SQLite, to avoid
confusion.
Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
Introduces the ability to explicitly block a peer via the node control
socket. Previously, the node only exposed follow and unfollow commands.
While the underlying policy database schema supported a Block variant,
there was no mechanism to trigger this state via the client handle.
The new block command:
1. Updates the node's follow policy to Block.
2. Immediately disconnects the peer if a session is active.
The `SeedingPolicy` type now requires a `Scope`, however existing
configurations allow it to not be specified.
Introduce a `radicle::node::config::Scope` type that allows for an
optional `policy::Scope`. The default value resolves to
`policy::Scope::All`. This preserves backwards compatibility for
existing configurations.
Recent versions of Windows support Unix Domain Sockets natively, see
<https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/>.
By using that feature instead of Windows named pipes, the difference
for handling communication via the control socket comparing Unix-like
systems and Windows becomes smaller:
1. No special paths like `\.\pipe\…` have to be handled.
2. Not two I/O mechanisms are abstracted (named pipe and UDS) but
just one.
3. `winpipe` relies on background threads while `uds_windows` does
not.
Once the feature `windows_unix_domain_sockets` (which is tracked at
https://github.com/rust-lang/rust/issues/150487) stabilizes, it will
likely be possible to just drop the dependency `uds_windows` and use
the implementation in `std::os::windows::net` directly.
Git for Windows horribly mangles the configured binaries through a POSIX
`sh`-like environment even on Windows. We might be able to figure out
which `sh.exe` we would have to execute on Windows, but this is too
brittle. Windows users will have to use other mechanisms (like `$EDITOR`
and `$PAGER`) to configure their programs.
Wire up the new `FetcherService`. This reduces the `Service` fetch
methods down to:
- `fetch`
- `fetched`
- `dequeue_fetches`
- `fetch_refs_at`
This simplifies the code by off-loading the logic to the `fetcher`
family of types, rather than handling the intricacies in the `Service`
itself.
The `Service` now just performs the necessary I/O based on the
returned events from the `fetcher`.
This also removes the fetching state from the node sessions. This
follows the single responsibility, and sessions now only care about
their connectivity.
Breaking changes:
- The `Connected` state of a peer no longer contains fetching
information, which was being returned as part of the JSON payload
result.
- The `rad debug` information for ongoing fetches contained the number
listeners awaiting for results, this was removed.
The previous error `UnexpectedState` was opaque, and ended up in a
confusing message when a user would try to edit the identity document
while not being a deleagte themselves.
Instead, provide a more specific error that mentions the `Did`
performing the action, and the action itself.
Note that the `UnexpectedState` variant was matched on during
evaluation, meaning that operations would still appear in the timeline
of the COB. With the change to the other variant, the timeline does
not record the operation anymore.
Previously, the `references_of` implementation restricted the set of
references to certain categories: heads, tags, notes, rad, and cobs.
This is too restrictive to allow peers to share any references they
want. The only exceptions is references `refs/tmp/heads` that are
created for creating patches.
A call to `Database::prune` with no limit could result in a
domain-defined overflow error.
If the limit is not provided, then it uses `i64::MAX`. It is
reasonable to expect that if the `usize` provided is more than
`i64::MAX` then the `i64::MAX` value is acceptable to use for pruning.
Introduce the `radicle-core` crate for housing data types that are at
the core of the Radicle protocol. The initial data type being added is
`RepoId`. To make the crate as lightweight as possible many of
dependencies are optional and gated by feature flags.
The crate is marked as `no_std` even though it unconditionally depends
on `radicle-crypto`, which is *not* `no_std`. This is to invite
refactoring of `radicle-crypto` to `no_std` at a later time.