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
use crate::timestamp::Timestamp;
pub use builder::ActionBuilder;
pub use builder::ActionBuilderCommon;
use conversions::WrongActionError;
use holo_hash::ActionHash;
use holochain_serialized_bytes::prelude::*;
use thiserror::Error;

pub use holochain_integrity_types::action::*;

#[cfg(any(test, feature = "test_utils"))]
pub use facts::*;

pub mod builder;
#[cfg(any(test, feature = "test_utils"))]
pub mod facts;

#[derive(Error, Debug)]
pub enum ActionError {
    #[error("Tried to create a NewEntryAction with a type that isn't a Create or Update")]
    NotNewEntry,
    #[error(transparent)]
    WrongActionError(#[from] WrongActionError),
    #[error("{0}")]
    Rebase(String),
}

#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ChainTopOrdering {
    /// Relaxed chain top ordering REWRITES ACTIONS INLINE during a flush of
    /// the source chain to sit on top of the current chain top. The "as at"
    /// of the zome call initial state is completely ignored.
    /// This may be significantly more efficient if you are CERTAIN that none
    /// of your zome or validation logic is order dependent. Examples include
    /// simple chat messages or tweets. Note however that even chat messages
    /// and tweets may have subtle order dependencies, such as if a cap grant
    /// was written or revoked that would have invalidated the zome call that
    /// wrote data after the revocation, etc.
    /// The efficiency of relaxed ordering comes from simply rehashing and
    /// signing actions on the new chain top during flush, avoiding the
    /// overhead of the client, websockets, zome call instance, wasm execution,
    /// validation, etc. that would result from handling a `HeadMoved` error
    /// via an external driver.
    Relaxed,
    /// The default `Strict` ordering is the default for a very good reason.
    /// Writes normally compare the chain head from the start of a zome call
    /// against the time a write transaction is flushed from the source chain.
    /// This is REQUIRED for data integrity if any zome or validation logic
    /// depends on the ordering of data in a chain.
    /// This order dependence could be obvious such as an explicit reference or
    /// dependency. It could be very subtle such as checking for the existence
    /// or absence of some data.
    /// If you are unsure whether your data is order dependent you should err
    /// on the side of caution and handle `HeadMoved` errors on the client of
    /// the zome call and restart the zome call from the start.
    Strict,
}

impl Default for ChainTopOrdering {
    fn default() -> Self {
        Self::Strict
    }
}

pub trait ActionExt {
    fn rebase_on(
        &mut self,
        new_prev_action: ActionHash,
        new_prev_seq: u32,
        new_prev_timestamp: Timestamp,
    ) -> Result<(), ActionError>;
}

impl ActionExt for Action {
    fn rebase_on(
        &mut self,
        new_prev_action: ActionHash,
        new_prev_seq: u32,
        new_prev_timestamp: Timestamp,
    ) -> Result<(), ActionError> {
        let new_seq = new_prev_seq + 1;
        let new_timestamp = self.timestamp().max(
            (new_prev_timestamp + std::time::Duration::from_nanos(1))
                .map_err(|e| ActionError::Rebase(e.to_string()))?,
        );
        match self {
            Self::Dna(_) => return Err(ActionError::Rebase("Rebased a DNA Action".to_string())),
            Self::AgentValidationPkg(AgentValidationPkg {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::InitZomesComplete(InitZomesComplete {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::CreateLink(CreateLink {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::DeleteLink(DeleteLink {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::Delete(Delete {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::CloseChain(CloseChain {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::OpenChain(OpenChain {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::Create(Create {
                timestamp,
                action_seq,
                prev_action,
                ..
            })
            | Self::Update(Update {
                timestamp,
                action_seq,
                prev_action,
                ..
            }) => {
                *timestamp = new_timestamp;
                *action_seq = new_seq;
                *prev_action = new_prev_action;
            }
        };
        Ok(())
    }
}