1use bincode::error::EncodeError;
2use thiserror::Error;
3
4use core::{
5 block::Block,
6 blockchain::BlockchainError,
7 economics::{DEV_WALLET, calculate_dev_fee, get_block_reward},
8 transaction::{Transaction, TransactionInput, TransactionOutput},
9};
10
11pub mod api;
13
14pub mod blockchain_data_provider;
16
17pub mod core;
19
20pub mod crypto;
22
23pub mod node;
25
26mod tests;
28
29pub mod version;
31
32pub use core::economics;
34pub use core::economics::to_snap;
35pub use economics::to_nano;
36
37use crate::{
38 blockchain_data_provider::{BlockchainDataProvider, BlockchainDataProviderError},
39 crypto::keys::{Private, Public}, economics::GENESIS_PREVIOUS_BLOCK_HASH,
40};
41
42#[derive(Error, Debug)]
43pub enum UtilError {
44 #[error("Blockchain error: {0}")]
45 BlockchainError(#[from] BlockchainError),
46
47 #[error("Insufficient funds to complete operation")]
48 InsufficientFunds,
49
50 #[error("Encode error {0}")]
51 EncodeError(#[from] EncodeError),
52
53 #[error("Data provider error {0}")]
54 BlockchainDataProviderError(#[from] BlockchainDataProviderError)
55}
56
57pub async fn build_transaction<B>(
60 blockchain_data_provider: &B,
61 sender: Private,
62 mut receivers: Vec<(Public, u64)>,
63) -> Result<Transaction, UtilError> where B: BlockchainDataProvider {
64 let target_balance = receivers
65 .iter()
66 .fold(0u64, |acc, receiver| acc + receiver.1);
67
68 let available_inputs = blockchain_data_provider.get_available_transaction_outputs(sender.to_public()).await?;
69
70 let mut used_inputs = vec![];
71
72 let mut current_funds = 0u64;
73 for (transaction, input, index) in available_inputs {
74 current_funds += input.amount;
75 used_inputs.push((transaction, input, index));
76 if current_funds >= target_balance {
77 break;
78 }
79 }
80
81 if target_balance > current_funds {
82 return Err(UtilError::InsufficientFunds);
83 }
84
85 if target_balance < current_funds {
86 receivers.push((sender.to_public(), current_funds - target_balance));
87 }
88
89 used_inputs.sort_by(|a, b| a.1.amount.cmp(&b.1.amount)); let transaction = Transaction::new_transaction_now(
92 used_inputs
93 .iter()
94 .map(|input| TransactionInput {
95 transaction_id: input.0,
96 output_index: input.2,
97 signature: None,
98 })
99 .collect::<Vec<TransactionInput>>(),
100 receivers
101 .iter()
102 .map(|receiver| TransactionOutput {
103 amount: receiver.1,
104 receiver: receiver.0,
105 })
106 .collect(),
107 &mut vec![sender; used_inputs.len()],
108 )?;
109
110 Ok(transaction)
111}
112
113pub async fn build_block<B>(
118 blockchain_data_provider: &B,
119 transactions: &Vec<Transaction>,
120 miner: Public,
121) -> Result<Block, UtilError> where B: BlockchainDataProvider {
122 let reward = get_block_reward(blockchain_data_provider.get_height().await?);
123 let mut transactions = transactions.clone();
124 transactions.push(Transaction::new_transaction_now(
125 vec![],
126 vec![
127 TransactionOutput {
128 amount: calculate_dev_fee(reward),
129 receiver: DEV_WALLET,
130 },
131 TransactionOutput {
132 amount: reward - calculate_dev_fee(reward),
133 receiver: miner,
134 },
135 ],
136 &mut vec![],
137 )?);
138 let reward_tx_i = transactions.len() - 1;
139 transactions[reward_tx_i]
140 .compute_pow(&blockchain_data_provider.get_transaction_difficulty().await?, None)?;
141 let block = Block::new_block_now(
142 transactions,
143 &blockchain_data_provider.get_block_difficulty().await?,
144 &blockchain_data_provider.get_transaction_difficulty().await?,
145 blockchain_data_provider.get_block_hash_by_height(blockchain_data_provider.get_height().await?.saturating_sub(1)).await?.unwrap_or(GENESIS_PREVIOUS_BLOCK_HASH)
146 );
147
148 Ok(block)
149}