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
// SPDX-License-Identifier: AGPL-3.0-or-later
//! Create, encode and decode p2panda operations.
//!
//! Operations describe data mutations in the p2panda network. Authors send operations to create,
//! update or delete documents.
//!
//! Every operations contains application data which is formed after a schema. To be able to decode
//! an operation, a schema aids with getting the data out of the operation.
//!
//! ## Build or decode operation
//!
//! There are two approaches (similar to `Entry`) to create an `Operation`.
//!
//! To programmatically create an `Operation`, use the `OperationBuilder`. When working with
//! operations coming in as bytes, you can use the `decode_operation` method to first deserialize
//! it into a `PlainOperation` instance, which is a schemaless object giving you already access to
//! the "header" data, like the schema id.
//!
//! Knowing the schema id you can look up your internal database for known schemas and derive a
//! `Schema` instance from there. Now together with the `PlainOperation` and `Schema` you can
//! finally validate the operation (via `validate_operation`) to arrive at the final, verified
//! `Operation`.
//!
//! ```text
//! ┌────────────────┐
//! │OperationBuilder├──────────build()──────────────┐
//! └────────────────┘ │
//! │
//! │
//! ▼
//! ┌──────┐ ┌─────────┐
//! │Schema│ │Operation│
//! └──┬───┘ └─────────┘
//! │ ▲
//! Lookup Schema │ │
//! │ │
//! ┌──────────────┐ ▼ │
//! │PlainOperation├───────validate_operation()──────┘
//! └──────────────┘
//! ▲
//! │
//! decode_operation()
//! │
//! ┌───────┴────────┐
//! bytes ────► │EncodedOperation│
//! └────────────────┘
//! ```
//!
//! Please note that `Operation` in itself is immutable and can not directly be deserialized, there
//! are only these above mentioned approaches to arrive at it. Both approaches apply all means to
//! validate the integrity and correct encoding of the operation as per specification.
//!
//! ## Encoding
//!
//! `Operation` structs can be encoded again into their raw bytes form like that, for this no
//! `Schema` is required:
//!
//! ```text
//! ┌─────────┐ ┌────────────────┐
//! │Operation│ ───encode_operation()───► │EncodedOperation│ ────► bytes
//! └─────────┘ └────────────────┘
//! ```
//!
//! ## Validation
//!
//! The above mentioned high-level methods will automatically apply different sorts of validation
//! checks. All low-level methods can also be used independently, depending on your implementation:
//!
//! 1. Correct hexadecimal encoding (when using human-readable encoding format) (#OP1)
//! 2. Correct operation format as per specification, including canonic format checks against
//! duplicate and unsorted operation fields (#OP2)
//! 3. Correctly formatted and canonic operation field values, like document view ids (no
//! duplicates, sorted, when no semantic value is given by that) as per specification (#OP3)
//! 4. Operation fields match the claimed schema (#OP4)
//!
//! Both #OP2 and #OP3 check against the canonic format but in separate steps, this is required as
//! we can only check for the correct format of the operation field values, like document view ids
//! _after_ we obtained the schema, while we can already check the correct operation format
//! _before_.
//!
//! This module also provides a high-level method `validate_operation_with_entry` which will apply
//! _all_ checks required to verify the integrity of an operation and entry. This includes all
//! validation steps listed above plus the ones mentioned in the `entry` module. Since this
//! validation requires you to provide a `Schema` instance and the regarding back- & skiplink
//! `Entry` instances yourself, it needs some preparation from your end which can roughly be
//! described like this:
//!
//! ```text
//! Look-Up
//!
//! ┌────────────┐ ┌─────┐ ┌─────┐ ┌─────┐
//! bytes ───► │EncodedEntry├────decode_entry()────►│Entry│ │Entry│ │Entry│
//! └──────┬─────┘ └──┬──┘ └─────┘ └─────┘
//! │ │
//! └───────────────────────────┐ │ Skiplink Backlink
//! │ │ │ │
//! ┌────────────────┐ │ │ │ │
//! bytes ───► │EncodedOperation├─────────────┐ │ │ │ │
//! └───────┬────────┘ │ │ │ │ │
//! │ │ │ │ │ │
//! decode_operation() │ │ │ │ │
//! │ Look-Up │ │ │ │ │
//! ▼ │ │ │ │ │
//! ┌──────────────┐ ┌──────┐ │ │ │ │ │
//! │PlainOperation│ │Schema│ │ │ │ │ │
//! └──────┬───────┘ └──┬───┘ │ │ │ │ │
//! │ │ │ │ │ │ │
//! │ │ │ │ │ │ │
//! │ │ │ │ │ │ │
//! │ │ │ │ │ │ │
//! │ ▼ ▼ ▼ ▼ ▼ │
//! └───────────► validate_operation_with_entry() ◄──────┘
//! │
//! │
//! │
//! │
//! ▼
//! ┌────────────────────────┐
//! │(Operation, OperationId)│
//! └────────────────────────┘
//! ```
pub use EncodedOperation;
pub use ;
pub use OperationAction;
pub use OperationFields;
pub use OperationId;
pub use OperationValue;
pub use OperationVersion;
pub use ;