uqoin-core 0.1.3

Core algorithms that implement Uqoin protocol in Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
//! Defines the structure and behavior of transactions within the Uqoin
//! protocol.
//!
//! Uqoin supports four types of transactions:
//! - **Transfer**: Moves a coin from one address to another.
//! - **Fee**: Represents a transaction fee.
//! - **Split**: Divides a coin into smaller denominations.
//! - **Merge**: Combines multiple coins into a larger denomination.
//!
//! Each transaction includes the coin's identifier, the recipient's address,
//! and a digital signature.
//! The sender's address can be derived from the signature and transaction
//! details, though this process may be computationally intensive.
//! To optimize performance, it's advisable to cache sender addresses after
//! extraction.
//!
//! Transactions can be grouped, especially when combining operations like a
//! main transaction with its associated fee.
//! Such groupings are valid within a specific blockchain state.
//! If the state changes, the validity of the group must be reassessed, ensuring
//! consistency and preventing validation errors.

use rand::Rng;
use serde::{Serialize, Deserialize};

use crate::validate;
use crate::utils::*;
use crate::schema::Schema;
use crate::coin::{coin_validate, coin_order};
use crate::state::State;
use crate::error::ErrorKind;


/// Enumerates the different types of transactions in the Uqoin protocol.
#[derive(Debug, PartialEq)]
pub enum Type {
    Transfer,
    Fee,
    Split,
    Merge,
}


/// Represents a transaction in the Uqoin protocol.
///
/// Each transaction includes:
/// - `coin`: The identifier of the coin involved.
/// - `addr`: The recipient's address.
/// - `sign_r` and `sign_s`: Components of the digital signature.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transaction {
    pub coin: U256,
    pub addr: U256,
    pub sign_r: U256,
    pub sign_s: U256,
}


impl Transaction {
    /// Constructs a new `Transaction` instance.
    pub fn new(coin: U256, addr: U256, sign_r: U256, sign_s: U256) -> Self {
        Self { coin, addr, sign_r, sign_s }
    }

    /// Build a transaction of the `coin` from `key` to `addr`. In case of
    /// fee, split and merge use 0, 1 and 2 for `addr` respectively.
    pub fn build<R: Rng>(rng: &mut R, coin: U256, addr: U256, key: &U256, 
                         counter: u64, schema: &Schema) -> Self {
        let hash = Self::calc_msg(&coin, &addr, counter);
        let (sign_r, sign_s) = schema.build_signature(rng, &hash, key);
        Self::new(coin, addr, sign_r, sign_s)
    }

    /// Determines the type of the transaction based on the recipient's address.
    pub fn get_type(&self) -> Type {
        if self.addr == U256::from(0) {
            Type::Fee
        } else if self.addr == U256::from(1) {
            Type::Split
        } else if self.addr == U256::from(2) {
            Type::Merge
        } else {
            Type::Transfer
        }
    }

    /// Computes the message hash used for signing the transaction.
    pub fn get_msg(&self, counter: u64) -> U256 {
        Self::calc_msg(&self.coin, &self.addr, counter)
    }

    /// Get transaction hash.
    pub fn get_hash(&self) -> U256 {
        hash_of_u256(
            [&self.coin, &self.addr, &self.sign_r, &self.sign_s].into_iter()
        )
    }

    /// Get transaction sender.
    #[deprecated(since="0.1.0", note="use precalculated sender instead")]
    pub fn get_sender(&self, state: &State, schema: &Schema) -> U256 {
        let counter = state.get_coin_counter(&self.coin);
        schema.extract_public(
            &self.get_msg(counter), 
            &(self.sign_r.clone(), self.sign_s.clone())
        )
    }

    /// Get order of the coin.
    pub fn get_order(&self, state: &State, sender: &U256) -> u64 {
        if let Some(coin_info) = state.get_coin_info(&self.coin) {
            coin_info.order
        } else {
            coin_order(&self.coin, sender)
        }
    }

