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 blockchain_data_provider;
13
14pub mod core;
16
17pub mod crypto;
19
20pub mod node;
22
23pub mod full_node;
25
26pub mod light_node;
28
29pub mod api;
31
32pub mod bounded_set;
34
35mod tests;
37
38pub mod version;
40
41pub use core::economics;
43pub use core::economics::to_snap;
44pub use economics::to_nano;
45
46use crate::{
47 blockchain_data_provider::{BlockchainDataProvider, BlockchainDataProviderError},
48 core::{
49 block::MAX_TRANSACTIONS_PER_BLOCK,
50 transaction::{MAX_TRANSACTION_IO, TransactionError},
51 },
52 crypto::{
53 address_inclusion_filter::{AddressInclusionFilter, AddressInclusionFilterError},
54 keys::{Private, Public},
55 merkle_tree::MerkleTree,
56 },
57 economics::GENESIS_PREVIOUS_BLOCK_HASH,
58};
59
60#[derive(Error, Debug)]
61pub enum UtilError {
62 #[error("Blockchain error: {0}")]
63 BlockchainError(#[from] BlockchainError),
64
65 #[error("Transaction error: {0}")]
66 TransactionError(#[from] TransactionError),
67
68 #[error("Insufficient funds to complete operation")]
69 InsufficientFunds,
70
71 #[error("Encode error {0}")]
72 EncodeError(#[from] EncodeError),
73
74 #[error("Data provider error {0}")]
75 BlockchainDataProviderError(#[from] BlockchainDataProviderError),
76
77 #[error(
78 "Too many inputs and outputs for one transaction. Consider splitting transaction in to more than one (smaller SNAP amount) or less receivers."
79 )]
80 TooMuchIO,
81
82 #[error("Too many transactions for block")]
83 TooManyTransactions,
84
85 #[error("Address inclusion filter error: {0}")]
86 AddressInclusionFilter(#[from] AddressInclusionFilterError),
87}
88
89pub async fn build_transaction<B>(
92 blockchain_data_provider: &B,
93 sender: Private,
94 mut receivers: Vec<(Public, u64)>,
95 ignore_inputs: &Vec<TransactionInput>,
96) -> Result<Transaction, UtilError>
97where
98 B: BlockchainDataProvider,
99{
100 let target_balance = receivers
101 .iter()
102 .fold(0u64, |acc, receiver| acc + receiver.1);
103
104 let mut available_inputs = blockchain_data_provider
105 .get_available_transaction_outputs(sender.to_public())
106 .await?;
107
108 available_inputs.retain(|(transaction, _, index)| {
109 !ignore_inputs
110 .iter()
111 .any(|i_input| i_input.output_index == *index && i_input.transaction_id == *transaction)
112 });
113
114 let mut used_inputs = vec![];
115
116 let mut current_funds = 0u64;
117 for (transaction, input, index) in available_inputs {
118 current_funds += input.amount;
119 used_inputs.push((transaction, input, index));
120 if current_funds >= target_balance {
121 break;
122 }
123 }
124
125 if target_balance > current_funds {
126 return Err(UtilError::InsufficientFunds);
127 }
128
129 if target_balance < current_funds {
130 receivers.push((sender.to_public(), current_funds - target_balance));
131 }
132
133 if used_inputs.len() + receivers.len() > MAX_TRANSACTION_IO {
134 return Err(UtilError::TooMuchIO);
135 }
136
137 used_inputs.sort_by(|a, b| a.1.amount.cmp(&b.1.amount)); let transaction = Transaction::new_transaction_now(
140 used_inputs
141 .iter()
142 .map(|input| TransactionInput {
143 transaction_id: input.0,
144 output_index: input.2,
145 signature: None,
146 output_owner: sender.to_public(),
147 })
148 .collect::<Vec<TransactionInput>>(),
149 receivers
150 .iter()
151 .map(|receiver| TransactionOutput {
152 amount: receiver.1,
153 receiver: receiver.0,
154 })
155 .collect(),
156 &mut vec![sender; used_inputs.len()],
157 )?;
158
159 Ok(transaction)
160}
161
162pub async fn build_block<B>(
167 blockchain_data_provider: &B,
168 transactions: &Vec<Transaction>,
169 miner: Public,
170) -> Result<Block, UtilError>
171where
172 B: BlockchainDataProvider,
173{
174 let reward = get_block_reward(blockchain_data_provider.get_height().await?);
175
176 let mut transactions = transactions.clone();
177
178 transactions.push(Transaction::new_transaction_now(
179 vec![],
180 vec![
181 TransactionOutput {
182 amount: calculate_dev_fee(reward),
183 receiver: DEV_WALLET,
184 },
185 TransactionOutput {
186 amount: reward - calculate_dev_fee(reward),
187 receiver: miner,
188 },
189 ],
190 &mut vec![],
191 )?);
192
193 if transactions.len() > MAX_TRANSACTIONS_PER_BLOCK {
194 return Err(UtilError::TooManyTransactions);
195 }
196 let reward_tx_i = transactions.len() - 1;
197 transactions[reward_tx_i].compute_pow(
198 &blockchain_data_provider
199 .get_transaction_difficulty()
200 .await?,
201 None,
202 )?;
203
204 let mut ids = vec![];
205 for tx in &transactions {
206 tx.check_completeness()?;
207 ids.push(tx.transaction_id.unwrap());
208 }
209 let merkle_tree = MerkleTree::build(&ids);
210
211 let filter = AddressInclusionFilter::create_filter(&transactions)?;
212
213 let previous_block = blockchain_data_provider
214 .get_block_hash_by_height(
215 blockchain_data_provider
216 .get_height()
217 .await?
218 .saturating_sub(1),
219 )
220 .await?
221 .unwrap_or(GENESIS_PREVIOUS_BLOCK_HASH);
222
223 let block = Block::new_block_now(
224 transactions,
225 &blockchain_data_provider.get_block_difficulty().await?,
226 &blockchain_data_provider
227 .get_transaction_difficulty()
228 .await?,
229 previous_block,
230 &merkle_tree.root_hash(),
231 filter,
232 );
233
234 Ok(block)
235}