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}