    /// Validate coin in the transaction. The checks:
    /// 1. Sender is the owner of each coin, if it met before.
    /// 2. The coin number corresponds the previous block hash and the sender
    /// if the coin is new (just mined).
    pub fn validate_coin(&self, state: &State, 
                         sender: &U256) -> UqoinResult<()> {
        // Try to find the coin in coin-owner map
        if let Some(owner) = state.get_owner(&self.coin) {
            // Check ownership
            validate!(owner == sender, TransactionInvalidSender)?;
        } else {
            // Check mining
            coin_validate(&self.coin, sender)?;
        }

        Ok(())
    }

    /// Calculate transaction message as hash of the `coin` and `addr`.
    pub fn calc_msg(coin: &U256, addr: &U256, counter: u64) -> U256 {
        hash_of_u256([coin, addr, &U256::from(counter)].into_iter())
    }

    /// Calculate senders of given transactions. Since the sender is extracted
    /// from signature, it takes a while, so use it carefully.
    pub fn calc_senders(transactions: &[Self], state: &State, 
                        schema: &Schema) -> Vec<U256> {
        transactions.iter().map(|tr| {
            let counter = state.get_coin_counter(&tr.coin);
            let msg = Self::calc_msg(&tr.coin, &tr.addr, counter);
            let signature = (tr.sign_r.clone(), tr.sign_s.clone());
            schema.extract_public(&msg, &signature)
        }).collect::<Vec<U256>>()
    }
}


/// Group of transactions. Due to the check on create, group cannot be invalid.
/// The valid group must have: 1) unique coins, 2) the same sender, 3) correct 
/// coins ownership, 4) consistent transaction order, types, values and count.  
/// Empty group is not allowed.
#[derive(Debug, Clone)]
pub struct Group(Vec<Transaction>);


impl Group {
    /// Create group from transactions. Validation is included, so if the
    /// vector is not valid, `None` will be returned.
    pub fn new(transactions: Vec<Transaction>, state: &State, 
               senders: &[U256]) -> UqoinResult<Self> {
        Self::validate_transactions(&transactions, state, senders)?;
        Ok(Self(transactions))
    }

    /// Try to create a group from the leading transactions in the given slice.
    /// Fees are joined by the greedy approach.
    pub fn from_vec(transactions: &mut Vec<Transaction>, state: &State, 
                    senders: &[U256]) -> UqoinResult<Self> {
        if transactions.is_empty() {
            // `TransactionEmpty` if the slice is empty
            Err(ErrorKind::TransactionEmpty.into())
        } else {
            // Size of the group without fee
            let mut size = match transactions[0].get_type() {
                Type::Split => 1,
                Type::Merge => 3,
                Type::Transfer => 1,
                _ => 0,
            };

            if size == 0 {
                // `TransactionBrokenGroup` if we start from a fee transaction
                Err(ErrorKind::TransactionBrokenGroup.into())
            } else {
                // Increment size if the next transaction is fee
                if (size < transactions.len()) && 
                   (transactions[size].get_type() == Type::Fee) {
                    size += 1;
                }

                // Try to create a group using validation in `Self::new`
                let trs = vec_split_left(transactions, size);
                Self::new(trs, state, &senders[..size])
            }
        }
    }

    /// Accessor to the inner transactions.
    pub fn transactions(&self) -> &[Transaction] {
        &self.0
    }

    /// Get type of the group.
    pub fn get_type(&self) -> Type {
        self.0[0].get_type()
    }

    /// Get sender of the group.
    pub fn get_sender(&self, senders: &[U256]) -> U256 {
        senders[0].clone()
    }

    /// Get fee transaction.
    pub fn get_fee(&self) -> Option<&Transaction> {
        let fee_ix = match self.0[0].get_type() {
            Type::Split => 1,
            Type::Merge => 3,
            Type::Transfer => 1,
            _ => panic!("Invalid group."),
        };
        self.0.get(fee_ix)
    }

