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
//! Defines the structure and validation logic for blocks within the Uqoin
//! blockchain.
//!
//! A block in Uqoin encapsulates a set of transactions and metadata essential
//! for maintaining the blockchain's integrity.
//! Each block contains:
//! - `offset`: The position of the block in the blockchain sequence.
//! - `size`: The number of transactions included in the block.
//! - `hash_prev`: The hash of the preceding block, establishing a link between
//! blocks.
//! - `validator`: The public key of the validator who created the block.
//! - `nonce`: A 256-bit random value used in the proof-of-work mechanism.
//! - `hash`: The resulting hash of the block, which must satisfy the network's
//! difficulty requirements.
//!
//! The module also defines:
//! - `BlockInfo`: A concise summary of a block's essential information.
//! - `BlockData`: An extended structure that includes all transactions
//! associated with a block.
//!
//! Constants:
//! - `GENESIS_HASH`: The predefined hash value for the genesis (first) block.
//! - `COMPLEXITY`: The network's difficulty level, determining the required
//! number of trailing zeros in a valid block hash.
//!
//! The `Block` struct provides methods for:
//! - Creating new blocks.
//! - Validating blocks against the previous block's information, current state,
//! and network complexity.
//! - Calculating the block's message and hash.
//! - Ensuring the block's hash meets the required complexity.
//!
//! This module ensures that each block adheres to the Uqoin protocol's rules,
//! maintaining the blockchain's security and consistency.

use rand::Rng;
use sha3::{Sha3_256, Digest};
use serde::{Serialize, Deserialize};

use crate::validate;
use crate::utils::*;
use crate::transaction::{Type, Transaction, group_transactions};
use crate::state::State;


/// Hash of the zero block.
pub const GENESIS_HASH: &str = 
    "E12BA98A17FD8F70608668AA32AEB3BE1F202B4BD69880A6C0CFE855B1A0706B";

/// Complexity after calibration.
pub const COMPLEXITY: usize = 24;


/// Basic structure for block.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Block {
    pub offset: u64,
    pub size: u64,
    pub hash_prev: U256,
    pub validator: U256,
    pub nonce: U256,
    pub hash: U256,
}


impl Block {
    /// New block.
    pub fn new(offset: u64, size: u64, hash_prev: U256, validator: U256, 
               nonce: U256, hash: U256) -> Self {
        Self { offset, size, hash_prev, validator, nonce, hash }
    }

    /// Full validation of the block that includes transactions, info of the 
    /// previous block, complexity, state between this block and the previous
    /// one.
    pub fn validate(&self, transactions: &[Transaction], 
                    block_info_prev: &BlockInfo, complexity: usize, 
                    state: &State, senders: &[U256]) -> UqoinResult<()> {
        // Check block hash
        validate!(block_info_prev.hash == self.hash_prev, 
                  BlockPreviousHashMismatch)?;

        // Check block offset
        validate!(block_info_prev.offset == self.offset, 
                  BlockOffsetMismatch)?;

        // Validate transactions
        Self::validate_transactions(transactions, &self.validator, state, 
                                    senders)?;

        // Calculate the message
        let msg = Self::calc_msg(&self.hash_prev, &self.validator, 
                                 transactions);

        // Calculate the hash
        let hash = Self::calc_hash(&msg, &self.nonce);

        // Check hash
        validate!(hash == self.hash, BlockInvalidHash)?;

        // Validate hash
        Self::validate_hash_complexity(&self.hash, transactions.len(), 
                                       complexity)?;

        // Return
        Ok(())
    }

    /// Build a new block for the transactions. It validates the final hash.
    pub fn build(block_info_prev: &BlockInfo, validator: U256, 
                 transactions: &[Transaction], nonce: U256,
                 complexity: usize, state: &State, 
                 senders: &[U256]) -> UqoinResult<Self> {
        // Validate transactions
        Self::validate_transactions(transactions, &validator, state, senders)?;

        // Calculate the message
        let msg = Self::calc_msg(&block_info_prev.hash, &validator, 
                                 transactions);

        // Calculate the hash
        let hash = Self::calc_hash(&msg, &nonce);

        // Validate hash
        Self::validate_hash_complexity(&hash, transactions.len(), complexity)?;

        // Create a block
        Ok(Self::new(block_info_prev.offset, 
                     transactions.len() as u64, 
                     block_info_prev.hash.clone(),
                     validator, nonce, hash))
    }

    /// Validate coins. The checks:
    /// 1. All coins are unique.
    /// 2. All transactions are valid (see `Transaction::validate_coins()`).
    #[deprecated(since="0.1.0", note="use groups and check_unique instead")]
    pub fn validate_coins(transactions: &[Transaction], state: &State, 
                          senders: &[U256]) -> UqoinResult<()> {
        // Repeated coins are not valid
        validate!(check_unique(transactions.iter().map(|tr| &tr.coin)), 
                  CoinNotUnique)?;

        // Validate coin in each transaction
        for (transaction, sender) in transactions.iter().zip(senders.iter()) {
            transaction.validate_coin(state, sender)?;
        }

        Ok(())
    }

