1mod constants;
10mod explorer;
11mod runner;
12mod scripts;
13mod taproot_pubkey_gen;
14mod trackers;
15
16#[cfg(feature = "encoding")]
17pub mod encoding {
18 pub use bincode::{Decode, Encode};
19
20 pub trait Encodable {
21 fn encode(&self) -> anyhow::Result<Vec<u8>>
22 where
23 Self: Encode,
24 {
25 bincode::encode_to_vec(self, bincode::config::standard()).map_err(anyhow::Error::msg)
26 }
27
28 fn decode(buf: &[u8]) -> anyhow::Result<Self>
29 where
30 Self: Sized,
31 Self: Decode<()>,
32 {
33 Ok(bincode::decode_from_slice(buf, bincode::config::standard())?.0)
34 }
35
36 fn to_hex(&self) -> anyhow::Result<String>
37 where
38 Self: Encode,
39 {
40 Ok(hex::encode(Encodable::encode(self)?))
41 }
42
43 fn from_hex(hex: &str) -> anyhow::Result<Self>
44 where
45 Self: bincode::Decode<()>,
46 {
47 Encodable::decode(&hex::decode(hex).map_err(anyhow::Error::msg)?)
48 }
49 }
50}
51
52pub use constants::*;
53pub use explorer::*;
54pub use runner::*;
55pub use scripts::*;
56pub use taproot_pubkey_gen::*;
57pub use trackers::*;
58
59#[cfg(feature = "encoding")]
60pub use encoding::Encodable;
61
62use std::collections::HashMap;
63use std::sync::Arc;
64
65use simplicityhl::num::U256;
66use simplicityhl::simplicity::RedeemNode;
67use simplicityhl::simplicity::bitcoin::key::Keypair;
68use simplicityhl::simplicity::bitcoin::{XOnlyPublicKey, secp256k1};
69use simplicityhl::simplicity::elements::{Address, AddressParams, Transaction, TxInWitness, TxOut};
70use simplicityhl::simplicity::hashes::Hash;
71use simplicityhl::simplicity::jet::Elements;
72use simplicityhl::simplicity::jet::elements::{ElementsEnv, ElementsUtxo};
73use simplicityhl::str::WitnessName;
74use simplicityhl::value::ValueConstructible;
75use simplicityhl::{CompiledProgram, Value, elements};
76
77pub const P2PK_SOURCE: &str = include_str!("source_simf/p2pk.simf");
79
80pub fn get_p2pk_address(
82 x_only_public_key: &XOnlyPublicKey,
83 params: &'static AddressParams,
84) -> anyhow::Result<Address> {
85 Ok(create_p2tr_address(
86 get_p2pk_program(x_only_public_key)?.commit().cmr(),
87 x_only_public_key,
88 params,
89 ))
90}
91
92pub fn get_p2pk_program(account_public_key: &XOnlyPublicKey) -> anyhow::Result<CompiledProgram> {
94 let arguments = simplicityhl::Arguments::from(HashMap::from([(
95 WitnessName::from_str_unchecked("PUBLIC_KEY"),
96 Value::u256(U256::from_byte_array(account_public_key.serialize())),
97 )]));
98
99 load_program(P2PK_SOURCE, arguments)
100}
101
102pub fn execute_p2pk_program(
104 compiled_program: &CompiledProgram,
105 keypair: &Keypair,
106 env: ElementsEnv<Arc<Transaction>>,
107 runner_log_level: RunnerLogLevel,
108) -> anyhow::Result<Arc<RedeemNode<Elements>>> {
109 let sighash_all = secp256k1::Message::from_digest(env.c_tx_env().sighash_all().to_byte_array());
110
111 let witness_values = simplicityhl::WitnessValues::from(HashMap::from([(
112 WitnessName::from_str_unchecked("SIGNATURE"),
113 Value::byte_array(keypair.sign_schnorr(sighash_all).serialize()),
114 )]));
115
116 Ok(run_program(compiled_program, witness_values, env, runner_log_level)?.0)
117}
118
119pub fn finalize_p2pk_transaction(
124 mut tx: Transaction,
125 utxos: &[TxOut],
126 keypair: &Keypair,
127 input_index: usize,
128 params: &'static AddressParams,
129 genesis_hash: elements::BlockHash,
130) -> anyhow::Result<Transaction> {
131 let p2pk_program = get_p2pk_program(&keypair.x_only_public_key().0)?;
132
133 let cmr = p2pk_program.commit().cmr();
134
135 assert!(
136 utxos.len() > input_index,
137 "UTXOs must be greater than input index"
138 );
139
140 let target_utxo = &utxos[input_index];
141 let script_pubkey =
142 create_p2tr_address(cmr, &keypair.x_only_public_key().0, params).script_pubkey();
143
144 assert_eq!(
145 target_utxo.script_pubkey, script_pubkey,
146 "Expected for the UTXO to be spent by P2PK to be owned by the user."
147 );
148
149 let env: ElementsEnv<Arc<Transaction>> = ElementsEnv::new(
150 Arc::new(tx.clone()),
151 utxos
152 .iter()
153 .map(|utxo| ElementsUtxo {
154 script_pubkey: utxo.script_pubkey.clone(),
155 asset: utxo.asset,
156 value: utxo.value,
157 })
158 .collect(),
159 input_index as u32,
160 cmr,
161 control_block(cmr, keypair.x_only_public_key().0),
162 None,
163 genesis_hash,
164 );
165
166 let pruned = execute_p2pk_program(&p2pk_program, keypair, env, RunnerLogLevel::None)?;
167
168 let (simplicity_program_bytes, simplicity_witness_bytes) = pruned.to_vec_with_witness();
169 let cmr = pruned.cmr();
170
171 tx.input[input_index].witness = TxInWitness {
172 amount_rangeproof: None,
173 inflation_keys_rangeproof: None,
174 script_witness: vec![
175 simplicity_witness_bytes,
176 simplicity_program_bytes,
177 cmr.as_ref().to_vec(),
178 control_block(cmr, keypair.x_only_public_key().0).serialize(),
179 ],
180 pegin_witness: vec![],
181 };
182
183 Ok(tx)
184}