    /// Get hash of the group as the hash of leading transaction.
    pub fn get_hash(&self) -> U256 {
        self.0[0].get_hash()
    }

    /// Get total number of transactions.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Get order of the main coins.
    pub fn get_order(&self, state: &State, senders: &[U256]) -> u64 {
        match self.get_type() {
            Type::Split => self.0[0].get_order(state, &senders[0]),
            Type::Merge => self.0[0].get_order(state, &senders[0]) + 1,
            Type::Transfer => self.0[0].get_order(state, &senders[0]),
            _ => panic!("Invalid transactions in the group."),
        }
    }

    /// Get number or required response transactions from the validator.
    pub fn ext_size(&self) -> usize {
        match self.get_type() {
            Type::Split => 3,
            Type::Merge => 1,
            Type::Transfer => 0,
            _ => panic!("Invalid transactions in the group."),
        }
    }

    /// Validate transactions for the group creation.
    pub fn validate_transactions(transactions: &[Transaction], state: &State, 
                                 senders: &[U256]) -> UqoinResult<()> {
        // Error if no transactions in the slice
        validate!(!transactions.is_empty(), TransactionEmpty)?;

        // Check unique coins
        validate!(check_unique(transactions.iter().map(|tr| &tr.coin)), 
                  CoinNotUnique)?;

        // Check same sender
        validate!(check_same(senders.iter()), TransactionInvalidSender)?;

        // Check ownership
        for transaction in transactions.iter() {
            transaction.validate_coin(state, &senders[0])?;
        }

        // Check the first type
        match transactions[0].get_type() {
            // Error if the first transaction is fee
            Type::Fee => validate!(false, TransactionBrokenGroup)?,

            // Check the rest fees if split
            Type::Split => {
                if transactions.len() > 1 {
                    validate!(transactions.len() == 2, TransactionBrokenGroup)?;
                    validate!(transactions[1].get_type() == Type::Fee, 
                              TransactionBrokenGroup)?;
                }
            },

            // Check fees, other types and values for the rest if merge
            Type::Merge => {
                let fee_check = (transactions.len() == 3) || (
                    (transactions.len() == 4) && 
                    (transactions[3].get_type() == Type::Fee)
                );

                validate!(fee_check, TransactionBrokenGroup)?;

                let type_check = 
                    (transactions[1].get_type() == Type::Merge) && 
                    (transactions[2].get_type() == Type::Merge);

                validate!(type_check, TransactionBrokenGroup)?;

                let order0 = transactions[0].get_order(state, &senders[0]);
                let order1 = transactions[1].get_order(state, &senders[1]);
                let order2 = transactions[2].get_order(state, &senders[2]);

                let order_check = (order1 + 1 == order0) && 
                                  (order2 + 1 == order0);

                validate!(order_check, TransactionBrokenGroup)?;
            },

            // Check the rest fees if transfer
            Type::Transfer => {
                if transactions.len() > 1 {
                    validate!(transactions.len() == 2, TransactionBrokenGroup)?;
                    validate!(transactions[1].get_type() == Type::Fee, 
                              TransactionBrokenGroup)?;
                }
            },
        }

        Ok(())
    }
}


/// Extension for the group of transactions. It must be filled by the validator
/// in `Split` or `Merge` types. Due to the check on create, extenstion cannot  
/// be invalid. The valid extension must have: 1) unique coins, 2) the same  
/// sender (validator), 3) correct coins ownership, 4) consistent transaction  
/// order, types, values and count depending on the group type. Extension can be 
/// empty for `Transfer` type.
#[derive(Debug, Clone)]
pub struct Ext(Vec<Transaction>);


