aranya_runtime/
command.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use alloc::{
    format,
    string::{String, ToString},
};

use aranya_buggy::{Bug, BugExt};
use serde::{Deserialize, Serialize};

use crate::Prior;

aranya_crypto::custom_id! {
    /// An ID constructed as a cryptographic hash of a serialized [`Command`].
    pub struct CommandId;
}

impl CommandId {
    /// Derives a [`CommandId`] from some data.
    ///
    /// This is for testing only. It's not `#[cfg(test)]` because
    /// (unfortunately) some code already depends on it.
    pub fn hash_for_testing_only(data: &[u8]) -> Self {
        use aranya_crypto::{hash::Hash, rust::Sha512};
        Sha512::hash(data).into_array().into()
    }

    pub fn short_b58(&self) -> String {
        #![allow(clippy::arithmetic_side_effects)]
        let b58 = self.to_string();
        let trimmed = b58.trim_start_matches('1');
        let len = trimmed.len();
        if len == 0 {
            "1".into()
        } else if len > 8 {
            format!("..{}", &trimmed[len - 6..])
        } else {
            trimmed.into()
        }
    }
}

/// Identify how the client will sort the associated [`Command`].
// Note: Order of variants affects derived Ord: Merge is least and Init is greatest.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Priority {
    /// Indicates two branches in the parent graph have been merged at this
    /// command. A command with this priority must have two parents,
    /// `Parent::Merge`.
    Merge,
    /// Indicates a user-specific action; the runtime uses the internal u32
    /// for ordering.
    Basic(u32),
    /// Indicates all preceding commands are ancestors of this command.
    Finalize,
    /// Indicates state is initialized; the associated command is a common
    /// ancestor to all other commands in the graph. A command with this
    /// priority must have no parents, `Parent::None`.
    Init,
}

/// An action message interpreted by its associated policy to affect state.
///
/// A [`Command`] is opaque to the runtime engine. When the engine receives a
/// message, it is validated and serialized by its policy. The policy
/// returns a command implementation to update the stored graph. A
/// policy will also emit effects once a command is verified,
/// which are sent to the client.
pub trait Command {
    /// Return this command's [`Priority`], determining how this event is
    /// ordered amongst others it does not have a causal relationship with.
    fn priority(&self) -> Priority;

    /// Uniquely identifies the serialized command.
    fn id(&self) -> CommandId;

    /// Return this command's parents, or address(s) that immediately
    /// precede(s) this.
    fn parent(&self) -> Prior<Address>;

    /// Return this command's associated policy.
    fn policy(&self) -> Option<&[u8]>;

    /// Return this command's serialized data.
    fn bytes(&self) -> &[u8];

    /// Return this command's max cut. Max cut is the maximum distance to the init command.
    fn max_cut(&self) -> Result<usize, Bug> {
        match self.parent() {
            Prior::None => Ok(0),
            Prior::Single(l) => Ok(l.max_cut.checked_add(1).assume("must not overflow")?),
            Prior::Merge(l, r) => Ok(l
                .max_cut
                .max(r.max_cut)
                .checked_add(1)
                .assume("must not overflow")?),
        }
    }

    /// Return this command's address.
    fn address(&self) -> Result<Address, Bug> {
        Ok(Address {
            id: self.id(),
            max_cut: self.max_cut()?,
        })
    }
}

impl<C: Command> Command for &C {
    fn priority(&self) -> Priority {
        (*self).priority()
    }

    fn id(&self) -> CommandId {
        (*self).id()
    }

    fn parent(&self) -> Prior<Address> {
        (*self).parent()
    }

    fn policy(&self) -> Option<&[u8]> {
        (*self).policy()
    }

    fn bytes(&self) -> &[u8] {
        (*self).bytes()
    }

    fn max_cut(&self) -> Result<usize, Bug> {
        (*self).max_cut()
    }

    fn address(&self) -> Result<Address, Bug> {
        (*self).address()
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, Ord, PartialEq, PartialOrd, Eq, Default)]
/// An address contains all of the information needed to find a command in
/// another graph.
///
/// The command id identifies the command you're searching for and the
/// max_cut allows that command to be found efficiently.
pub struct Address {
    pub id: CommandId,
    pub max_cut: usize,
}

impl Prior<Address> {
    /// Returns the max cut for the command that is after this prior.
    pub fn next_max_cut(&self) -> Result<usize, Bug> {
        Ok(match self {
            Prior::None => 1,
            Prior::Single(l) => l.max_cut.checked_add(1).assume("must not overflow")?,
            Prior::Merge(l, r) => l
                .max_cut
                .max(r.max_cut)
                .checked_add(1)
                .assume("must not overflow")?,
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn priority_ordering() {
        assert!(Priority::Merge < Priority::Basic(0));
        assert!(Priority::Basic(0) < Priority::Basic(1));
        assert!(Priority::Basic(1) < Priority::Basic(u32::MAX));
        assert!(Priority::Basic(u32::MAX) < Priority::Finalize);
        assert!(Priority::Finalize < Priority::Init);
    }
}