p2panda_rs/operation/
encode.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3//! Methods to encode operations.
4//!
5//! Encoding an operation does not require a schema, the `Operation` will be serialized into bytes,
6//! represented as a `EncodedOperation` which is then ready to be sent to a p2panda node (alongside
7//! an entry).
8//!
9//! ```text
10//! ┌─────────┐                           ┌────────────────┐
11//! │Operation│ ───encode_operation()───► │EncodedOperation│ ────► bytes
12//! └─────────┘                           └────────────────┘
13//! ```
14use crate::operation::error::EncodeOperationError;
15use crate::operation::plain::PlainOperation;
16use crate::operation::{EncodedOperation, Operation};
17
18/// Encodes an operation in canonic format.
19pub fn encode_operation(operation: &Operation) -> Result<EncodedOperation, EncodeOperationError> {
20    // Convert to plain operation format
21    let plain: PlainOperation = operation.into();
22
23    // Encode as CBOR byte sequence
24    let encoded_operation = encode_plain_operation(&plain)?;
25
26    Ok(encoded_operation)
27}
28
29/// Encodes a `PlainOperation` instance in canonic format.
30pub fn encode_plain_operation(
31    plain: &PlainOperation,
32) -> Result<EncodedOperation, EncodeOperationError> {
33    let mut cbor_bytes = Vec::new();
34
35    ciborium::ser::into_writer(&plain, &mut cbor_bytes).map_err(|err| match err {
36        ciborium::ser::Error::Io(err) => EncodeOperationError::EncoderIOFailed(err.to_string()),
37        ciborium::ser::Error::Value(err) => EncodeOperationError::EncoderFailed(err),
38    })?;
39
40    Ok(EncodedOperation::from_bytes(&cbor_bytes))
41}
42
43#[cfg(test)]
44mod tests {
45    use ciborium::cbor;
46    use rstest::rstest;
47    use serde_bytes::ByteBuf;
48
49    use crate::operation::Operation;
50    use crate::serde::serialize_value;
51    use crate::test_utils::fixtures::operation_with_schema;
52    use crate::{operation::plain::PlainOperation, serde::hex_string_to_bytes};
53
54    use super::{encode_operation, encode_plain_operation};
55
56    #[rstest]
57    fn encoding(#[from(operation_with_schema)] operation: Operation) {
58        let plain_operation = PlainOperation::from(&operation);
59
60        // Test both methods to encode operations and compare
61        let from_operation = encode_operation(&operation).unwrap();
62        let from_plain_operation = encode_plain_operation(&plain_operation).unwrap();
63
64        assert_eq!(from_operation.to_string(), from_plain_operation.to_string());
65        let operation_bytes = serialize_value(cbor!(
66            [
67                1,
68                0,
69                "venue_0020c65567ae37efea293e34a9c7d13f8f2bf23dbdc3b5c7b9ab46293111c48fc78b",
70                {
71                    "age" => 28,
72                    "comments" => [
73                        [
74                            hex_string_to_bytes("00204f0dd3a1b8205b6d4ce3fd4c158bb91c9e131bd842e727164ea220b5b6d09346")
75                        ],
76                        [
77                            hex_string_to_bytes("002019ed3e9b39cd17f1dbc0f6e31a6e7b9c9ab7e349332e710c946a441b7d308eb5"),
78                            hex_string_to_bytes("0020995d53f460293c5686c42037b72787ed28668ad8b6d18e9d5f02c5d3301161f0")
79                        ]
80                    ],
81                    "data" => ByteBuf::from([0, 1, 2, 3]),
82                    "height" => 3.5,
83                    "is_admin" => false,
84                    "my_friends" => [
85                        hex_string_to_bytes("00209a2149589672fa1ac2348e48b4c56fc208a0eff44938464dd2091850f444a323")
86                    ],
87                    "past_event" => [
88                        hex_string_to_bytes("0020475488c0e2bbb9f5a81929e2fe11de81c1f83c8045de41da43899d25ad0d4afa"),
89                        hex_string_to_bytes("0020f7a17e14b9a5e87435decdbc28d562662fbf37da39b94e8469d8e1873336e80e")
90                    ],
91                    "profile_picture" => hex_string_to_bytes("0020b177ec1bf26dfb3b7010d473e6d44713b29b765b99c6e60ecbfae742de496543"),
92                    "username" => "bubu"
93                }
94        ]));
95        assert_eq!(from_operation.into_bytes(), operation_bytes);
96    }
97}