cardano_sdk/builder/tx.rs
1use crate::{chain::*, crypto::key::SignatureVerification};
2use cryptoxide::hashing::blake2b_256;
3use std::sync::Arc;
4
5/// The static parts of building a transaction
6#[derive(Clone, Debug, Default, PartialEq, Eq)]
7pub struct TransactionSettings {
8 pub ttl: Option<u64>,
9 pub certs: Option<Certificates>,
10 pub withdrawals: Option<Withdrawals>,
11 // TODO pub update: Option<AnyCbor>,
12 pub metadata: Option<Metadata>,
13 pub validity_start_interval: Option<u64>,
14 pub mint: Option<Mint>,
15 pub script_data_hash: Option<ScriptDataHash>,
16 pub collateral: Option<TransactionInputs>,
17 pub required_signers: Option<Ed25519KeyHashes>,
18 pub network_id: Option<u8>,
19 pub collateral_return: Option<TransactionOutput>,
20 pub total_collateral: Option<Coin>,
21 pub reference_inputs: Option<TransactionInputs>,
22}
23
24impl TransactionSettings {
25 pub fn build() -> TransactionSettingsBuilder {
26 TransactionSettingsBuilder(Default::default())
27 }
28}
29
30pub struct TransactionSettingsBuilder(TransactionSettings);
31
32macro_rules! builder_field_opt {
33 ($obj:ident, $field:ident, $ty:ident, $set:ident, $clear:ident, $get:ident, ) => {
34 impl $obj {
35 pub fn $set(mut self, value: $ty) -> Self {
36 self.0.$field = Some(value);
37 self
38 }
39
40 pub fn $clear(mut self) -> Self {
41 self.0.$field = None;
42 self
43 }
44
45 pub fn $get(&self) -> &Option<$ty> {
46 &self.0.$field
47 }
48 }
49 };
50}
51
52impl TransactionSettingsBuilder {
53 pub fn finalize(self) -> TransactionSettings {
54 self.0
55 }
56}
57
58builder_field_opt!(
59 TransactionSettingsBuilder,
60 ttl,
61 u64,
62 ttl,
63 ttl_clear,
64 ttl_get,
65);
66builder_field_opt!(
67 TransactionSettingsBuilder,
68 mint,
69 Mint,
70 mint,
71 mint_clear,
72 mint_get,
73);
74builder_field_opt!(
75 TransactionSettingsBuilder,
76 metadata,
77 Metadata,
78 metadata,
79 metadata_clear,
80 metadata_get,
81);
82builder_field_opt!(
83 TransactionSettingsBuilder,
84 network_id,
85 u8,
86 network,
87 network_clear,
88 network_get,
89);
90builder_field_opt!(
91 TransactionSettingsBuilder,
92 validity_start_interval,
93 u64,
94 validity_start_interval,
95 validity_start_interval_clear,
96 validity_start_interval_get,
97);
98
99pub struct TransactionBuilder {
100 inputs: Vec<TransactionInput>,
101 outputs: Vec<TransactionOutput>,
102 fee: Coin,
103 settings: Arc<TransactionSettings>,
104}
105
106pub struct TransactionSigner {
107 builder: TransactionBuilder,
108 pub tx_body_hash: TxHash,
109 key_witnesses: Vec<VkeyWitness>,
110 native_scripts: Vec<NativeScript>,
111}
112
113macro_rules! builder_field_copy {
114 ($obj:ident, $field:ident, $ty:ident, $set:ident, $get:ident, ) => {
115 impl $obj {
116 pub fn $set(mut self, value: $ty) -> Self {
117 self.$field = value;
118 self
119 }
120
121 pub fn $get(&self) -> $ty {
122 self.$field
123 }
124 }
125 };
126}
127
128macro_rules! builder_field_vec {
129 ($obj:ident, $field:ident, $ty:ident, $add:ident, $remove_at:ident, ) => {
130 impl $obj {
131 pub fn $add(mut self, value: $ty) -> Self {
132 self.$field.push(value);
133 self
134 }
135
136 pub fn $remove_at(mut self, index: usize) -> Self {
137 self.$field.remove(index);
138 self
139 }
140 }
141 };
142}
143
144builder_field_vec!(
145 TransactionBuilder,
146 inputs,
147 TransactionInput,
148 inputs_add,
149 inputs_remove_at,
150);
151
152builder_field_vec!(
153 TransactionBuilder,
154 outputs,
155 TransactionOutput,
156 outputs_add,
157 outputs_remove_at,
158);
159
160builder_field_copy!(TransactionBuilder, fee, Coin, fee, fee_get,);
161
162impl TransactionBuilder {
163 pub fn new(settings: TransactionSettings) -> Self {
164 Self {
165 inputs: Vec::new(),
166 outputs: Vec::new(),
167 fee: Coin::ZERO,
168 settings: Arc::new(settings),
169 }
170 }
171
172 pub fn body(&self) -> TransactionBody {
173 TransactionBody {
174 inputs: TransactionInputs::from(self.inputs.clone()),
175 outputs: TransactionOutputs::from(self.outputs.clone()),
176 fee: self.fee,
177 ttl: self.settings.ttl,
178 certs: self.settings.certs.clone(),
179 withdrawals: self.settings.withdrawals.clone(),
180 update: None,
181 metadata_hash: self.settings.metadata.as_ref().map(|m| m.hash()),
182 validity_start_interval: self.settings.validity_start_interval.clone(),
183 mint: self.settings.mint.clone(),
184 script_data_hash: self.settings.script_data_hash.clone(),
185 collateral: self.settings.collateral.clone(),
186 required_signers: self.settings.required_signers.clone(),
187 network_id: self.settings.network_id,
188 collateral_return: self.settings.collateral_return.clone(),
189 total_collateral: self.settings.total_collateral.clone(),
190 reference_inputs: self.settings.reference_inputs.clone(),
191 }
192 }
193
194 pub fn signer(self) -> TransactionSigner {
195 TransactionSigner::new(self)
196 }
197}
198
199impl TransactionSigner {
200 pub fn new(builder: TransactionBuilder) -> Self {
201 let tx_body_hash = builder.body().hash();
202 Self {
203 builder,
204 tx_body_hash,
205 key_witnesses: Vec::new(),
206 native_scripts: Vec::new(),
207 }
208 }
209
210 pub fn add_signature(mut self, witness: VkeyWitness) -> Self {
211 self.key_witnesses.push(witness);
212 self
213 }
214
215 pub fn add_native_script(mut self, native_script: NativeScript) -> Self {
216 self.native_scripts.push(native_script);
217 self
218 }
219
220 pub fn finalize(self) -> Transaction {
221 let vkeys = if self.key_witnesses.len() > 0 {
222 Some(VkeyWitnesses::from(self.key_witnesses))
223 } else {
224 None
225 };
226 let native_scripts = if self.native_scripts.len() > 0 {
227 Some(NativeScripts::from(self.native_scripts))
228 } else {
229 None
230 };
231 Transaction {
232 body: self.builder.body().serialize(),
233 witness: TransactionWitness {
234 vkeys,
235 native_scripts,
236 bootstraps: None,
237 plutus_scripts: None,
238 plutus_scripts_v2: None,
239 plutus_data: None,
240 redeemers: None,
241 },
242 is_valid: true,
243 auxiliary_data: Nullable::from(self.builder.settings.metadata.clone()),
244 }
245 }
246}
247
248impl Transaction {
249 pub fn hash(&self) -> TxHash {
250 TxHash(blake2b_256(&self.to_bytes()))
251 }
252
253 pub fn to_bytes(&self) -> Vec<u8> {
254 let mut w = cbored::Writer::new();
255 w.encode(self);
256 w.finalize()
257 }
258
259 // only able to verify vkeys
260 pub fn verify_vkeys(&self) -> SignatureVerification {
261 let tx_body_hash = self.body.hash();
262 match &self.witness.vkeys {
263 None => SignatureVerification::Failed,
264 Some(vkeywitnesses) => {
265 for vkeywitness in vkeywitnesses.iter() {
266 let VkeyWitness { vkey, signature } = &vkeywitness;
267 println!(
268 "verify vkey {} : tx-body-hash= {}, sig: {}",
269 vkey, tx_body_hash, signature
270 );
271 let mut writer = cbored::Writer::new();
272 writer.bytes(&cbored::Bytes::from_slice(&tx_body_hash.0));
273 let x = writer.finalize();
274
275 //match vkey.verify(signature, &tx_body_hash.0) {
276 match vkey.verify(signature, &x) {
277 SignatureVerification::Failed => return SignatureVerification::Failed,
278 SignatureVerification::Passed => {}
279 }
280 }
281 return SignatureVerification::Passed;
282 }
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 /*
290 use super::*;
291 use crate::crypto::key::Ed25519Key;
292 use cbored::Decode;
293 use cryptoxide::hashing::blake2b_256;
294 */
295
296 // test vectors from : https://gist.github.com/KtorZ/5a2089df0915f21aca368d12545ab230
297 /*
298 - inputs:
299 - id: 3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7
300 index: 42
301 input_prvkey: c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a
302 input_pubkey: f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee
303 - id: 82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32
304 index: 7
305 input_prvkey: 13fe79205e16c09536acb6f0524d04069f380329d13949698c5f22c65c989eb4
306 input_pubkey: 6872b0a874acfe1cace12b20ea348559a7ecc912f2fc7f674f43481df973d92c
307 outputs:
308 - address: 611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c
309 coin: 289
310 - address: 61bcd18fcffa797c16c007014e2b8553b8b9b1e94c507688726243d611
311 coin: 874551452
312 certificates: []
313 metadata: null
314 fee: 1478714
315 ttl: 999
316 network: 1
317 result:
318 83a400828258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a
319 1939b3aad1c0b7182a82582082839f8200d81858248258203b40265111d8bb3c
320 3c608d95b3a0bf83461ace3207018282581d611c616f1acb460668a9b2f123c8
321 0372c2adad3583b9c6cd2b1deeed1c19012182581d61bcd18fcffa797c16c007
322 014e2b8553b8b9b1e94c507688726243d6111a3420989c021a0016903a031903
323 e7a10082825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185
324 962484a4813bee5840938cd3feb731fe6749c9b2b127d3958821fc7649b337f4
325 aaae105bf51ad75906aec0112fb40ecc2c2e7c00592a1ec5ac59ddf31ad60488
326 6364f78dbf6da8fe0c8258206872b0a874acfe1cace12b20ea348559a7ecc912
327 f2fc7f674f43481df973d92c58402a442facbde5b67d7d15581e1b59ee44b375
328 0bd018f01a2fecb4bc82b558c90f946afcb9f03f18e7ffb40bd59c4793871b92
329 4ab707cffdfda10dd5595e29e709f6
330 */
331
332 /*
333 #[test]
334 fn vec2() {
335 let result = hex::decode("83a400828258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7182a82582082839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace3207018282581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c19012182581d61bcd18fcffa797c16c007014e2b8553b8b9b1e94c507688726243d6111a3420989c021a0016903a031903e7a10082825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee5840938cd3feb731fe6749c9b2b127d3958821fc7649b337f4aaae105bf51ad75906aec0112fb40ecc2c2e7c00592a1ec5ac59ddf31ad604886364f78dbf6da8fe0c8258206872b0a874acfe1cace12b20ea348559a7ecc912f2fc7f674f43481df973d92c58402a442facbde5b67d7d15581e1b59ee44b3750bd018f01a2fecb4bc82b558c90f946afcb9f03f18e7ffb40bd59c4793871b924ab707cffdfda10dd5595e29e709f6").unwrap();
336 let mut reader = cbored::Reader::new(&result);
337 let tx_expected = Transaction::decode(&mut reader).unwrap();
338 assert!(reader.is_finished());
339
340 let txbody_slice = {
341 let mut reader2 = cbored::Reader::new(&result);
342 let array = reader2.array().unwrap();
343 array[0]
344 };
345 let txbody_hash_from_slice = blake2b_256(txbody_slice.as_ref());
346
347 // check this the transaction re-encode to the byte stream
348 assert_eq!(tx_expected.to_bytes(), result);
349 assert_eq!(tx_expected.body.as_ref(), txbody_slice.as_ref());
350 println!("{:?}", tx_expected);
351
352 // check that the vkeys witnesses verification work
353 assert_eq!(tx_expected.verify_vkeys(), SignatureVerification::Passed);
354
355 let privkey1_data =
356 hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
357 .unwrap();
358 let privkey2_data =
359 hex::decode("13fe79205e16c09536acb6f0524d04069f380329d13949698c5f22c65c989eb4")
360 .unwrap();
361
362 let privkey1 = Ed25519Key::from_bytes(<&[u8; 32]>::try_from(&privkey1_data[..]).unwrap());
363 let privkey2 = Ed25519Key::from_bytes(<&[u8; 32]>::try_from(&privkey2_data[..]).unwrap());
364
365 assert_eq!(
366 privkey1.to_public().as_bytes(),
367 &[
368 0xf9, 0xaa, 0x3f, 0xcc, 0xb7, 0xfe, 0x53, 0x9e, 0x47, 0x11, 0x88, 0xcc, 0xc9, 0xee,
369 0x65, 0x51, 0x4c, 0x59, 0x61, 0xc0, 0x70, 0xb0, 0x6c, 0xa1, 0x85, 0x96, 0x24, 0x84,
370 0xa4, 0x81, 0x3b, 0xee,
371 ]
372 );
373 assert_eq!(
374 privkey2.to_public().as_bytes(),
375 &[
376 0x68, 0x72, 0xb0, 0xa8, 0x74, 0xac, 0xfe, 0x1c, 0xac, 0xe1, 0x2b, 0x20, 0xea, 0x34,
377 0x85, 0x59, 0xa7, 0xec, 0xc9, 0x12, 0xf2, 0xfc, 0x7f, 0x67, 0x4f, 0x43, 0x48, 0x1d,
378 0xf9, 0x73, 0xd9, 0x2c,
379 ]
380 );
381
382 let txid1 =
383 TxHash::from_hex("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7")
384 .expect("txid1");
385 let txid2 =
386 TxHash::from_hex("82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32")
387 .expect("txid2");
388 let settings = {
389 let mut settings = TransactionSettings::default();
390 //settings.fee = Coin::u64(1478714)
391 settings.ttl = Some(999);
392 settings
393 };
394 let builder = TransactionBuilder::new(settings)
395 .inputs_add(TransactionInput(txid1, TxIndex(42)))
396 .inputs_add(TransactionInput(txid2, TxIndex(7)))
397 .outputs_add(TransactionOutput::V1(TransactionOutputV1 {
398 address: SerializedAddress::from_bytes(vec![
399 0x61, 0x1c, 0x61, 0x6f, 0x1a, 0xcb, 0x46, 0x06, 0x68, 0xa9, 0xb2, 0xf1, 0x23,
400 0xc8, 0x03, 0x72, 0xc2, 0xad, 0xad, 0x35, 0x83, 0xb9, 0xc6, 0xcd, 0x2b, 0x1d,
401 0xee, 0xed, 0x1c,
402 ]),
403 amount: TransactionOutputValue::OnlyCoin(Coin::u64(289)),
404 data_hash: None,
405 }))
406 .outputs_add(TransactionOutput::V1(TransactionOutputV1 {
407 address: SerializedAddress::from_bytes(vec![
408 0x61, 0xbc, 0xd1, 0x8f, 0xcf, 0xfa, 0x79, 0x7c, 0x16, 0xc0, 0x07, 0x01, 0x4e,
409 0x2b, 0x85, 0x53, 0xb8, 0xb9, 0xb1, 0xe9, 0x4c, 0x50, 0x76, 0x88, 0x72, 0x62,
410 0x43, 0xd6, 0x11,
411 ]),
412 amount: TransactionOutputValue::OnlyCoin(Coin::u64(874551452)),
413 data_hash: None,
414 }))
415 .fee(Coin::u64(1478714));
416 let body = builder.body();
417 let body_data = body.serialize();
418 let tx_body_hash = body.hash();
419 assert_eq!(tx_body_hash.0, txbody_hash_from_slice);
420 assert_eq!(body_data.as_ref(), tx_expected.body.as_ref());
421
422 let t = builder
423 .signer()
424 .add_signature(privkey1.to_witness(&tx_body_hash))
425 .add_signature(privkey2.to_witness(&tx_body_hash))
426 .finalize();
427 assert_eq!(
428 t.verify_vkeys(),
429 SignatureVerification::Passed,
430 "cannot verify freshly generated signature"
431 );
432
433 let tx_data = t.to_bytes();
434 assert_eq!(
435 tx_data, result,
436 "{:#?}\n{:#?}",
437 tx_expected.witness, t.witness
438 );
439 //println!("{:?}", tx_expected.witness);
440 }
441 */
442
443 /*
444 #[test]
445 fn random_vec() {
446 let body = [
447 165, 0, 129, 130, 88, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
448 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 129, 130, 88, 57, 0, 42, 21, 122, 39, 160,
449 98, 31, 170, 131, 132, 74, 105, 123, 52, 124, 251, 64, 218, 211, 161, 64, 243, 68, 11,
450 228, 139, 56, 52, 129, 226, 227, 96, 102, 67, 17, 159, 245, 188, 53, 46, 220, 15, 104,
451 199, 116, 182, 114, 123, 35, 187, 198, 135, 130, 58, 192, 223, 26, 0, 15, 62, 88, 2,
452 25, 3, 232, 3, 25, 3, 232, 7, 88, 32, 64, 89, 113, 48, 84, 146, 202, 106, 151, 62, 19,
453 73, 79, 167, 143, 251, 3, 215, 69, 231, 142, 204, 155, 128, 80, 120, 62, 100, 94, 149,
454 225, 163,
455 ];
456 let txbodyhash = TxHash([
457 46, 98, 186, 242, 236, 242, 223, 12, 166, 121, 139, 80, 248, 227, 165, 198, 165, 96,
458 250, 47, 74, 107, 155, 237, 0, 45, 205, 42, 174, 181, 65, 95,
459 ]);
460 let sk = Ed25519Key::from_bytes(&[
461 198, 96, 229, 3, 21, 215, 106, 83, 216, 7, 50, 239, 218, 118, 48, 202, 232, 136, 93,
462 251, 133, 196, 99, 120, 104, 75, 60, 97, 3, 225, 40, 74,
463 ]);
464 let expected_sig = [
465 157, 212, 245, 1, 76, 138, 152, 118, 2, 149, 83, 50, 16, 187, 78, 95, 188, 111, 132,
466 151, 118, 158, 62, 252, 174, 111, 33, 157, 18, 252, 18, 227, 6, 240, 72, 120, 184, 230,
467 235, 157, 209, 37, 169, 207, 113, 59, 54, 49, 199, 129, 47, 233, 202, 240, 137, 77, 81,
468 57, 106, 127, 203, 160, 86, 0,
469 ];
470
471 let mut r = cbored::Reader::new(&body);
472 let txbody: TransactionBody = r.decode().unwrap();
473 let got_hash = txbody.hash();
474
475 assert_eq!(txbodyhash, got_hash);
476
477 let witness = sk.to_witness(&txbodyhash);
478
479 assert_eq!(witness.signature.as_bytes(), &expected_sig);
480
481 assert_eq!(
482 witness.vkey.verify(&witness.signature, &txbodyhash.0),
483 SignatureVerification::Passed
484 );
485 }
486 */
487}