impl Ext {
    /// Create a new extension from transactions.
    pub fn new(transactions: Vec<Transaction>, state: &State, 
               senders: &[U256]) -> UqoinResult<Self> {
        Self::validate_transactions(&transactions, state, senders)?;
        Ok(Self(transactions))
    }

    /// Accessor to the inner transactions.
    pub fn transactions(&self) -> &[Transaction] {
        &self.0
    }

    /// Get type of the extension.
    pub fn get_type(&self) -> Type {
        match self.0.len() {
            0 => Type::Transfer,
            1 => Type::Merge,
            3 => Type::Split,
            _ => panic!("Invalid size of extension."),
        }
    }

    /// Get sender of the extension.
    pub fn get_sender(&self, senders: &[U256]) -> Option<U256> {
        if self.0.is_empty() {
            None
        } else {
            Some(senders[0].clone())
        }
    }

    /// Get total number of transactions.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Get order of the main coins in the extension.
    pub fn get_order(&self, state: &State, senders: &[U256]) -> u64 {
        match self.0.len() {
            0 => 0,
            1 => self.0[0].get_order(state, &senders[0]),
            3 => &self.0[0].get_order(state, &senders[0]) + 1,
            _ => panic!("Invalid transactions in the group."),
        }
    }

    /// Validate transactions for the extension creation.
    pub fn validate_transactions(transactions: &[Transaction], state: &State, 
                                 senders: &[U256]) -> UqoinResult<()> {
        // Check unique coins
        validate!(check_unique(transactions.iter().map(|tr| &tr.coin)), 
                  CoinNotUnique)?;

        // Check same sender
        validate!(check_same(senders.iter()), TransactionInvalidSender)?;

        // Check ownership
        for transaction in transactions.iter() {
            transaction.validate_coin(state, &senders[0])?;
        }

        // Check the size
        match transactions.len() {
            // Ok for the transfer type
            0 => {},

            // Check the type for the merge type
            1 => validate!(transactions[0].get_type() == Type::Transfer, 
                           TransactionBrokenExt)?,

            // Complex check for the split check
            3 => {
                // Get the first addr
                let addr = &transactions[0].addr;

                // Check transfer type
                let type_check = transactions.iter()
                    .all(|tr| tr.get_type() == Type::Transfer);

                validate!(type_check, TransactionBrokenExt)?;

                // Check same addr
                let addr_check = 
                    (&transactions[1].addr == addr) && 
                    (&transactions[2].addr == addr);

                validate!(addr_check, TransactionBrokenExt)?;

                // Check order
                let order0 = transactions[0].get_order(state, &senders[0]);
                let order1 = transactions[1].get_order(state, &senders[1]);
                let order2 = transactions[2].get_order(state, &senders[2]);

                let order_check = (order1 + 1 == order0) && 
                                  (order2 + 1 == order0);

                validate!(order_check, TransactionBrokenExt)?;
            },

            // Panic if the wrong size
            _ => panic!("Invalid size of extension."),
        }

        Ok(())
    }
}


/// Try to split transactions into groups and extensions. In case of not valid
/// `transactions` the iterator stops until the first error, so for the
/// validation purpose check the total size of yielded groups and extensions.
pub fn group_transactions(mut transactions: Vec<Transaction>, state: &State, 
                          senders: &[U256]) -> 
                          impl Iterator<Item = (usize, Group, Ext)> {
    let mut offset = 0;
    std::iter::from_fn(move || {
        if let Ok(group) = Group::from_vec(&mut transactions, state, 
                                           &senders[offset..]) {
            let group_size = group.len();
            let ext_size = group.ext_size();
            let ext_trs = vec_split_left(&mut transactions, ext_size);
            let ext_senders = &senders[
                offset + group_size .. offset + group_size + ext_size
            ];

            if let Ok(ext) = Ext::new(ext_trs, state, ext_senders) {
                let res = (offset, group, ext);
                offset += group_size + ext_size;
                Some(res)
            } else {
                None
            }
        } else {
            None
        }
    })
}