1use crate::error::{MultisigError, Result};
2use serde::{Deserialize, Serialize};
3use sha2::{Digest, Sha256};
4use wasm_bindgen::prelude::*;
5
6#[wasm_bindgen]
7#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
8pub struct PublicKey {
9 #[wasm_bindgen(skip)]
10 pub key: Vec<u8>,
11}
12
13#[wasm_bindgen]
14impl PublicKey {
15 #[wasm_bindgen(constructor)]
16 pub fn new(key: Vec<u8>) -> PublicKey {
17 PublicKey { key }
18 }
19 pub fn to_bytes(&self) -> Vec<u8> {
20 self.key.clone()
21 }
22}
23
24#[wasm_bindgen]
25#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
26pub struct PrivateKey {
27 #[wasm_bindgen(skip)]
28 pub key: Vec<u8>,
29}
30
31#[wasm_bindgen]
32impl PrivateKey {
33 #[wasm_bindgen(constructor)]
34 pub fn new(key: Vec<u8>) -> PrivateKey {
35 PrivateKey { key }
36 }
37 pub fn to_bytes(&self) -> Vec<u8> {
38 self.key.clone()
39 }
40}
41
42#[wasm_bindgen]
43#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
44pub struct TransactionOutput {
45 pub satoshis: u64,
46 #[wasm_bindgen(skip)]
47 pub locking_script: Vec<u8>,
48}
49
50#[wasm_bindgen]
51impl TransactionOutput {
52 #[wasm_bindgen(constructor)]
53 pub fn new(satoshis: u64, locking_script: Vec<u8>) -> TransactionOutput {
54 TransactionOutput {
55 satoshis,
56 locking_script,
57 }
58 }
59}
60
61#[wasm_bindgen]
62#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
63pub struct TransactionInput {
64 #[wasm_bindgen(skip)]
65 pub source_txid: String,
66 pub source_output_index: u32,
67 #[wasm_bindgen(skip)]
68 pub unlocking_script: Vec<u8>,
69 pub sequence: u32,
70 #[wasm_bindgen(skip)]
71 pub source_output: Option<TransactionOutput>,
72}
73
74#[wasm_bindgen]
75impl TransactionInput {
76 #[wasm_bindgen(constructor)]
77 pub fn new(source_txid: String, source_output_index: u32, sequence: u32) -> TransactionInput {
78 TransactionInput {
79 source_txid,
80 source_output_index,
81 unlocking_script: Vec::new(),
82 sequence,
83 source_output: None,
84 }
85 }
86}
87
88#[wasm_bindgen]
89#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
90pub struct Transaction {
91 pub version: u32,
92 #[wasm_bindgen(skip)]
93 pub inputs: Vec<TransactionInput>,
94 #[wasm_bindgen(skip)]
95 pub outputs: Vec<TransactionOutput>,
96 pub lock_time: u32,
97}
98
99#[wasm_bindgen]
100impl Transaction {
101 #[wasm_bindgen(constructor)]
102 pub fn new(
103 version: u32,
104 inputs: Vec<TransactionInput>,
105 outputs: Vec<TransactionOutput>,
106 lock_time: u32,
107 ) -> Transaction {
108 Transaction {
109 version,
110 inputs,
111 outputs,
112 lock_time,
113 }
114 }
115}
116
117impl Transaction {
118 pub fn serialize(&self) -> Result<Vec<u8>> {
119 let mut result = Vec::new();
120 result.extend_from_slice(&self.version.to_le_bytes());
121 result.extend(encode_varint(self.inputs.len() as u64));
122 for input in &self.inputs {
123 let mut txid = hex::decode(&input.source_txid)
124 .map_err(|_| MultisigError::TransactionError("Invalid source txid".to_string()))?;
125 if txid.len() != 32 {
126 return Err(MultisigError::TransactionError(
127 "Invalid source txid length".to_string(),
128 ));
129 }
130 txid.reverse();
131 result.extend(txid);
132 result.extend_from_slice(&input.source_output_index.to_le_bytes());
133 result.extend(encode_varint(input.unlocking_script.len() as u64));
134 result.extend(&input.unlocking_script);
135 result.extend_from_slice(&input.sequence.to_le_bytes());
136 }
137 result.extend(encode_varint(self.outputs.len() as u64));
138 for output in &self.outputs {
139 result.extend_from_slice(&output.satoshis.to_le_bytes());
140 result.extend(encode_varint(output.locking_script.len() as u64));
141 result.extend(&output.locking_script);
142 }
143 result.extend_from_slice(&self.lock_time.to_le_bytes());
144 Ok(result)
145 }
146
147 pub fn to_hex(&self) -> Result<String> {
148 Ok(hex::encode(self.serialize()?))
149 }
150
151 pub fn from_hex(value: &str) -> Result<Transaction> {
152 let bytes = hex::decode(value).map_err(|_| {
153 MultisigError::SerializationError("Invalid transaction hex".to_string())
154 })?;
155 let (transaction, offset) = Transaction::from_bytes(&bytes)?;
156 if offset != bytes.len() {
157 return Err(MultisigError::SerializationError(
158 "Trailing transaction bytes".to_string(),
159 ));
160 }
161 Ok(transaction)
162 }
163
164 pub fn from_bytes(bytes: &[u8]) -> Result<(Transaction, usize)> {
165 let mut cursor = Cursor { bytes, offset: 0 };
166 let version = cursor.read_u32()?;
167 let input_count = cursor.read_varint()? as usize;
168 let mut inputs = Vec::with_capacity(input_count);
169 for _ in 0..input_count {
170 let mut txid = cursor.read_exact(32)?.to_vec();
171 txid.reverse();
172 let source_output_index = cursor.read_u32()?;
173 let script_len = cursor.read_varint()? as usize;
174 let unlocking_script = cursor.read_exact(script_len)?.to_vec();
175 let sequence = cursor.read_u32()?;
176 inputs.push(TransactionInput {
177 source_txid: hex::encode(txid),
178 source_output_index,
179 unlocking_script,
180 sequence,
181 source_output: None,
182 });
183 }
184 let output_count = cursor.read_varint()? as usize;
185 let mut outputs = Vec::with_capacity(output_count);
186 for _ in 0..output_count {
187 let satoshis = cursor.read_u64()?;
188 let script_len = cursor.read_varint()? as usize;
189 let locking_script = cursor.read_exact(script_len)?.to_vec();
190 outputs.push(TransactionOutput {
191 satoshis,
192 locking_script,
193 });
194 }
195 let lock_time = cursor.read_u32()?;
196 Ok((
197 Transaction {
198 version,
199 inputs,
200 outputs,
201 lock_time,
202 },
203 cursor.offset,
204 ))
205 }
206
207 pub fn txid(&self) -> Result<String> {
208 let first = Sha256::digest(self.serialize()?);
209 let second = Sha256::digest(first);
210 Ok(hex::encode(
211 second.iter().rev().copied().collect::<Vec<u8>>(),
212 ))
213 }
214}
215
216#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
217pub struct Utxo {
218 pub txid: String,
219 pub vout: u32,
220 pub satoshis: u64,
221}
222
223#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
224pub struct MultisigConfig {
225 pub public_keys: Vec<PublicKey>,
226 pub m: usize,
227 pub sig_hash_type: u8,
228}
229
230#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
231pub struct Signature {
232 pub r: Vec<u8>,
233 pub s: Vec<u8>,
234 pub sighash_type: u8,
235}
236
237pub fn encode_varint(value: u64) -> Vec<u8> {
238 match value {
239 0..=0xfc => vec![value as u8],
240 0xfd..=0xffff => {
241 let mut v = vec![0xfd];
242 v.extend_from_slice(&(value as u16).to_le_bytes());
243 v
244 }
245 0x10000..=0xffff_ffff => {
246 let mut v = vec![0xfe];
247 v.extend_from_slice(&(value as u32).to_le_bytes());
248 v
249 }
250 _ => {
251 let mut v = vec![0xff];
252 v.extend_from_slice(&value.to_le_bytes());
253 v
254 }
255 }
256}
257
258struct Cursor<'a> {
259 bytes: &'a [u8],
260 offset: usize,
261}
262impl<'a> Cursor<'a> {
263 fn read_exact(&mut self, length: usize) -> Result<&'a [u8]> {
264 let end = self.offset.checked_add(length).ok_or_else(|| {
265 MultisigError::SerializationError("Transaction length overflow".to_string())
266 })?;
267 if end > self.bytes.len() {
268 return Err(MultisigError::SerializationError(
269 "Unexpected end of transaction".to_string(),
270 ));
271 }
272 let value = &self.bytes[self.offset..end];
273 self.offset = end;
274 Ok(value)
275 }
276 fn read_u32(&mut self) -> Result<u32> {
277 Ok(u32::from_le_bytes(self.read_exact(4)?.try_into().unwrap()))
278 }
279 fn read_u64(&mut self) -> Result<u64> {
280 Ok(u64::from_le_bytes(self.read_exact(8)?.try_into().unwrap()))
281 }
282 fn read_varint(&mut self) -> Result<u64> {
283 let prefix = self.read_exact(1)?[0];
284 match prefix {
285 0xfd => Ok(u16::from_le_bytes(self.read_exact(2)?.try_into().unwrap()) as u64),
286 0xfe => Ok(u32::from_le_bytes(self.read_exact(4)?.try_into().unwrap()) as u64),
287 0xff => Ok(u64::from_le_bytes(self.read_exact(8)?.try_into().unwrap())),
288 value => Ok(value as u64),
289 }
290 }
291}