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