dig_clvm/consensus/
validate.rs1use std::collections::HashSet;
7
8use chia_bls::BlsCache;
9use chia_consensus::allocator::make_allocator;
10use chia_consensus::flags::DONT_VALIDATE_SIGNATURE;
11use chia_consensus::owned_conditions::OwnedSpendBundleConditions;
12use chia_consensus::spendbundle_conditions::run_spendbundle;
13use chia_consensus::spendbundle_validation::validate_clvm_and_signature;
14use chia_protocol::{Bytes, Coin, SpendBundle};
15use clvmr::LIMIT_HEAP;
16
17use super::config::ValidationConfig;
18use super::context::ValidationContext;
19use super::error::ValidationError;
20use super::result::SpendResult;
21
22pub fn validate_spend_bundle(
31 bundle: &SpendBundle,
32 context: &ValidationContext,
33 config: &ValidationConfig,
34 _bls_cache: Option<&mut BlsCache>,
35) -> Result<SpendResult, ValidationError> {
36 let mut seen_coin_ids = HashSet::new();
40 for spend in &bundle.coin_spends {
41 let coin_id = spend.coin.coin_id();
42 if !seen_coin_ids.insert(coin_id) {
43 return Err(ValidationError::DoubleSpend(coin_id));
44 }
45 }
46
47 for spend in &bundle.coin_spends {
49 let coin_id = spend.coin.coin_id();
50 match context.coin_records.get(&coin_id) {
51 Some(record) => {
52 if record.spent {
53 return Err(ValidationError::AlreadySpent(coin_id));
54 }
55 }
56 None => {
57 if !context.ephemeral_coins.contains(&coin_id) {
58 return Err(ValidationError::CoinNotFound(coin_id));
59 }
60 }
61 }
62 }
63
64 let max_cost = config.max_cost_per_block;
74 let consensus = context.constants.consensus();
75
76 let conditions: OwnedSpendBundleConditions = if config.flags & DONT_VALIDATE_SIGNATURE != 0 {
77 let mut a = make_allocator(LIMIT_HEAP);
79 let (sbc, _pkm_pairs) = run_spendbundle(
80 &mut a,
81 bundle,
82 max_cost,
83 context.height,
84 config.flags,
85 consensus,
86 )
87 .map_err(|e| ValidationError::Clvm(format!("{:?}", e)))?;
88 OwnedSpendBundleConditions::from(&a, sbc)
89 } else if let Some(cache) = _bls_cache {
90 let mut a = make_allocator(LIMIT_HEAP);
94 let (sbc, pkm_pairs) = run_spendbundle(
95 &mut a,
96 bundle,
97 max_cost,
98 context.height,
99 config.flags,
100 consensus,
101 )
102 .map_err(|e| ValidationError::Clvm(format!("{:?}", e)))?;
103
104 let pks_msgs: Vec<(chia_bls::PublicKey, Bytes)> = pkm_pairs;
107 let sig_valid = cache.aggregate_verify(
108 pks_msgs.iter().map(|(pk, msg)| (pk, msg.as_ref())),
109 &bundle.aggregated_signature,
110 );
111 if !sig_valid {
112 return Err(ValidationError::SignatureFailed);
113 }
114
115 OwnedSpendBundleConditions::from(&a, sbc)
116 } else {
117 let (owned_conditions, _validation_pairs, _duration) =
120 validate_clvm_and_signature(bundle, max_cost, consensus, context.height)
121 .map_err(|e| ValidationError::Clvm(format!("{:?}", e)))?;
122 owned_conditions
123 };
124
125 if conditions.cost > config.max_cost_per_block {
127 return Err(ValidationError::CostExceeded {
128 limit: config.max_cost_per_block,
129 consumed: conditions.cost,
130 });
131 }
132
133 let removals: Vec<Coin> = bundle.coin_spends.iter().map(|cs| cs.coin).collect();
135
136 let additions: Vec<Coin> = conditions
137 .spends
138 .iter()
139 .flat_map(|spend| {
140 let parent_id = spend.coin_id;
141 spend
142 .create_coin
143 .iter()
144 .map(move |cc| Coin::new(parent_id, cc.0, cc.1))
145 })
146 .collect();
147
148 let total_input: u64 = removals.iter().map(|c| c.amount).sum();
150 let total_output: u64 = additions.iter().map(|c| c.amount).sum();
151
152 if total_input < total_output {
153 return Err(ValidationError::ConservationViolation {
154 input: total_input,
155 output: total_output,
156 });
157 }
158
159 let fee = total_input - total_output;
160
161 Ok(SpendResult {
162 additions,
163 removals,
164 fee,
165 conditions,
166 })
167}