1use crate::{
2 hashing::{
3 sighash::{calc_schnorr_signature_hash, SigHashReusedValues},
4 sighash_type::{SigHashType, SIG_HASH_ALL},
5 },
6 tx::{SignableTransaction, VerifiableTransaction},
7};
8use itertools::Itertools;
9use std::collections::BTreeMap;
10use std::iter::once;
11use thiserror::Error;
12
13#[derive(Error, Debug, Clone)]
14pub enum Error {
15 #[error("{0}")]
16 Message(String),
17
18 #[error("Secp256k1 -> {0}")]
19 Secp256k1Error(#[from] secp256k1::Error),
20
21 #[error("The transaction is partially signed")]
22 PartiallySigned,
23
24 #[error("The transaction is fully signed")]
25 FullySigned,
26}
27
28pub enum Signed {
31 Fully(SignableTransaction),
32 Partially(SignableTransaction),
33}
34
35impl Signed {
36 pub fn fully_signed(self) -> std::result::Result<SignableTransaction, Error> {
38 match self {
39 Signed::Fully(tx) => Ok(tx),
40 Signed::Partially(_) => Err(Error::PartiallySigned),
41 }
42 }
43
44 #[allow(clippy::result_large_err)]
47 pub fn try_fully_signed(self) -> std::result::Result<SignableTransaction, SignableTransaction> {
48 match self {
49 Signed::Fully(tx) => Ok(tx),
50 Signed::Partially(tx) => Err(tx),
51 }
52 }
53
54 pub fn partially_signed(self) -> std::result::Result<SignableTransaction, Error> {
56 match self {
57 Signed::Fully(_) => Err(Error::FullySigned),
58 Signed::Partially(tx) => Ok(tx),
59 }
60 }
61
62 #[allow(clippy::result_large_err)]
65 pub fn try_partially_signed(self) -> std::result::Result<SignableTransaction, SignableTransaction> {
66 match self {
67 Signed::Fully(tx) => Err(tx),
68 Signed::Partially(tx) => Ok(tx),
69 }
70 }
71
72 pub fn unwrap(self) -> SignableTransaction {
74 match self {
75 Signed::Fully(tx) => tx,
76 Signed::Partially(tx) => tx,
77 }
78 }
79}
80
81pub fn sign(mut signable_tx: SignableTransaction, schnorr_key: secp256k1::Keypair) -> SignableTransaction {
83 for i in 0..signable_tx.tx.inputs.len() {
84 signable_tx.tx.inputs[i].sig_op_count = 1;
85 }
86
87 let mut reused_values = SigHashReusedValues::new();
88 for i in 0..signable_tx.tx.inputs.len() {
89 let sig_hash = calc_schnorr_signature_hash(&signable_tx.as_verifiable(), i, SIG_HASH_ALL, &mut reused_values);
90 let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice()).unwrap();
91 let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref();
92 signable_tx.tx.inputs[i].signature_script = std::iter::once(65u8).chain(sig).chain([SIG_HASH_ALL.to_u8()]).collect();
94 }
95 signable_tx
96}
97
98pub fn sign_with_multiple(mut mutable_tx: SignableTransaction, privkeys: Vec<[u8; 32]>) -> SignableTransaction {
100 let mut map = BTreeMap::new();
101 for privkey in privkeys {
102 let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, &privkey).unwrap();
103 map.insert(schnorr_key.public_key().serialize(), schnorr_key);
104 }
105 for i in 0..mutable_tx.tx.inputs.len() {
106 mutable_tx.tx.inputs[i].sig_op_count = 1;
107 }
108
109 let mut reused_values = SigHashReusedValues::new();
110 for i in 0..mutable_tx.tx.inputs.len() {
111 let script = mutable_tx.entries[i].as_ref().unwrap().script_public_key.script();
112 if let Some(schnorr_key) = map.get(script) {
113 let sig_hash = calc_schnorr_signature_hash(&mutable_tx.as_verifiable(), i, SIG_HASH_ALL, &mut reused_values);
114 let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice()).unwrap();
115 let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref();
116 mutable_tx.tx.inputs[i].signature_script = std::iter::once(65u8).chain(sig).chain([SIG_HASH_ALL.to_u8()]).collect();
118 }
119 }
120 mutable_tx
121}
122
123#[allow(clippy::result_large_err)]
126pub fn sign_with_multiple_v2(mut mutable_tx: SignableTransaction, privkeys: &[[u8; 32]]) -> Signed {
127 let mut map = BTreeMap::new();
128 for privkey in privkeys {
129 let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, privkey).unwrap();
130 let schnorr_public_key = schnorr_key.public_key().x_only_public_key().0;
131 let script_pub_key_script = once(0x20).chain(schnorr_public_key.serialize().into_iter()).chain(once(0xac)).collect_vec();
132 map.insert(script_pub_key_script, schnorr_key);
133 }
134
135 let mut reused_values = SigHashReusedValues::new();
136 let mut additional_signatures_required = false;
137 for i in 0..mutable_tx.tx.inputs.len() {
138 let script = mutable_tx.entries[i].as_ref().unwrap().script_public_key.script();
139 if let Some(schnorr_key) = map.get(script) {
140 let sig_hash = calc_schnorr_signature_hash(&mutable_tx.as_verifiable(), i, SIG_HASH_ALL, &mut reused_values);
141 let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice()).unwrap();
142 let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref();
143 mutable_tx.tx.inputs[i].signature_script = std::iter::once(65u8).chain(sig).chain([SIG_HASH_ALL.to_u8()]).collect();
145 } else {
146 additional_signatures_required = true;
147 }
148 }
149 if additional_signatures_required {
150 Signed::Partially(mutable_tx)
151 } else {
152 Signed::Fully(mutable_tx)
153 }
154}
155
156pub fn sign_input(tx: &impl VerifiableTransaction, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Vec<u8> {
158 let mut reused_values = SigHashReusedValues::new();
159
160 let hash = calc_schnorr_signature_hash(tx, input_index, hash_type, &mut reused_values);
161 let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap();
162 let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key).unwrap();
163 let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref();
164
165 std::iter::once(65u8).chain(sig).chain([hash_type.to_u8()]).collect()
167}
168
169pub fn verify(tx: &impl VerifiableTransaction) -> Result<(), Error> {
170 let mut reused_values = SigHashReusedValues::new();
171 for (i, (input, entry)) in tx.populated_inputs().enumerate() {
172 if input.signature_script.is_empty() {
173 return Err(Error::Message(format!("Signature is empty for input: {i}")));
174 }
175 let pk = &entry.script_public_key.script()[1..33];
176 let pk = secp256k1::XOnlyPublicKey::from_slice(pk)?;
177 let sig = secp256k1::schnorr::Signature::from_slice(&input.signature_script[1..65])?;
178 let sig_hash = calc_schnorr_signature_hash(tx, i, SIG_HASH_ALL, &mut reused_values);
179 let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice())?;
180 sig.verify(&msg, &pk)?;
181 }
182
183 Ok(())
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use crate::{subnets::SubnetworkId, tx::*};
190 use secp256k1::{rand, Secp256k1};
191 use std::str::FromStr;
192
193 #[test]
194 fn test_and_verify_sign() {
195 let secp = Secp256k1::new();
196 let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
197 let script_pub_key = ScriptVec::from_slice(&public_key.serialize());
198
199 let (secret_key2, public_key2) = secp.generate_keypair(&mut rand::thread_rng());
200 let script_pub_key2 = ScriptVec::from_slice(&public_key2.serialize());
201
202 let prev_tx_id = TransactionId::from_str("880eb9819a31821d9d2399e2f35e2433b72637e393d71ecc9b8d0250f49153c3").unwrap();
203 let unsigned_tx = Transaction::new(
204 0,
205 vec![
206 TransactionInput {
207 previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 0 },
208 signature_script: vec![],
209 sequence: 0,
210 sig_op_count: 0,
211 },
212 TransactionInput {
213 previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 1 },
214 signature_script: vec![],
215 sequence: 1,
216 sig_op_count: 0,
217 },
218 TransactionInput {
219 previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 2 },
220 signature_script: vec![],
221 sequence: 2,
222 sig_op_count: 0,
223 },
224 ],
225 vec![
226 TransactionOutput { value: 300, script_public_key: ScriptPublicKey::new(0, script_pub_key.clone()) },
227 TransactionOutput { value: 300, script_public_key: ScriptPublicKey::new(0, script_pub_key.clone()) },
228 ],
229 1615462089000,
230 SubnetworkId::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
231 0,
232 vec![],
233 );
234
235 let entries = vec![
236 UtxoEntry {
237 amount: 100,
238 script_public_key: ScriptPublicKey::new(0, script_pub_key.clone()),
239 block_daa_score: 0,
240 is_coinbase: false,
241 },
242 UtxoEntry {
243 amount: 200,
244 script_public_key: ScriptPublicKey::new(0, script_pub_key),
245 block_daa_score: 0,
246 is_coinbase: false,
247 },
248 UtxoEntry {
249 amount: 300,
250 script_public_key: ScriptPublicKey::new(0, script_pub_key2),
251 block_daa_score: 0,
252 is_coinbase: false,
253 },
254 ];
255 let signed_tx = sign_with_multiple(
256 SignableTransaction::with_entries(unsigned_tx, entries),
257 vec![secret_key.secret_bytes(), secret_key2.secret_bytes()],
258 );
259
260 assert!(verify(&signed_tx.as_verifiable()).is_ok());
261 }
262}