1use bytes::Bytes;
2use exocore_core::{
3 cell::{LocalNode, NodeId},
4 framing::{
5 CapnpFrameBuilder, FrameBuilder, FrameReader, MultihashFrame, MultihashFrameBuilder,
6 SizedFrame, SizedFrameBuilder, TypedCapnpFrame,
7 },
8 sec::{hash::Sha3_256, signature::Signature},
9};
10use exocore_protos::{
11 capnp,
12 generated::data_chain_capnp::{block_signature, chain_operation},
13};
14
15use crate::block::Block;
16
17pub type GroupId = u64;
18pub type OperationId = u64;
19
20pub type OperationFrame<I> =
21 TypedCapnpFrame<MultihashFrame<32, Sha3_256, SizedFrame<I>>, chain_operation::Owned>;
22
23pub type OperationFrameBuilder = SizedFrameBuilder<
24 MultihashFrameBuilder<32, Sha3_256, CapnpFrameBuilder<chain_operation::Owned>>,
25>;
26
27pub trait Operation {
30 fn get_operation_reader(&self) -> Result<chain_operation::Reader, Error>;
31
32 fn as_entry_data(&self) -> Result<&[u8], Error> {
33 let frame_reader = self.get_operation_reader()?;
34 match frame_reader.get_operation().which()? {
35 chain_operation::operation::Entry(entry) => Ok(entry?.get_data()?),
36 _ => Err(Error::NotAnEntry),
37 }
38 }
39
40 fn get_type(&self) -> Result<OperationType, Error> {
41 let operation_reader = self.get_operation_reader()?;
42 Ok(match operation_reader.get_operation().which()? {
43 chain_operation::operation::Which::BlockSign(_) => OperationType::BlockSign,
44 chain_operation::operation::Which::BlockPropose(_) => OperationType::BlockPropose,
45 chain_operation::operation::Which::BlockRefuse(_) => OperationType::BlockRefuse,
46 chain_operation::operation::Which::Entry(_) => OperationType::Entry,
47 })
48 }
49
50 fn get_id(&self) -> Result<OperationId, Error> {
51 let operation_reader = self.get_operation_reader()?;
52 Ok(operation_reader.get_operation_id())
53 }
54
55 fn get_group_id(&self) -> Result<OperationId, Error> {
56 let operation_reader = self.get_operation_reader()?;
57 Ok(operation_reader.get_group_id())
58 }
59}
60
61#[derive(Copy, Clone, Debug, PartialEq, Eq)]
63pub enum OperationType {
64 Entry,
65 BlockPropose,
66 BlockSign,
67 BlockRefuse,
68}
69
70pub struct OperationBuilder {
72 pub operation_id: OperationId,
73 pub frame_builder: CapnpFrameBuilder<chain_operation::Owned>,
74}
75
76impl OperationBuilder {
77 pub fn new_entry(operation_id: OperationId, node_id: &NodeId, data: &[u8]) -> OperationBuilder {
78 let mut frame_builder = CapnpFrameBuilder::new();
79
80 let mut operation_builder: chain_operation::Builder = frame_builder.get_builder();
81 operation_builder.set_operation_id(operation_id);
82 operation_builder.set_group_id(operation_id);
83 operation_builder.set_node_id(node_id.to_string().as_str());
84
85 let inner_operation_builder = operation_builder.init_operation();
86
87 let mut new_entry_builder = inner_operation_builder.init_entry();
88 new_entry_builder.set_data(data);
89
90 OperationBuilder {
91 operation_id,
92 frame_builder,
93 }
94 }
95
96 pub fn new_block_proposal<B: Block>(
97 operation_id: OperationId,
98 node_id: &NodeId,
99 block: &B,
100 ) -> Result<OperationBuilder, Error> {
101 Self::new_block_proposal_from_data(operation_id, node_id, &block.as_data_vec())
102 }
103
104 pub fn new_block_proposal_from_data(
105 operation_id: OperationId,
106 node_id: &NodeId,
107 data: &[u8],
108 ) -> Result<OperationBuilder, Error> {
109 let mut frame_builder = CapnpFrameBuilder::new();
110
111 let mut operation_builder: chain_operation::Builder = frame_builder.get_builder();
112 operation_builder.set_operation_id(operation_id);
113 operation_builder.set_group_id(operation_id);
114 operation_builder.set_node_id(node_id.to_string().as_str());
115
116 let inner_operation_builder = operation_builder.init_operation();
117 let mut new_block_builder = inner_operation_builder.init_block_propose();
118 new_block_builder.set_block(data);
119
120 Ok(OperationBuilder {
121 operation_id,
122 frame_builder,
123 })
124 }
125
126 pub fn new_signature_for_block<I: FrameReader>(
127 group_id: OperationId,
128 operation_id: OperationId,
129 node_id: &NodeId,
130 _header: &crate::block::BlockHeaderFrame<I>,
131 ) -> Result<OperationBuilder, Error> {
132 let mut frame_builder = CapnpFrameBuilder::new();
133
134 let mut operation_builder: chain_operation::Builder = frame_builder.get_builder();
135 operation_builder.set_operation_id(operation_id);
136 operation_builder.set_group_id(group_id);
137 operation_builder.set_node_id(node_id.to_string().as_str());
138
139 let inner_operation_builder = operation_builder.init_operation();
140 let new_sig_builder = inner_operation_builder.init_block_sign();
141
142 let signature = Signature::empty();
145
146 let mut sig_builder: block_signature::Builder = new_sig_builder.init_signature();
147 sig_builder.set_node_id(node_id.to_string().as_str());
148 sig_builder.set_node_signature(signature.get_bytes());
149
150 Ok(OperationBuilder {
151 operation_id,
152 frame_builder,
153 })
154 }
155
156 pub fn new_refusal(
157 group_id: OperationId,
158 operation_id: OperationId,
159 node_id: &NodeId,
160 ) -> Result<OperationBuilder, Error> {
161 let mut frame_builder = CapnpFrameBuilder::new();
162
163 let mut operation_builder: chain_operation::Builder = frame_builder.get_builder();
164 operation_builder.set_operation_id(operation_id);
165 operation_builder.set_group_id(group_id);
166 operation_builder.set_node_id(node_id.to_string().as_str());
167
168 let inner_operation_builder = operation_builder.init_operation();
169 let _new_refuse_builder = inner_operation_builder.init_block_refuse();
170
171 Ok(OperationBuilder {
172 operation_id,
173 frame_builder,
174 })
175 }
176
177 pub fn sign_and_build(self, _local_node: &LocalNode) -> Result<NewOperation, Error> {
178 let msg_frame = self.frame_builder.as_bytes();
181 let signed_frame_builder = MultihashFrameBuilder::<32, Sha3_256, _>::new(msg_frame);
182 let sized_frame_builder = SizedFrameBuilder::new(signed_frame_builder);
183 let final_frame = read_operation_frame(sized_frame_builder.as_bytes())?;
184
185 Ok(NewOperation::from_frame(self.operation_id, final_frame))
186 }
187}
188
189pub fn read_operation_frame<I: FrameReader>(inner: I) -> Result<OperationFrame<I>, Error> {
190 let sized_frame = SizedFrame::new(inner)?;
191 let multihash_frame = MultihashFrame::<32, Sha3_256, _>::new(sized_frame)?;
192 let frame = TypedCapnpFrame::new(multihash_frame)?;
193 Ok(frame)
194}
195
196#[derive(Clone)]
198pub struct NewOperation {
199 pub operation_id: OperationId,
200 pub frame: OperationFrame<Bytes>,
201}
202
203impl NewOperation {
204 pub fn from_frame(operation_id: OperationId, frame: OperationFrame<Bytes>) -> NewOperation {
205 NewOperation {
206 operation_id,
207 frame,
208 }
209 }
210}
211
212impl crate::operation::Operation for NewOperation {
213 fn get_operation_reader(&self) -> Result<chain_operation::Reader, Error> {
214 Ok(self.frame.get_reader()?)
215 }
216}
217
218#[derive(Debug, thiserror::Error)]
220pub enum Error {
221 #[error("The operation is not any entry operation")]
222 NotAnEntry,
223
224 #[error("Framing error: {0}")]
225 Framing(#[from] exocore_core::framing::Error),
226
227 #[error("Error in capnp serialization: {0}")]
228 Serialization(#[from] capnp::Error),
229
230 #[error("Field is not in capnp schema: code={0}")]
231 SerializationNotInSchema(u16),
232
233 #[error("Other operation error: {0}")]
234 Other(String),
235}
236
237impl From<capnp::NotInSchema> for Error {
238 fn from(err: capnp::NotInSchema) -> Self {
239 Error::SerializationNotInSchema(err.0)
240 }
241}