p2panda_core/operation/
builder.rs1use std::marker::PhantomData;
4
5use cbor_core::Value;
6
7use crate::hash::Hash;
8use crate::identity::SigningKey;
9use crate::logs::SeqNum;
10use crate::operation::header::encode_header;
11use crate::operation::{Header, PayloadSize};
12use crate::traits::Extensions;
13
14pub struct Builder<E> {
16 payload_size: PayloadSize,
17 payload_hash: Option<Hash>,
18 seq_num: SeqNum,
19 backlink: Option<Hash>,
20 _marker: PhantomData<E>,
21}
22
23impl<E> Default for Builder<E>
24where
25 E: Extensions,
26{
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl<E> Builder<E>
33where
34 E: Extensions,
35{
36 pub fn new() -> Self {
37 Self {
38 payload_size: 0,
39 payload_hash: None,
40 seq_num: 0,
41 backlink: None,
42 _marker: PhantomData,
43 }
44 }
45
46 pub fn body(mut self, bytes: impl AsRef<[u8]>) -> Self {
48 let bytes = bytes.as_ref();
49
50 self.payload_size = bytes.len() as PayloadSize;
51 self.payload_hash = if self.payload_size == 0 {
52 None
53 } else {
54 Some(Hash::digest(bytes))
55 };
56
57 self
58 }
59
60 pub fn chain(mut self, seq_num: SeqNum, backlink: Hash) -> Self {
62 self.seq_num = seq_num;
63
64 if self.seq_num > 0 {
65 self.backlink = Some(backlink);
66 } else {
67 self.backlink = None;
69 }
70
71 self
72 }
73
74 pub fn seq_num(mut self, seq_num: SeqNum) -> Self {
77 self.seq_num = seq_num;
78 self
79 }
80
81 pub fn backlink(mut self, backlink: Option<Hash>) -> Self {
84 self.backlink = backlink;
85 self
86 }
87
88 pub fn build(self, signing_key: &SigningKey, extensions: E) -> Header<E> {
93 let version = 1;
94
95 let verifying_key = signing_key.verifying_key();
96
97 let extensions_cbor = if !Header::<E>::has_zero_sized_extensions() {
98 let extensions_cbor = Value::serialized(&extensions).expect("serializable extensions");
99 Some(extensions_cbor)
100 } else {
101 None
102 };
103
104 let signing_bytes = encode_header(
105 version,
106 verifying_key,
107 None,
108 self.payload_size,
109 self.payload_hash,
110 self.seq_num,
111 self.backlink,
112 extensions_cbor.as_ref(),
113 );
114
115 let signature = signing_key.sign(&signing_bytes);
116
117 let bytes = encode_header(
118 version,
119 verifying_key,
120 Some(&signature),
121 self.payload_size,
122 self.payload_hash,
123 self.seq_num,
124 self.backlink,
125 extensions_cbor.as_ref(),
126 );
127
128 let digest = Hash::digest(&bytes);
129 let size = bytes.len() as u32;
130
131 Header {
132 version,
133 verifying_key,
134 signature,
135 payload_size: self.payload_size,
136 payload_hash: self.payload_hash,
137 seq_num: self.seq_num,
138 backlink: self.backlink,
139 extensions,
140 extensions_cbor,
141 digest,
142 size,
143 }
144 }
145}