Skip to main content

uqoin_core/
transaction.rs

1//! Defines the structure and behavior of transactions within the Uqoin
2//! protocol.
3//!
4//! Uqoin supports four types of transactions:
5//! - **Transfer**: Moves a coin from one address to another.
6//! - **Fee**: Represents a transaction fee.
7//! - **Split**: Divides a coin into smaller denominations.
8//! - **Merge**: Combines multiple coins into a larger denomination.
9//!
10//! Each transaction includes the coin's identifier, the recipient's address,
11//! and a digital signature.
12//! The sender's address can be derived from the signature and transaction
13//! details, though this process may be computationally intensive.
14//! To optimize performance, it's advisable to cache sender addresses after
15//! extraction.
16//!
17//! Transactions can be grouped, especially when combining operations like a
18//! main transaction with its associated fee.
19//! Such groupings are valid within a specific blockchain state.
20//! If the state changes, the validity of the group must be reassessed, ensuring
21//! consistency and preventing validation errors.
22
23use rand::Rng;
24use serde::{Serialize, Deserialize};
25
26use crate::validate;
27use crate::utils::*;
28use crate::schema::Schema;
29use crate::coin::{coin_validate, coin_order};
30use crate::state::State;
31use crate::error::ErrorKind;
32
33
34/// Enumerates the different types of transactions in the Uqoin protocol.
35#[derive(Debug, PartialEq)]
36pub enum Type {
37    Transfer,
38    Fee,
39    Split,
40    Merge,
41}
42
43
44/// Represents a transaction in the Uqoin protocol.
45///
46/// Each transaction includes:
47/// - `coin`: The identifier of the coin involved.
48/// - `addr`: The recipient's address.
49/// - `sign_r` and `sign_s`: Components of the digital signature.
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Transaction {
52    pub coin: U256,
53    pub addr: U256,
54    pub sign_r: U256,
55    pub sign_s: U256,
56}
57
58
59impl Transaction {
60    /// Constructs a new `Transaction` instance.
61    pub fn new(coin: U256, addr: U256, sign_r: U256, sign_s: U256) -> Self {
62        Self { coin, addr, sign_r, sign_s }
63    }
64
65    /// Build a transaction of the `coin` from `key` to `addr`. In case of
66    /// fee, split and merge use 0, 1 and 2 for `addr` respectively.
67    pub fn build<R: Rng>(rng: &mut R, coin: U256, addr: U256, key: &U256, 
68                         counter: u64, schema: &Schema) -> Self {
69        let hash = Self::calc_msg(&coin, &addr, counter);
70        let (sign_r, sign_s) = schema.build_signature(rng, &hash, key);
71        Self::new(coin, addr, sign_r, sign_s)
72    }
73
74    /// Determines the type of the transaction based on the recipient's address.
75    pub fn get_type(&self) -> Type {
76        if self.addr == U256::from(0) {
77            Type::Fee
78        } else if self.addr == U256::from(1) {
79            Type::Split
80        } else if self.addr == U256::from(2) {
81            Type::Merge
82        } else {
83            Type::Transfer
84        }
85    }
86
87    /// Computes the message hash used for signing the transaction.
88    pub fn get_msg(&self, counter: u64) -> U256 {
89        Self::calc_msg(&self.coin, &self.addr, counter)
90    }
91
92    /// Get transaction hash.
93    pub fn get_hash(&self) -> U256 {
94        hash_of_u256(
95            [&self.coin, &self.addr, &self.sign_r, &self.sign_s].into_iter()
96        )
97    }
98
99    /// Get transaction sender.
100    #[deprecated(since="0.1.0", note="use precalculated sender instead")]
101    pub fn get_sender(&self, state: &State, schema: &Schema) -> U256 {
102        let counter = state.get_coin_counter(&self.coin);
103        schema.extract_public(
104            &self.get_msg(counter), 
105            &(self.sign_r.clone(), self.sign_s.clone())
106        )
107    }
108
109    /// Get order of the coin.
110    pub fn get_order(&self, state: &State, sender: &U256) -> u64 {
111        if let Some(coin_info) = state.get_coin_info(&self.coin) {
112            coin_info.order
113        } else {
114            coin_order(&self.coin, sender)
115        }
116    }
117
118    /// Validate coin in the transaction. The checks:
119    /// 1. Sender is the owner of each coin, if it met before.
120    /// 2. The coin number corresponds the previous block hash and the sender
121    /// if the coin is new (just mined).
122    pub fn validate_coin(&self, state: &State, 
123                         sender: &U256) -> UqoinResult<()> {
124        // Try to find the coin in coin-owner map
125        if let Some(owner) = state.get_owner(&self.coin) {
126            // Check ownership
127            validate!(owner == sender, TransactionInvalidSender)?;
128        } else {
129            // Check mining
130            coin_validate(&self.coin, sender)?;
131        }
132
133        Ok(())
134    }
135
136    /// Calculate transaction message as hash of the `coin` and `addr`.
137    pub fn calc_msg(coin: &U256, addr: &U256, counter: u64) -> U256 {
138        hash_of_u256([coin, addr, &U256::from(counter)].into_iter())
139    }
140
141    /// Calculate senders of given transactions. Since the sender is extracted
142    /// from signature, it takes a while, so use it carefully.
143    pub fn calc_senders(transactions: &[Self], state: &State, 
144                        schema: &Schema) -> Vec<U256> {
145        transactions.iter().map(|tr| {
146            let counter = state.get_coin_counter(&tr.coin);
147            let msg = Self::calc_msg(&tr.coin, &tr.addr, counter);
148            let signature = (tr.sign_r.clone(), tr.sign_s.clone());
149            schema.extract_public(&msg, &signature)
150        }).collect::<Vec<U256>>()
151    }
152}
153
154
155/// Group of transactions. Due to the check on create, group cannot be invalid.
156/// The valid group must have: 1) unique coins, 2) the same sender, 3) correct 
157/// coins ownership, 4) consistent transaction order, types, values and count.  
158/// Empty group is not allowed.
159#[derive(Debug, Clone)]
160pub struct Group(Vec<Transaction>);
161
162
163impl Group {
164    /// Create group from transactions. Validation is included, so if the
165    /// vector is not valid, `None` will be returned.
166    pub fn new(transactions: Vec<Transaction>, state: &State, 
167               senders: &[U256]) -> UqoinResult<Self> {
168        Self::validate_transactions(&transactions, state, senders)?;
169        Ok(Self(transactions))
170    }
171
172    /// Try to create a group from the leading transactions in the given slice.
173    /// Fees are joined by the greedy approach.
174    pub fn from_vec(transactions: &mut Vec<Transaction>, state: &State, 
175                    senders: &[U256]) -> UqoinResult<Self> {
176        if transactions.is_empty() {
177            // `TransactionEmpty` if the slice is empty
178            Err(ErrorKind::TransactionEmpty.into())
179        } else {
180            // Size of the group without fee
181            let mut size = match transactions[0].get_type() {
182                Type::Split => 1,
183                Type::Merge => 3,
184                Type::Transfer => 1,
185                _ => 0,
186            };
187
188            if size == 0 {
189                // `TransactionBrokenGroup` if we start from a fee transaction
190                Err(ErrorKind::TransactionBrokenGroup.into())
191            } else {
192                // Increment size if the next transaction is fee
193                if (size < transactions.len()) && 
194                   (transactions[size].get_type() == Type::Fee) {
195                    size += 1;
196                }
197
198                // Try to create a group using validation in `Self::new`
199                let trs = vec_split_left(transactions, size);
200                Self::new(trs, state, &senders[..size])
201            }
202        }
203    }
204
205    /// Accessor to the inner transactions.
206    pub fn transactions(&self) -> &[Transaction] {
207        &self.0
208    }
209
210    /// Get type of the group.
211    pub fn get_type(&self) -> Type {
212        self.0[0].get_type()
213    }
214
215    /// Get sender of the group.
216    pub fn get_sender(&self, senders: &[U256]) -> U256 {
217        senders[0].clone()
218    }
219
220    /// Get fee transaction.
221    pub fn get_fee(&self) -> Option<&Transaction> {
222        let fee_ix = match self.0[0].get_type() {
223            Type::Split => 1,
224            Type::Merge => 3,
225            Type::Transfer => 1,
226            _ => panic!("Invalid group."),
227        };
228        self.0.get(fee_ix)
229    }
230
231    /// Get hash of the group as the hash of leading transaction.
232    pub fn get_hash(&self) -> U256 {
233        self.0[0].get_hash()
234    }
235
236    /// Get total number of transactions.
237    pub fn len(&self) -> usize {
238        self.0.len()
239    }
240
241    /// Get order of the main coins.
242    pub fn get_order(&self, state: &State, senders: &[U256]) -> u64 {
243        match self.get_type() {
244            Type::Split => self.0[0].get_order(state, &senders[0]),
245            Type::Merge => self.0[0].get_order(state, &senders[0]) + 1,
246            Type::Transfer => self.0[0].get_order(state, &senders[0]),
247            _ => panic!("Invalid transactions in the group."),
248        }
249    }
250
251    /// Get number or required response transactions from the validator.
252    pub fn ext_size(&self) -> usize {
253        match self.get_type() {
254            Type::Split => 3,
255            Type::Merge => 1,
256            Type::Transfer => 0,
257            _ => panic!("Invalid transactions in the group."),
258        }
259    }
260
261    /// Validate transactions for the group creation.
262    pub fn validate_transactions(transactions: &[Transaction], state: &State, 
263                                 senders: &[U256]) -> UqoinResult<()> {
264        // Error if no transactions in the slice
265        validate!(!transactions.is_empty(), TransactionEmpty)?;
266
267        // Check unique coins
268        validate!(check_unique(transactions.iter().map(|tr| &tr.coin)), 
269                  CoinNotUnique)?;
270
271        // Check same sender
272        validate!(check_same(senders.iter()), TransactionInvalidSender)?;
273
274        // Check ownership
275        for transaction in transactions.iter() {
276            transaction.validate_coin(state, &senders[0])?;
277        }
278
279        // Check the first type
280        match transactions[0].get_type() {
281            // Error if the first transaction is fee
282            Type::Fee => validate!(false, TransactionBrokenGroup)?,
283
284            // Check the rest fees if split
285            Type::Split => {
286                if transactions.len() > 1 {
287                    validate!(transactions.len() == 2, TransactionBrokenGroup)?;
288                    validate!(transactions[1].get_type() == Type::Fee, 
289                              TransactionBrokenGroup)?;
290                }
291            },
292
293            // Check fees, other types and values for the rest if merge
294            Type::Merge => {
295                let fee_check = (transactions.len() == 3) || (
296                    (transactions.len() == 4) && 
297                    (transactions[3].get_type() == Type::Fee)
298                );
299
300                validate!(fee_check, TransactionBrokenGroup)?;
301
302                let type_check = 
303                    (transactions[1].get_type() == Type::Merge) && 
304                    (transactions[2].get_type() == Type::Merge);
305
306                validate!(type_check, TransactionBrokenGroup)?;
307
308                let order0 = transactions[0].get_order(state, &senders[0]);
309                let order1 = transactions[1].get_order(state, &senders[1]);
310                let order2 = transactions[2].get_order(state, &senders[2]);
311
312                let order_check = (order1 + 1 == order0) && 
313                                  (order2 + 1 == order0);
314
315                validate!(order_check, TransactionBrokenGroup)?;
316            },
317
318            // Check the rest fees if transfer
319            Type::Transfer => {
320                if transactions.len() > 1 {
321                    validate!(transactions.len() == 2, TransactionBrokenGroup)?;
322                    validate!(transactions[1].get_type() == Type::Fee, 
323                              TransactionBrokenGroup)?;
324                }
325            },
326        }
327
328        Ok(())
329    }
330}
331
332
333/// Extension for the group of transactions. It must be filled by the validator
334/// in `Split` or `Merge` types. Due to the check on create, extenstion cannot  
335/// be invalid. The valid extension must have: 1) unique coins, 2) the same  
336/// sender (validator), 3) correct coins ownership, 4) consistent transaction  
337/// order, types, values and count depending on the group type. Extension can be 
338/// empty for `Transfer` type.
339#[derive(Debug, Clone)]
340pub struct Ext(Vec<Transaction>);
341
342
343impl Ext {
344    /// Create a new extension from transactions.
345    pub fn new(transactions: Vec<Transaction>, state: &State, 
346               senders: &[U256]) -> UqoinResult<Self> {
347        Self::validate_transactions(&transactions, state, senders)?;
348        Ok(Self(transactions))
349    }
350
351    /// Accessor to the inner transactions.
352    pub fn transactions(&self) -> &[Transaction] {
353        &self.0
354    }
355
356    /// Get type of the extension.
357    pub fn get_type(&self) -> Type {
358        match self.0.len() {
359            0 => Type::Transfer,
360            1 => Type::Merge,
361            3 => Type::Split,
362            _ => panic!("Invalid size of extension."),
363        }
364    }
365
366    /// Get sender of the extension.
367    pub fn get_sender(&self, senders: &[U256]) -> Option<U256> {
368        if self.0.is_empty() {
369            None
370        } else {
371            Some(senders[0].clone())
372        }
373    }
374
375    /// Get total number of transactions.
376    pub fn len(&self) -> usize {
377        self.0.len()
378    }
379
380    /// Get order of the main coins in the extension.
381    pub fn get_order(&self, state: &State, senders: &[U256]) -> u64 {
382        match self.0.len() {
383            0 => 0,
384            1 => self.0[0].get_order(state, &senders[0]),
385            3 => &self.0[0].get_order(state, &senders[0]) + 1,
386            _ => panic!("Invalid transactions in the group."),
387        }
388    }
389
390    /// Validate transactions for the extension creation.
391    pub fn validate_transactions(transactions: &[Transaction], state: &State, 
392                                 senders: &[U256]) -> UqoinResult<()> {
393        // Check unique coins
394        validate!(check_unique(transactions.iter().map(|tr| &tr.coin)), 
395                  CoinNotUnique)?;
396
397        // Check same sender
398        validate!(check_same(senders.iter()), TransactionInvalidSender)?;
399
400        // Check ownership
401        for transaction in transactions.iter() {
402            transaction.validate_coin(state, &senders[0])?;
403        }
404
405        // Check the size
406        match transactions.len() {
407            // Ok for the transfer type
408            0 => {},
409
410            // Check the type for the merge type
411            1 => validate!(transactions[0].get_type() == Type::Transfer, 
412                           TransactionBrokenExt)?,
413
414            // Complex check for the split check
415            3 => {
416                // Get the first addr
417                let addr = &transactions[0].addr;
418
419                // Check transfer type
420                let type_check = transactions.iter()
421                    .all(|tr| tr.get_type() == Type::Transfer);
422
423                validate!(type_check, TransactionBrokenExt)?;
424
425                // Check same addr
426                let addr_check = 
427                    (&transactions[1].addr == addr) && 
428                    (&transactions[2].addr == addr);
429
430                validate!(addr_check, TransactionBrokenExt)?;
431
432                // Check order
433                let order0 = transactions[0].get_order(state, &senders[0]);
434                let order1 = transactions[1].get_order(state, &senders[1]);
435                let order2 = transactions[2].get_order(state, &senders[2]);
436
437                let order_check = (order1 + 1 == order0) && 
438                                  (order2 + 1 == order0);
439
440                validate!(order_check, TransactionBrokenExt)?;
441            },
442
443            // Panic if the wrong size
444            _ => panic!("Invalid size of extension."),
445        }
446
447        Ok(())
448    }
449}
450
451
452/// Try to split transactions into groups and extensions. In case of not valid
453/// `transactions` the iterator stops until the first error, so for the
454/// validation purpose check the total size of yielded groups and extensions.
455pub fn group_transactions(mut transactions: Vec<Transaction>, state: &State, 
456                          senders: &[U256]) -> 
457                          impl Iterator<Item = (usize, Group, Ext)> {
458    let mut offset = 0;
459    std::iter::from_fn(move || {
460        if let Ok(group) = Group::from_vec(&mut transactions, state, 
461                                           &senders[offset..]) {
462            let group_size = group.len();
463            let ext_size = group.ext_size();
464            let ext_trs = vec_split_left(&mut transactions, ext_size);
465            let ext_senders = &senders[
466                offset + group_size .. offset + group_size + ext_size
467            ];
468
469            if let Ok(ext) = Ext::new(ext_trs, state, ext_senders) {
470                let res = (offset, group, ext);
471                offset += group_size + ext_size;
472                Some(res)
473            } else {
474                None
475            }
476        } else {
477            None
478        }
479    })
480}