|
1 | 1 | use bech32::Variant;
|
2 | 2 | use chia::{
|
3 |
| - protocol::{Bytes, Bytes32, SpendBundle}, |
| 3 | + clvm_utils::ToTreeHash, |
| 4 | + protocol::{Bytes, Bytes32, Coin, SpendBundle}, |
4 | 5 | traits::Streamable,
|
5 | 6 | };
|
| 7 | +use chia_puzzle_types::{ |
| 8 | + cat::CatArgs, |
| 9 | + offer::{NotarizedPayment, Payment}, |
| 10 | +}; |
| 11 | +use chia_puzzles::{CAT_PUZZLE_HASH, SETTLEMENT_PAYMENT_HASH}; |
6 | 12 | use chia_wallet_sdk::{
|
7 |
| - driver::{decompress_offer_bytes, DriverError, Launcher, OfferError}, |
8 |
| - types::Conditions, |
| 13 | + driver::{decompress_offer_bytes, Launcher, Puzzle}, |
| 14 | + types::{announcement_id, Condition, Conditions}, |
| 15 | + utils::Address, |
9 | 16 | };
|
10 | 17 | use clvm_traits::clvm_quote;
|
11 | 18 | use clvmr::{serde::node_from_bytes, NodePtr};
|
12 | 19 |
|
13 | 20 | use crate::{
|
14 | 21 | get_constants, get_latest_data_for_asset_id, hex_string_to_bytes32,
|
15 |
| - multisig_broadcast_thing_finish, multisig_broadcast_thing_start, CliError, MedievalVault, |
16 |
| - Verification, VerificationInfo, VerificationLauncherKVList, VerifiedData, |
| 22 | + multisig_broadcast_thing_finish, multisig_broadcast_thing_start, yes_no_prompt, CliError, |
| 23 | + MedievalVault, Verification, VerificationAsserter, VerificationLauncherKVList, VerifiedData, |
17 | 24 | };
|
18 | 25 |
|
19 | 26 | pub async fn verifications_broadcast_launch(
|
20 | 27 | launcher_id_str: String,
|
21 | 28 | asset_id_str: String,
|
22 | 29 | comment: String,
|
23 | 30 | request_offer: Option<String>,
|
| 31 | + request_offer_recipient: Option<String>, |
24 | 32 | signatures_str: String,
|
25 | 33 | testnet11: bool,
|
26 | 34 | fee_str: String,
|
27 | 35 | ) -> Result<(), CliError> {
|
28 | 36 | let launcher_id = hex_string_to_bytes32(&launcher_id_str)?;
|
29 | 37 | let asset_id = hex_string_to_bytes32(&asset_id_str)?;
|
30 | 38 |
|
31 |
| - let (signature_from_signers, pubkeys, client, mut ctx, medieval_vault) = |
| 39 | + let (mut signature_from_signers, pubkeys, client, mut ctx, medieval_vault) = |
32 | 40 | multisig_broadcast_thing_start(signatures_str, launcher_id_str, testnet11).await?;
|
33 | 41 |
|
34 | 42 | println!(
|
@@ -109,11 +117,112 @@ pub async fn verifications_broadcast_launch(
|
109 | 117 | }
|
110 | 118 | let spend_bundle = SpendBundle::from_bytes(&spend_bundle_bytes).unwrap();
|
111 | 119 |
|
112 |
| - // todo: find payment and send it to recipient |
113 |
| - // todo: find verification asserter |
114 |
| - // todo: spend verification asserter |
| 120 | + let verification_asserter = VerificationAsserter::from( |
| 121 | + launcher_id, |
| 122 | + verified_data.version, |
| 123 | + verified_data.asset_id.tree_hash(), |
| 124 | + verified_data.data_hash.tree_hash(), |
| 125 | + ); |
| 126 | + let verification_asserter_puzzle_hash: Bytes32 = verification_asserter.tree_hash().into(); |
| 127 | + |
| 128 | + let mut payment_sent = false; |
| 129 | + let mut verification_asserter_spent = false; |
| 130 | + let mut conds = Conditions::new(); |
| 131 | + for coin_spend in spend_bundle.coin_spends.into_iter() { |
| 132 | + let recipient_puzzle_hash = |
| 133 | + Address::decode(&request_offer_recipient.ok_or(CliError::Custom( |
| 134 | + "Verification offer provided but recipient not specified".to_string(), |
| 135 | + ))?)? |
| 136 | + .puzzle_hash; |
| 137 | + |
| 138 | + let puzzle_ptr = ctx.alloc(&coin_spend.puzzle_reveal)?; |
| 139 | + let solution_ptr = ctx.alloc(&coin_spend.solution)?; |
| 140 | + let output = ctx.run(puzzle_ptr, solution_ptr)?; |
| 141 | + let output = ctx.extract::<Conditions>(output)?; |
| 142 | + |
| 143 | + let puzzle = Puzzle::parse(&ctx, puzzle_ptr); |
| 144 | + match puzzle { |
| 145 | + Puzzle::Curried(puzzle) => { |
| 146 | + if puzzle.mod_hash == CAT_PUZZLE_HASH.into() { |
| 147 | + let payment_cat_asset_id = |
| 148 | + ctx.extract::<CatArgs<NodePtr>>(puzzle.args)?.asset_id; |
| 149 | + let offer_puzzle_hash: Bytes32 = CatArgs::curry_tree_hash( |
| 150 | + payment_cat_asset_id, |
| 151 | + SETTLEMENT_PAYMENT_HASH.into(), |
| 152 | + ) |
| 153 | + .into(); |
| 154 | + |
| 155 | + if let Some(cc) = output.iter().find_map(|c| match c { |
| 156 | + Condition::CreateCoin(cc) => { |
| 157 | + if cc.puzzle_hash == offer_puzzle_hash { |
| 158 | + Some(cc) |
| 159 | + } else { |
| 160 | + None |
| 161 | + } |
| 162 | + } |
| 163 | + _ => None, |
| 164 | + }) { |
| 165 | + yes_no_prompt(format!("{} CAT mojos (asset id: {}) will be transferred to the specified recipient. Continue?", cc.amount, hex::encode(payment_cat_asset_id)).as_str())?; |
| 166 | + let notarized_payment = NotarizedPayment { |
| 167 | + nonce: coin_spend.coin.coin_id(), |
| 168 | + payments: vec![Payment::with_memos( |
| 169 | + recipient_puzzle_hash, |
| 170 | + cc.amount, |
| 171 | + vec![recipient_puzzle_hash.into()], |
| 172 | + )], |
| 173 | + }; |
| 174 | + |
| 175 | + // todo: spend CAT |
| 176 | + |
| 177 | + payment_sent = true; |
| 178 | + let msg: Bytes32 = notarized_payment.tree_hash().into(); |
| 179 | + conds = conds.assert_puzzle_announcement(announcement_id( |
| 180 | + offer_puzzle_hash, |
| 181 | + &msg, |
| 182 | + )); |
| 183 | + } |
| 184 | + } |
| 185 | + } |
| 186 | + Puzzle::Raw(_puzzle) => { |
| 187 | + if let Some(cc) = output.iter().find_map(|c| match c { |
| 188 | + Condition::CreateCoin(cc) => { |
| 189 | + if cc.puzzle_hash == verification_asserter_puzzle_hash { |
| 190 | + Some(cc) |
| 191 | + } else { |
| 192 | + None |
| 193 | + } |
| 194 | + } |
| 195 | + _ => None, |
| 196 | + }) { |
| 197 | + verification_asserter.spend( |
| 198 | + &mut ctx, |
| 199 | + Coin::new(coin_spend.coin.coin_id(), cc.puzzle_hash, cc.amount), |
| 200 | + verifier_proof, |
| 201 | + 0, |
| 202 | + comment, |
| 203 | + ); |
| 204 | + verification_asserter_spent = true; |
| 205 | + } |
| 206 | + } |
| 207 | + }; |
| 208 | + |
| 209 | + ctx.insert(coin_spend); |
| 210 | + } |
| 211 | + |
| 212 | + if !payment_sent { |
| 213 | + return Err(CliError::Custom( |
| 214 | + "Payment in offer could not be found".to_string(), |
| 215 | + )); |
| 216 | + } |
| 217 | + if !verification_asserter_spent { |
| 218 | + return Err(CliError::Custom( |
| 219 | + "Verification asserter could not be found in offer - it is likely invalid" |
| 220 | + .to_string(), |
| 221 | + )); |
| 222 | + } |
115 | 223 |
|
116 |
| - Some(Conditions::new()) // todo |
| 224 | + signature_from_signers += &spend_bundle.aggregated_signature; |
| 225 | + Some(conds) |
117 | 226 | } else {
|
118 | 227 | None
|
119 | 228 | };
|
|
0 commit comments