    /// Validate transactions. The checks:
    /// 1. All coins are valid (see `validate_coins`).
    /// 2. All transactions can be groupped into groups and extensions.
    /// 3. Sender of each extension is the validator.
    /// 4. Values of groups and extensions correspond each other.
    /// Each group or extension has valid structure after the groupping because
    /// they cannot be created invalid due to inner validation.
    pub fn validate_transactions(transactions: &[Transaction], validator: &U256, 
                                 state: &State, senders: &[U256]) -> 
                                 UqoinResult<()> {
        // // Check coins
        // Self::validate_coins(transactions, state, senders)?;

        // Repeated coins are not valid
        validate!(check_unique(transactions.iter().map(|tr| &tr.coin)), 
                  CoinNotUnique)?;

        // Set a countdown for groupped transactions
        let mut countdown = transactions.len();

        // Loop for groups and extensions
        for (offset, group, ext) in group_transactions(transactions.to_vec(), 
                                                       state, senders) {
            // Get senders
            let group_senders = &senders[offset .. offset + group.len()];
            let ext_senders = &senders[
                offset + group.len() .. offset + group.len() + ext.len()
            ];

            // Check validator
            if let Some(ext_sender) = ext.get_sender(ext_senders) {
                validate!(&ext_sender == validator, BlockValidatorMismatch)?;
            }

            // Check value
            if ext.get_type() != Type::Transfer {
                validate!(group.get_order(state, group_senders) 
                    == ext.get_order(state, ext_senders), BlockOrderMismatch)?;
            }

            // Decrement the countdown
            countdown -= group.len() + ext.len();
        }

        // Validate that all transactions have been groupped
        validate!(countdown == 0, BlockBroken)?;

        Ok(())
    }

    /// Validate hash for the certain complexity.
    pub fn validate_hash_complexity(hash: &U256, size: usize, 
                                    complexity: usize) -> UqoinResult<()> {
        let limit_hash = Self::calc_limit_hash(size, complexity);
        validate!(Self::is_hash_valid(&hash.to_bytes(), &limit_hash), 
                  BlockInvalidHashComplexity)
    }

    /// calculate block message as hash of the important content.
    pub fn calc_msg(block_hash_prev: &U256, validator: &U256, 
                    transactions: &[Transaction]) -> U256 {
        let mut elems = vec![block_hash_prev.clone(), validator.clone()];
        elems.extend(transactions.iter().map(|tr| tr.get_hash()));
        hash_of_u256(elems.iter())
    }

    /// Calculate block hash from message and nonce.
    pub fn calc_hash(msg: &U256, nonce: &U256) -> U256 {
        hash_of_u256([msg, nonce].into_iter())
    }

    /// Chech if the hash corresponds to the necessary size.
    pub fn is_hash_valid(hash_bytes: &[u8], limit_hash_bytes: &[u8]) -> bool {
        hash_bytes <= limit_hash_bytes
    }

    /// Find correct nonce bytes to mine the block.
    pub fn mine<R: Rng>(rng: &mut R, block_hash_prev: &U256, validator: &U256, 
                        transactions: &[Transaction], 
                        complexity: usize, 
                        iterations: Option<usize>) -> Option<[u8; 32]> {
        // Calculate the message bytes
        let msg = Self::calc_msg(block_hash_prev, validator, transactions);

        // Number of transactions
        let size = transactions.len();

        // Calculate limit hash
        let limit_hash = Self::calc_limit_hash(size, complexity);

        // Initialize SHA3 hasher with the block message
        let mut hasher = Sha3_256::new();
        hasher.update(msg.to_bytes());

        // Mining loop
        for iteration in 0.. {
            // Stop by iterations
            if let Some(iterations) = iterations {
                if iteration >= iterations {
                    break;
                }
            }

            // Clone the hasher state before adding nonce
            let mut hasher_clone = hasher.clone();

            // Generate a random 256-bit nonce
            let nonce_bytes: [u8; 32] = rng.random();

            // Update the hasher with the generated nonce
            hasher_clone.update(nonce_bytes);

            // Get the bytes of the final hash
            let hash_bytes = hasher_clone.finalize();

            // If the hash is valid return the generated nonce and U256
            if Self::is_hash_valid(&hash_bytes, &limit_hash) {
                return Some(nonce_bytes);
            }
        }

        // Return `None` if nothing mined
        None
    }

