We were using a breadth-first search which doesn't work as-is for
topological ordering in all cases. Instead of using Kahn's algorithm
which is a little complex, we switch to a depth-first search.
Instead of returning histories from `radicle-cob`, we return an
evaluated object which we build during initial graph traversal.
We do this so that branches can be correctly pruned when operations are
invalid at the application level. This way, new operations are not
building on top of invalid ones contained in the history.
To achieve this, we introduce a new `Evaluate` trait that is implemented
by all COBs, and we make change graph evaluation fallible.
Doing this means that we get `apply` errors returned for free when an
invalid transaction is applied, and no longer need to check for action
validity in multiple places.
We also implement a new `Dag::prune` method to avoid having to copy
graph nodes during traversal.
Add `deny.toml` with a few exceptions, and update certain dependencies
to get rid of duplicates.
Running `cargo deny check` will yield some warnings still, but no
errors.
Simplify graph traversal and evaluation by building in some of the
functionality into `radicle-dag`, namely the pruning fold.
We avoid building vectors of graph nodes this way, and simply iterate
over the graph in one go.
Rust 1.67 was announced[0]. Update the necessary files for running
1.67 and fix the clippy suggestions for string interpolation.
[0]: https://blog.rust-lang.org/2023/01/26/Rust-1.67.0.html
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
We're using petgraph to encode an operation based CRDT (each graph node
is an operation) as a DAG, and one of the important things to ensure
is that the final state is not influenced by the order of concurrent
operations. In this case a concurrent operation would mean two potential
graph traversal orders, as there's no direct edge between the two
concurrent ops. This is essential a "partial order" of operations.
To be able to test this, we'd want some kind of control over how
neighbors are iterated over I guess, when there's more than one possible
sort order. Say we have a graph like this:
┌────────b◄─────┐
▼ │
a d
▲ │
└────────c◄─────┘
There are two possible topological traverse orders: [a, b, c, d] and [a,
c, b, d]. Having a way to go through these different orders would
be super handy.
One option would be to allow random order traversal. This would allow us
to test all orders by running the test enough times to likely test all
permutations.
Since petgraph doesn't support this, we implement our own simple DAG in
`radicle-dag`, which implements random-order topological orders.
For now, we don't make explicit use of these improvements, we simply
replace the underlying graph with our own.