1use core::fmt;
8
9use crate::crypto::{key_id, SecretKey};
10use crate::error::Result;
11use crate::json::Value;
12use crate::lexical::to_hex;
13use crate::model::{args_to_value, Args, Cell, Scalar};
14use crate::receipt::Receipt;
15
16#[derive(Clone)]
20pub struct SigningKey {
21 secret: SecretKey,
22 key_id: String,
23}
24
25impl SigningKey {
26 pub fn from_seed(seed: &[u8; 32]) -> SigningKey {
28 let secret = SecretKey::from_seed(seed);
29 let key_id = key_id(&secret.public);
30 SigningKey { secret, key_id }
31 }
32
33 pub fn public_key(&self) -> [u8; 32] {
35 self.secret.public
36 }
37
38 pub fn key_id(&self) -> &str {
40 &self.key_id
41 }
42}
43
44impl fmt::Debug for SigningKey {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 f.debug_struct("SigningKey").field("key_id", &self.key_id).finish_non_exhaustive()
47 }
48}
49
50#[derive(Clone, Debug, Default)]
62pub struct ReceiptBuilder {
63 cell: Option<Cell>,
64 value: Option<Scalar>,
65 valid: Option<Args>,
66 asof: Option<String>,
67 tx: Option<String>,
68 issued: Option<String>,
69 publisher: Option<String>,
70 signer_host: Option<String>,
71 inputs: Vec<String>,
72 licence: Option<String>,
73 summary: Option<String>,
74}
75
76impl ReceiptBuilder {
77 pub fn new() -> ReceiptBuilder {
79 ReceiptBuilder::default()
80 }
81
82 pub fn cell(mut self, cell: Cell) -> Self {
84 self.cell = Some(cell);
85 self
86 }
87
88 pub fn value(mut self, value: Scalar) -> Self {
90 self.value = Some(value);
91 self
92 }
93
94 pub fn valid(mut self, valid: Args) -> Self {
96 self.valid = Some(valid);
97 self
98 }
99
100 pub fn asof(mut self, asof: impl Into<String>) -> Self {
102 self.asof = Some(asof.into());
103 self
104 }
105
106 pub fn tx(mut self, tx: impl Into<String>) -> Self {
108 self.tx = Some(tx.into());
109 self
110 }
111
112 pub fn issued(mut self, issued: impl Into<String>) -> Self {
114 self.issued = Some(issued.into());
115 self
116 }
117
118 pub fn publisher(mut self, publisher: impl Into<String>) -> Self {
120 self.publisher = Some(publisher.into());
121 self
122 }
123
124 pub fn signer_host(mut self, host: impl Into<String>) -> Self {
126 self.signer_host = Some(host.into());
127 self
128 }
129
130 pub fn inputs<I, S>(mut self, inputs: I) -> Self
133 where
134 I: IntoIterator<Item = S>,
135 S: Into<String>,
136 {
137 self.inputs = inputs.into_iter().map(Into::into).collect();
138 self
139 }
140
141 pub fn licence(mut self, licence: impl Into<String>) -> Self {
143 self.licence = Some(licence.into());
144 self
145 }
146
147 pub fn summary(mut self, summary: impl Into<String>) -> Self {
149 self.summary = Some(summary.into());
150 self
151 }
152
153 pub fn sign(&self, key: &SigningKey) -> Result<Receipt> {
159 let s = |x: &Option<String>| x.as_ref().map(|t| Value::String(t.clone()));
160 let computed_by = self.signer_host.as_ref().map(|h| Value::String(format!("{h}#{}", key.key_id())));
161 let (ty, value) = match &self.value {
162 Some(v) => {
163 let raw = match v {
164 Scalar::Boolean(b) => Value::Bool(*b),
165 other => Value::String(other.lexical().to_string()),
166 };
167 (Some(Value::String(v.ty().as_str().into())), Some(raw))
168 }
169 None => (None, None),
170 };
171 let placeholder = "0".repeat(128);
173 let members: Vec<(&str, Option<Value>)> = vec![
174 ("asof", s(&self.asof)),
175 ("cell", self.cell.as_ref().map(Cell::to_value)),
176 ("computed_by", computed_by),
177 ("inputs", Some(Value::Array(self.inputs.iter().map(|h| Value::String(h.clone())).collect()))),
178 ("issued", s(&self.issued)),
179 ("licence", s(&self.licence)),
180 ("proto", Some(Value::String("datumesh/1".into()))),
181 ("publisher", s(&self.publisher)),
182 ("sig", Some(Value::String(placeholder))),
183 ("summary", s(&self.summary)),
184 ("tx", s(&self.tx)),
185 ("type", ty),
186 ("valid", self.valid.as_ref().map(args_to_value)),
187 ("value", value),
188 ];
189 let object = members.into_iter().filter_map(|(k, v)| v.map(|v| (k.to_string(), v))).collect();
190 let unsigned = Receipt::from_value(&Value::Object(object))?;
191 let sig = key.secret.sign(unsigned.payload());
192 Ok(unsigned.with_sig(to_hex(&sig)))
193 }
194}