    /// Calculate maximum allowed block hash depending on the size.
    fn calc_limit_hash(size: usize, complexity: usize) -> Vec<u8> {
        assert!(complexity > 0);
        let mut num = U256::from(1);
        num <<= 256 - complexity;
        let bytes = if size > 1 {
            num.divide_unit(size as u64).unwrap().0.to_bytes()
        } else {
            num.to_bytes()
        };
        bytes.into_iter().rev().collect::<Vec<u8>>()
    }
}


/// Short information about the block.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockInfo {
    /// Block number.
    pub bix: u64,

    /// Total number of transaction up to this block (`offset` for the next 
    /// block).
    pub offset: u64,

    /// Last block hash.
    pub hash: U256,
}


impl BlockInfo {
    /// Get information of the genesis block (`bix=0`).
    pub fn genesis() -> Self {
        Self {
            bix: 0,
            offset: 0,
            hash: U256::from_hex(GENESIS_HASH),
        }
    }
}


/// Full information about the block.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockData {
    /// Block number.
    pub bix: u64,

    /// Block data.
    pub block: Block,

    /// Included transactions.
    pub transactions: Vec<Transaction>,
}


impl BlockData {
    /// Get data of the genesis block (`bix=0`).
    pub fn genesis() -> Self {
        Self {
            bix: 0,
            block: Block {
                offset: 0,
                size: 0,
                hash_prev: U256::from(0),
                validator: U256::from(0),
                nonce: U256::from(0),
                hash: U256::from_hex(GENESIS_HASH),
            },
            transactions: Vec::new(),
        }
    }

    /// Get short information.
    pub fn get_block_info(&self) -> BlockInfo {
        BlockInfo {
            bix: self.bix,
            offset: self.block.offset + self.block.size,
            hash: self.block.hash.clone(),
        }
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use test::Bencher;
    use crate::schema::Schema;

    #[test]
    fn test_mine() {
        // Best value is complexity = 24 that corresponds to ~10 seconds 
        // per empty block (for --release, 1 core, desktop)
        let complexity = 8;

        // Initial arguments
        let mut rng = rand::rng();
        let schema = Schema::new();

        let block_hash_prev: U256 = rng.random();
        let validator: U256 = schema.gen_pair(&mut rng).1;

        let transactions: Vec<Transaction> = vec![];

        // Mining the nonce
        let nonce_bytes = Block::mine(&mut rng, &block_hash_prev, &validator, 
                                      &transactions, complexity, 
                                      Some(10000)).unwrap();

        // Calculate hash
        let msg = Block::calc_msg(&block_hash_prev, &validator, &transactions);
        let nonce = U256::from_bytes(&nonce_bytes);
        let hash = hash_of_u256([&msg, &nonce].into_iter());

        // Calculate limit hash
        let limit_hash = Block::calc_limit_hash(transactions.len(), complexity);

        // Check that the hash is valid
        assert!(hash.to_bytes() <= limit_hash);
        assert!(Block::is_hash_valid(&hash.to_bytes(), &limit_hash));
    }

    #[bench]
    fn bench_mine_10(bencher: &mut Bencher) {
        let size = 10;

        let mut rng = rand::rng();
        let schema = Schema::new();

        let block_hash_prev: U256 = rng.random();
        let validator: U256 = schema.gen_pair(&mut rng).1;
        let coin: U256 = rng.random();
        let addr: U256 = rng.random();
        let key: U256 = schema.gen_key(&mut rng);

        let transactions: Vec<Transaction> = vec![
            Transaction::build(
                &mut rng, coin.clone(), addr.clone(), &key, 0, &schema
            );
            size
        ];

        bencher.iter(|| {
            let _nonce = Block::mine(&mut rng, &block_hash_prev, &validator, 
                                     &transactions, 1, None);
        });
    }
    
    // Uncomment it to start calibration: 
    //     `cargo bench block::tests::bench_mine_calibration`
    // #[bench]
    // fn bench_mine_calibration(bencher: &mut Bencher) {
    //     // The result is ~4 s/iter
    //     let complexity = 24;

    //     let mut rng = rand::rng();
    //     let schema = Schema::new();

    //     let block_hash_prev: U256 = rng.random();
    //     let validator: U256 = schema.gen_pair(&mut rng).1;
    //     let coin: U256 = rng.random();
    //     let addr: U256 = rng.random();
    //     let key: U256 = schema.gen_key(&mut rng);

    //     let transactions: Vec<Transaction> = vec![
    //         Transaction::build(
    //             &mut rng, coin.clone(), addr.clone(), &key, 0, &schema
    //         ),
    //     ];

    //     bencher.iter(|| {
    //         let _nonce = Block::mine(&mut rng, &block_hash_prev, &validator, 
    //                                  &transactions, complexity, None);
    //     });
    // }
}