1#[macro_use]
23extern crate amplify;
24
25#[cfg(feature = "bp")]
26mod bp;
27#[cfg(feature = "bp")]
28pub mod bp_conversion_utils;
29mod rb;
30
31use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
32use std::str::FromStr;
33
34use amplify::confinement::{Confined, NonEmptyOrdMap, SmallVec, U16, U24, U32};
35use amplify::num::u5;
36use amplify::{confinement, FromSliceError, Wrapper};
37use rgbstd::bitcoin::bip32::DerivationPath;
38use rgbstd::bitcoin::key::UntweakedPublicKey;
39use rgbstd::bitcoin::{ScriptBuf, Transaction};
40use rgbstd::containers::{Batch, Fascia, PubWitness, SealWitness};
41use rgbstd::opret::OpretProof;
42use rgbstd::rgbcore::commit_verify::mpc::{
43 self, Commitment, Message, ProtocolId, MPC_MINIMAL_DEPTH,
44};
45use rgbstd::rgbcore::commit_verify::{CommitId, TryCommitVerify};
46use rgbstd::tapret::{TapretCommitment, TapretPathProof, TapretProof};
47use rgbstd::txout::CloseMethod;
48use rgbstd::{
49 AssignmentType, ContractId, KnownTransition, MergeReveal, MergeRevealError, OpId, Operation,
50 Opout, Proof, Transition, TransitionBundle, Txid,
51};
52use strict_encoding::{DeserializeError, StrictDeserialize, StrictSerialize};
53
54pub const PSBT_MPC_PREFIX: [u8; 3] = [77, 80, 67];
63pub const PSBT_MPC_PREFIX_STR: &str = "MPC";
64pub const PSBT_OUT_MPC_MESSAGE: u8 = 0x00;
67pub const PSBT_OUT_MPC_ENTROPY: u8 = 0x01;
69pub const PSBT_OUT_MPC_MIN_TREE_DEPTH: u8 = 0x04;
72pub const PSBT_OUT_MPC_COMMITMENT: u8 = 0x10;
74pub const PSBT_OUT_MPC_PROOF: u8 = 0x11;
76
77pub const PSBT_OPRET_PREFIX: [u8; 5] = [79, 80, 82, 69, 84];
79pub const PSBT_OPRET_PREFIX_STR: &str = "OPRET";
80pub const PSBT_OUT_OPRET_HOST: u8 = 0x00;
83pub const PSBT_OUT_OPRET_COMMITMENT: u8 = 0x01;
86
87pub const PSBT_TAPRET_PREFIX: [u8; 5] = [84, 65, 82, 69, 84];
89pub const PSBT_TAPRET_PREFIX_STR: &str = "TAPRET";
90pub const PSBT_OUT_TAPRET_HOST: u8 = 0x00;
93pub const PSBT_OUT_TAPRET_COMMITMENT: u8 = 0x01;
96pub const PSBT_OUT_TAPRET_PROOF: u8 = 0x02;
99
100pub const PSBT_RGB_PREFIX: [u8; 3] = [82, 71, 66];
102pub const PSBT_RGB_PREFIX_STR: &str = "RGB";
103pub const PSBT_GLOBAL_RGB_TRANSITION: u8 = 0x01;
105pub const PSBT_GLOBAL_RGB_CLOSE_METHOD: u8 = 0x02;
108pub const PSBT_GLOBAL_RGB_TAP_HOST_CHANGE: u8 = 0x03;
110pub const PSBT_GLOBAL_RGB_CONSUMED_BY: u8 = 0x04;
113
114#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
116#[display(doc_comments)]
117pub enum MpcPsbtError {
118 #[from(FromSliceError)]
120 InvalidKeyValue,
121
122 #[from]
124 MessageMapTooLarge(confinement::Error),
125
126 KeyAlreadyPresent,
128
129 OutputAlreadyHasCommitment,
132
133 #[from]
134 #[display(inner)]
135 Mpc(mpc::Error),
136
137 Finalized,
139}
140
141#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)]
143#[display(doc_comments)]
144pub enum OpretKeyError {
145 OutputAlreadyHasCommitment,
148
149 NonOpReturnOutput,
152
153 OpretProhibited,
156
157 NoCommitment,
159
160 InvalidCommitment,
162
163 InvalidOpReturnScript,
165}
166
167#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error, From)]
169#[display(doc_comments)]
170pub enum TapretKeyError {
171 OutputAlreadyHasCommitment,
174
175 TapretProhibited,
178
179 NotTaprootOutput,
182
183 NoCommitment,
185
186 InvalidCommitment,
188
189 TapTreeNonEmpty,
191
192 NoInternalKey,
194}
195
196#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
198#[display(doc_comments)]
199pub enum RgbPsbtError {
200 DoubleSpend,
202
203 UnrelatedTransitions(OpId, OpId, MergeRevealError),
206
207 NoContracts,
209
210 NoContractConsumers,
212
213 InvalidTransitionsNumber(ContractId, usize),
215
216 InvalidInputsNumber(usize),
218
219 #[from(FromSliceError)]
221 InvalidContractId,
222
223 InvalidOpoutAndOpidsData(String),
225
226 KnownTransitionsInconsistency,
228
229 NoCloseMethod,
231
232 InvalidCloseMethod,
234
235 NoHostOutput(CloseMethod),
237
238 TooManyContracts,
240
241 #[from(confinement::Error)]
243 TooManyTransitionsInBundle,
244
245 TransitionTooBig(OpId),
247
248 #[from]
250 InvalidTransition(DeserializeError),
251
252 #[from]
254 #[display(inner)]
255 Mpc(MpcPsbtError),
256}
257
258#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
259#[display(doc_comments)]
260pub enum EmbedError {
261 #[from]
262 Rgb(RgbPsbtError),
263}
264
265#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
267#[display(doc_comments)]
268pub enum DbcPsbtError {
269 NoHostOutput,
271
272 NoProperOutput(CloseMethod),
274
275 AlreadyPresent,
277
278 TxOutputsModifiable,
281
282 #[from]
284 #[display(inner)]
285 Mpc(MpcPsbtError),
286
287 #[from]
289 #[display(inner)]
290 Tapret(TapretKeyError),
291
292 #[from]
294 #[display(inner)]
295 Opret(OpretKeyError),
296}
297
298#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
299#[display(inner)]
300pub enum CommitError {
301 #[from]
302 Rgb(RgbPsbtError),
303
304 #[from]
305 Dbc(DbcPsbtError),
306}
307
308#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)]
309#[display("&{keychain}/{index}")]
310pub struct Terminal {
311 pub keychain: u8,
312 pub index: u32,
313}
314
315impl Terminal {
316 pub fn new(keychain: u8, index: u32) -> Self { Self { keychain, index } }
317
318 pub fn from_derivation_path(derivation_path: &DerivationPath) -> Option<Self> {
319 let mut path = derivation_path.to_u32_vec();
320 path.reverse();
321 let index = path.pop()?;
322 let keychain = u8::try_from(path.pop()?).ok()?;
323 Some(Self::new(keychain, index))
324 }
325}
326
327#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
328#[display(doc_comments)]
329pub enum TerminalParseError {
330 NoKeychain,
332
333 #[from]
335 InvalidTerminal(std::num::ParseIntError),
336
337 InvalidComponents(String),
340}
341
342impl FromStr for Terminal {
343 type Err = TerminalParseError;
344
345 fn from_str(s: &str) -> Result<Self, Self::Err> {
346 let mut iter = s.split('/');
347 match (iter.next(), iter.next(), iter.next()) {
348 (Some(keychain), Some(index), None) => {
349 if !keychain.starts_with('&') {
350 return Err(TerminalParseError::NoKeychain);
351 }
352 Ok(Terminal::new(u8::from_str(keychain.trim_start_matches('&'))?, index.parse()?))
353 }
354 _ => Err(TerminalParseError::InvalidComponents(s.to_owned())),
355 }
356 }
357}
358
359#[cfg(feature = "serde")]
360mod _serde {
361 use serde_crate::de::Error;
362 use serde_crate::{Deserialize, Deserializer, Serialize, Serializer};
363
364 use super::*;
365
366 impl Serialize for Terminal {
367 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
368 where S: Serializer {
369 if serializer.is_human_readable() {
370 self.to_string().serialize(serializer)
371 } else {
372 let tuple = (self.keychain, self.index);
373 tuple.serialize(serializer)
374 }
375 }
376 }
377
378 impl<'de> Deserialize<'de> for Terminal {
379 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
380 where D: Deserializer<'de> {
381 if deserializer.is_human_readable() {
382 let s = String::deserialize(deserializer)?;
383 Self::from_str(&s).map_err(D::Error::custom)
384 } else {
385 let d = <(u8, u32)>::deserialize(deserializer)?;
386 Ok(Self {
387 keychain: d.0,
388 index: d.1,
389 })
390 }
391 }
392 }
393}
394
395pub trait DbcPsbtProof: Proof {
396 const METHOD: CloseMethod;
397 fn dbc_commit<P: RgbPropKeyExt, O: RgbOutExt<P>>(
398 psbt: &mut impl RgbPsbtExt<P, O>,
399 ) -> Result<(mpc::MerkleBlock, Self), DbcPsbtError>;
400}
401
402impl DbcPsbtProof for OpretProof {
403 const METHOD: CloseMethod = CloseMethod::OpretFirst;
404
405 fn dbc_commit<P: RgbPropKeyExt, O: RgbOutExt<P>>(
406 psbt: &mut impl RgbPsbtExt<P, O>,
407 ) -> Result<(mpc::MerkleBlock, Self), DbcPsbtError> {
408 let (idx, output) = psbt
409 .dbc_output_mut::<Self>()
410 .ok_or(DbcPsbtError::NoProperOutput(Self::METHOD))?;
411
412 let (commitment, mpc_proof) = output.mpc_commit()?;
413 output.opret_commit(commitment)?;
414
415 psbt.set_opret_commitment(idx);
416
417 Ok((mpc_proof, OpretProof::default()))
418 }
419}
420
421impl DbcPsbtProof for TapretProof {
422 const METHOD: CloseMethod = CloseMethod::TapretFirst;
423
424 fn dbc_commit<P: RgbPropKeyExt, O: RgbOutExt<P>>(
425 psbt: &mut impl RgbPsbtExt<P, O>,
426 ) -> Result<(mpc::MerkleBlock, Self), DbcPsbtError> {
427 let (idx, output) = psbt
428 .dbc_output_mut::<Self>()
429 .ok_or(DbcPsbtError::NoProperOutput(Self::METHOD))?;
430
431 let (commitment, mpc_proof) = output.mpc_commit()?;
432 let tapret_proof = output.tapret_commit(commitment)?;
433
434 psbt.set_tapret_commitment(idx);
435
436 Ok((mpc_proof, tapret_proof))
437 }
438}
439
440#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Debug, From)]
441#[wrapper(Deref)]
442#[wrapper_mut(DerefMut)]
443pub struct OpoutAndOpids(BTreeMap<Opout, OpId>);
444
445impl OpoutAndOpids {
446 pub fn new(items: BTreeMap<Opout, OpId>) -> Self { Self(items) }
447
448 pub fn serialize(&self) -> Vec<u8> {
449 let mut bytes = Vec::new();
450 for (opout, opid) in &self.0 {
451 bytes.extend(opout.op.to_byte_array());
452 bytes.extend(opout.ty.to_le_bytes());
453 bytes.extend(opout.no.to_le_bytes());
454 bytes.extend(opid.to_byte_array());
455 }
456 bytes
457 }
458
459 #[allow(clippy::result_large_err)]
460 pub fn deserialize(bytes: &[u8]) -> Result<Self, RgbPsbtError> {
461 let opid_size = std::mem::size_of::<OpId>();
462 let assignment_type_size = std::mem::size_of::<u16>();
463 let u16_size = std::mem::size_of::<u16>();
464 let item_size = opid_size + assignment_type_size + u16_size + opid_size;
465 let bytes_len = bytes.len();
466 if bytes_len % item_size != 0 {
467 return Err(RgbPsbtError::InvalidOpoutAndOpidsData(format!(
468 "Input data length {bytes_len} is not a multiple of {item_size}"
469 )));
470 }
471 let mut items = BTreeMap::new();
472 for chunk in bytes.chunks_exact(item_size) {
473 let mut cursor = 0;
474 let op = OpId::copy_from_slice(&chunk[cursor..cursor + opid_size]).map_err(|e| {
475 RgbPsbtError::InvalidOpoutAndOpidsData(format!(
476 "Error deserializing Opout.op: {e:?}",
477 ))
478 })?;
479 cursor += opid_size;
480 let ty_bytes = &chunk[cursor..cursor + assignment_type_size];
481 let ty_u16 = u16::from_le_bytes([ty_bytes[0], ty_bytes[1]]);
482 let ty = AssignmentType::with(ty_u16);
483 cursor += assignment_type_size;
484 let no_bytes = &chunk[cursor..cursor + u16_size];
485 let no = u16::from_le_bytes([no_bytes[0], no_bytes[1]]);
486 cursor += u16_size;
487 let opid = OpId::copy_from_slice(&chunk[cursor..cursor + opid_size]).map_err(|e| {
488 RgbPsbtError::InvalidOpoutAndOpidsData(format!(
489 "Error deserializing consuming OpId: {e:?}"
490 ))
491 })?;
492 let opout = Opout::new(op, ty, no);
493 items.insert(opout, opid);
494 }
495 Ok(OpoutAndOpids::new(items))
496 }
497}
498
499#[allow(clippy::result_large_err)]
500fn insert_transitions_sorted(
501 transitions: &HashMap<OpId, Transition>,
502 known_transitions: &mut SmallVec<KnownTransition>,
503) -> Result<(), RgbPsbtError> {
504 #[allow(clippy::result_large_err)]
505 fn visit_and_insert(
506 opid: OpId,
507 transitions: &HashMap<OpId, Transition>,
508 known_transitions: &mut SmallVec<KnownTransition>,
509 visited: &mut HashSet<OpId>,
510 visiting: &mut HashSet<OpId>,
511 ) -> Result<(), RgbPsbtError> {
512 if visited.contains(&opid) {
513 return Ok(());
514 }
515 if visiting.contains(&opid) {
516 return Err(RgbPsbtError::KnownTransitionsInconsistency);
517 }
518 if let Some(transition) = transitions.get(&opid) {
519 visiting.insert(opid);
520 for input in transition.inputs() {
521 if transitions.contains_key(&input.op) {
522 visit_and_insert(input.op, transitions, known_transitions, visited, visiting)?;
523 }
524 }
525 visiting.remove(&opid);
526 visited.insert(opid);
527 known_transitions
528 .push(KnownTransition {
529 opid,
530 transition: transition.clone(),
531 })
532 .map_err(|_| {
533 RgbPsbtError::InvalidTransitionsNumber(
534 transition.contract_id,
535 transitions.len(),
536 )
537 })?;
538 }
539 Ok(())
540 }
541
542 let mut visited = HashSet::new();
543 let mut visiting = HashSet::new();
544 for &opid in transitions.keys() {
545 visit_and_insert(opid, transitions, known_transitions, &mut visited, &mut visiting)?;
546 }
547 Ok(())
548}
549
550pub trait RgbPropKeyExt {
552 fn mpc_message(protocol_id: ProtocolId) -> Self;
554 fn mpc_entropy() -> Self;
556 fn mpc_min_tree_depth() -> Self;
558 fn mpc_commitment() -> Self;
560 fn mpc_proof() -> Self;
562 fn opret_host() -> Self;
564 fn tapret_host() -> Self;
566 fn opret_commitment() -> Self;
568 fn tapret_commitment() -> Self;
570 fn tapret_proof() -> Self;
572 fn rgb_transition(opid: OpId) -> Self;
574 fn rgb_close_method() -> Self;
576 fn rgb_consumed_by(contract_id: ContractId) -> Self;
578 fn rgb_tapret_host_on_change() -> Self;
580}
581
582pub trait RgbOutExt<P: RgbPropKeyExt> {
583 fn is_opret_host(&self) -> bool { self.proprietary_contains(&P::opret_host()) }
584
585 fn is_tapret_host(&self) -> bool { self.proprietary_contains(&P::tapret_host()) }
586
587 fn set_opret_host(&mut self) -> bool { self.proprietary_push(P::opret_host(), vec![]).is_err() }
588
589 fn set_tapret_host(&mut self) -> bool {
590 self.proprietary_push(P::tapret_host(), vec![]).is_err()
591 }
592
593 fn tapret_commitment(&self) -> Result<TapretCommitment, TapretKeyError> {
597 let data = self
598 .proprietary_get(&P::tapret_commitment())
599 .ok_or(TapretKeyError::NoCommitment)?;
600 TapretCommitment::from_strict_serialized::<U16>(
601 Confined::try_from(data.to_vec()).map_err(|_| TapretKeyError::InvalidCommitment)?,
602 )
603 .map_err(|_| TapretKeyError::InvalidCommitment)
604 }
605
606 fn opret_commit(&mut self, commitment: mpc::Commitment) -> Result<(), OpretKeyError> {
619 if !self.is_opret_host() {
620 return Err(OpretKeyError::OpretProhibited);
621 }
622 self.proprietary_push(P::opret_commitment(), commitment.to_vec())
623 .map_err(|_| OpretKeyError::OutputAlreadyHasCommitment)?;
624 self.proprietary_remove(&P::opret_host());
625 Ok(())
626 }
627
628 fn get_internal_pk(&self) -> Option<UntweakedPublicKey>;
629
630 fn is_tap_tree_empty(&self) -> bool;
631
632 fn set_tap_tree(&mut self, script_commitment: &ScriptBuf);
633
634 fn tapret_commit(
643 &mut self,
644 commitment: mpc::Commitment,
645 ) -> Result<TapretProof, TapretKeyError> {
646 if !self.is_tapret_host() {
647 return Err(TapretKeyError::TapretProhibited);
648 }
649
650 if !self.is_tap_tree_empty() {
652 return Err(TapretKeyError::TapTreeNonEmpty);
653 }
654 let nonce = 0;
655 let tapret_commitment = &TapretCommitment::with(commitment, nonce);
656 let script_commitment = tapret_commitment.commit();
657
658 self.set_tap_tree(&script_commitment);
659
660 let internal_pk = self
661 .get_internal_pk()
662 .ok_or(TapretKeyError::NoInternalKey)?;
663 let tapret_proof = TapretProof {
664 path_proof: TapretPathProof::root(nonce),
665 internal_pk,
666 };
667
668 let tapret_proof_serialized = tapret_proof
669 .to_strict_serialized::<U16>()
670 .expect("tapret proof too long")
671 .to_vec();
672 self.proprietary_push(P::tapret_commitment(), tapret_commitment.to_vec())
673 .and_then(|_| self.proprietary_push(P::tapret_proof(), tapret_proof_serialized))
674 .map_err(|_| TapretKeyError::OutputAlreadyHasCommitment)?;
675 self.proprietary_remove(&P::tapret_host());
676
677 Ok(tapret_proof)
678 }
679
680 fn bip32_derivation_terminals(&self) -> Vec<Terminal>;
681
682 fn tap_bip32_derivation_terminals(&self) -> Vec<Terminal>;
683
684 fn terminal_derivation(&self) -> Option<Terminal> {
685 let terminal = self
686 .bip32_derivation_terminals()
687 .into_iter()
688 .chain(self.tap_bip32_derivation_terminals())
689 .collect::<BTreeSet<_>>();
690 if terminal.len() != 1 {
691 return None;
692 }
693 terminal.first().copied()
694 }
695
696 fn proprietary_mpc_messages<'a>(&'a self) -> impl Iterator<Item = (&'a [u8], &'a [u8])> + 'a;
697
698 fn mpc_message_map(&self) -> Result<mpc::MessageMap, MpcPsbtError> {
700 let map = self
701 .proprietary_mpc_messages()
702 .map(|(protocol_id_bytes, message_bytes)| {
703 Ok((
704 ProtocolId::copy_from_slice(protocol_id_bytes)?,
705 Message::copy_from_slice(message_bytes)?,
706 ))
707 })
708 .collect::<Result<BTreeMap<_, _>, MpcPsbtError>>()?;
709 Confined::try_from(map).map_err(MpcPsbtError::from)
710 }
711
712 fn mpc_entropy(&self) -> Option<u64> {
719 let key = P::mpc_entropy();
720 let data = self.proprietary_get(&key)?;
721 if data.len() != 8 {
722 return None;
723 }
724 let mut buf = [0u8; 8];
725 buf.copy_from_slice(data);
726 Some(u64::from_le_bytes(buf))
727 }
728
729 fn mpc_min_tree_depth(&self) -> Option<u8> {
730 let key = P::mpc_min_tree_depth();
731 let data = self.proprietary_get(&key)?;
732 if data.len() != 1 {
733 return None;
734 }
735 Some(data[0])
736 }
737
738 fn set_mpc_entropy(&mut self, entropy: u64) -> Result<bool, MpcPsbtError> {
750 if self.proprietary_contains(&P::mpc_commitment()) {
751 return Err(MpcPsbtError::Finalized);
752 }
753 let key = P::mpc_entropy();
754 let val = entropy.to_le_bytes().to_vec();
755 if let Some(v) = self.proprietary_get(&key) {
756 if v != val {
757 return Err(MpcPsbtError::InvalidKeyValue);
758 }
759 return Ok(false);
760 }
761 self.proprietary_push(key, val)?;
762 Ok(true)
763 }
764
765 fn set_mpc_message(
777 &mut self,
778 protocol_id: ProtocolId,
779 message: Message,
780 ) -> Result<bool, MpcPsbtError> {
781 if self.proprietary_contains(&P::mpc_commitment()) {
782 return Err(MpcPsbtError::Finalized);
783 }
784 let key = P::mpc_message(protocol_id);
785 let val = message.to_vec();
786 if let Some(v) = self.proprietary_get(&key) {
787 if v != val {
788 return Err(MpcPsbtError::InvalidKeyValue);
789 }
790 return Ok(false);
791 }
792 self.proprietary_push(key, val)?;
793 Ok(true)
794 }
795
796 fn mpc_commit(&mut self) -> Result<(Commitment, mpc::MerkleBlock), MpcPsbtError> {
797 let messages = self.mpc_message_map()?;
798 let min_depth = self
799 .mpc_min_tree_depth()
800 .map(u5::with)
801 .unwrap_or(MPC_MINIMAL_DEPTH);
802 let source = mpc::MultiSource {
803 min_depth,
804 messages,
805 static_entropy: self.mpc_entropy(),
806 };
807 let merkle_tree = mpc::MerkleTree::try_commit(&source)?;
808 let entropy = merkle_tree.entropy();
809 self.set_mpc_entropy(entropy)?;
810 let commitment = merkle_tree.commit_id();
811 let mpc_proof = mpc::MerkleBlock::from(merkle_tree);
812 let mpc_proof_serialized = mpc_proof.to_strict_serialized::<U32>().expect("max length");
813
814 self.proprietary_push(P::mpc_commitment(), commitment.to_vec())
815 .and_then(|_| {
816 self.proprietary_push(P::mpc_proof(), mpc_proof_serialized.to_unconfined())
817 })
818 .map_err(|_| MpcPsbtError::OutputAlreadyHasCommitment)?;
819
820 Ok((commitment, mpc_proof))
821 }
822
823 fn proprietary_push(&mut self, key: P, value: Vec<u8>) -> Result<(), MpcPsbtError> {
824 if self.proprietary_contains(&key) {
825 return Err(MpcPsbtError::KeyAlreadyPresent);
826 }
827 self.proprietary_insert(key, value);
828 Ok(())
829 }
830
831 fn proprietary_insert(&mut self, key: P, value: Vec<u8>);
832
833 fn proprietary_contains_key(&self, key: &P) -> bool;
834
835 fn proprietary_contains(&self, key: &P) -> bool { self.proprietary_contains_key(key) }
836
837 fn proprietary_get_value(&self, key: &P) -> Option<&[u8]>;
838
839 fn proprietary_get(&self, key: &P) -> Option<&[u8]> { self.proprietary_get_value(key) }
840
841 fn proprietary_remove(&mut self, key: &P);
842}
843
844#[allow(clippy::result_large_err)]
845pub trait RgbPsbtExt<P: RgbPropKeyExt, O: RgbOutExt<P>> {
846 fn get_txid(&self) -> Txid;
847
848 fn modifiable_outputs(&self) -> bool;
849
850 fn set_as_unmodifiable(&mut self);
851
852 fn unsigned_tx(&self) -> Transaction;
853
854 fn set_opret_host(&mut self) -> bool;
855
856 fn dbc_output<D: DbcPsbtProof>(&self) -> Option<&O>;
857
858 fn dbc_output_mut<D: DbcPsbtProof>(&mut self) -> Option<(usize, &mut O)>;
859
860 fn dbc_commit<D: DbcPsbtProof>(&mut self) -> Result<(mpc::MerkleBlock, D), DbcPsbtError>
861 where Self: std::marker::Sized {
862 if self.modifiable_outputs() {
863 return Err(DbcPsbtError::TxOutputsModifiable);
864 }
865
866 D::dbc_commit(self)
867 }
868
869 fn set_opret_commitment(&mut self, idx: usize);
870
871 fn set_tapret_commitment(&mut self, idx: usize);
872
873 fn rgb_embed(&mut self, batch: Batch) -> Result<(), EmbedError> {
874 for transition in batch {
875 self.push_rgb_transition(transition)?;
876 }
877 Ok(())
878 }
879
880 fn rgb_commit(&mut self) -> Result<Fascia, CommitError>
881 where Self: std::marker::Sized {
882 let bundles = self.rgb_bundles_to_mpc()?;
885 let close_method = self
887 .rgb_close_method()?
888 .ok_or(RgbPsbtError::NoCloseMethod)?;
889 let (merkle_block, dbc_proof) = match close_method {
890 CloseMethod::TapretFirst => self
891 .dbc_commit::<TapretProof>()
892 .map(|(mb, proof)| (mb, proof.into()))?,
893 CloseMethod::OpretFirst => self
894 .dbc_commit::<OpretProof>()
895 .map(|(mb, proof)| (mb, proof.into()))?,
896 };
897 let witness = PubWitness::with(self.unsigned_tx());
898 let seal_witness = SealWitness::new(witness, merkle_block, dbc_proof);
899 Ok(Fascia {
900 seal_witness,
901 bundles,
902 })
903 }
904
905 fn proprietary_rgb_contract_consumer_keys<'a>(&'a self) -> impl Iterator<Item = &'a [u8]> + 'a;
906
907 fn rgb_contract_ids(&self) -> Result<BTreeSet<ContractId>, FromSliceError> {
908 self.proprietary_rgb_contract_consumer_keys()
909 .map(ContractId::copy_from_slice)
910 .collect()
911 }
912
913 fn rgb_contract_consumers(
914 &self,
915 contract_id: ContractId,
916 ) -> Result<BTreeMap<Opout, OpId>, RgbPsbtError> {
917 let Some(data) = self.proprietary_get(&P::rgb_consumed_by(contract_id)) else {
918 return Ok(BTreeMap::new());
919 };
920 Ok(OpoutAndOpids::deserialize(data)?.into_inner())
921 }
922
923 fn rgb_transition(&self, opid: OpId) -> Result<Option<Transition>, RgbPsbtError> {
924 let Some(data) = self.proprietary_get(&P::rgb_transition(opid)) else {
925 return Ok(None);
926 };
927 let data = Confined::try_from_iter(data.iter().copied())?;
928 let transition = Transition::from_strict_serialized::<U24>(data)?;
929 Ok(Some(transition))
930 }
931
932 fn rgb_close_method(&self) -> Result<Option<CloseMethod>, RgbPsbtError> {
933 let Some(m) = self.proprietary_get(&P::rgb_close_method()) else {
934 return Ok(None);
935 };
936 if m.len() == 1 {
937 if let Ok(method) = CloseMethod::try_from(m[0]) {
938 return Ok(Some(method));
939 }
940 }
941 Err(RgbPsbtError::InvalidCloseMethod)
942 }
943
944 fn rgb_tapret_host_on_change(&self) -> bool {
945 self.proprietary_contains(&P::rgb_tapret_host_on_change())
946 }
947
948 fn set_rgb_close_method(&mut self, close_method: CloseMethod) {
949 let _ = self.proprietary_push(P::rgb_close_method(), vec![close_method as u8]);
950 }
951
952 fn set_rgb_tapret_host_on_change(&mut self) {
953 let _ = self.proprietary_push(P::rgb_tapret_host_on_change(), vec![]);
954 }
955
956 fn set_rgb_contract_consumer(
969 &mut self,
970 contract_id: ContractId,
971 opout: Opout,
972 opid: OpId,
973 ) -> Result<bool, RgbPsbtError> {
974 let key = P::rgb_consumed_by(contract_id);
975 if let Some(existing_data) = self.proprietary_get(&key) {
976 let mut items = OpoutAndOpids::deserialize(existing_data)?;
977 if let Some(existing_opid) = items.get(&opout) {
978 if *existing_opid != opid {
979 return Err(RgbPsbtError::DoubleSpend);
980 }
981 return Ok(false);
982 }
983 items.insert(opout, opid);
984 self.proprietary_insert(key, items.serialize());
985 } else {
986 let items = OpoutAndOpids::new(bmap![opout => opid]);
987 let _ = self.proprietary_push(key, items.serialize());
988 }
989 Ok(true)
990 }
991
992 fn push_rgb_transition(&mut self, mut transition: Transition) -> Result<bool, RgbPsbtError> {
993 let opid = transition.id();
994
995 let prev_transition = self.rgb_transition(opid)?;
996 if let Some(ref prev_transition) = prev_transition {
997 transition.merge_reveal(prev_transition).map_err(|err| {
998 RgbPsbtError::UnrelatedTransitions(prev_transition.id(), opid, err)
999 })?;
1000 }
1001 let serialized_transition = transition
1002 .to_strict_serialized::<U24>()
1003 .map_err(|_| RgbPsbtError::TransitionTooBig(opid))?;
1004
1005 let _ = self.proprietary_push(P::rgb_transition(opid), serialized_transition.release());
1008
1009 for opout in transition.inputs() {
1010 self.set_rgb_contract_consumer(transition.contract_id, opout, opid)?;
1011 }
1012
1013 Ok(prev_transition.is_none())
1014 }
1015
1016 fn rgb_bundles(&self) -> Result<BTreeMap<ContractId, TransitionBundle>, RgbPsbtError> {
1017 let mut map = BTreeMap::new();
1018 for contract_id in self.rgb_contract_ids()? {
1019 let contract_consumers = self.rgb_contract_consumers(contract_id)?;
1020 if contract_consumers.is_empty() {
1021 return Err(RgbPsbtError::NoContractConsumers);
1022 }
1023 let inputs_len = contract_consumers.len();
1024 let input_map = NonEmptyOrdMap::try_from(contract_consumers)
1025 .map_err(|_| RgbPsbtError::InvalidInputsNumber(inputs_len))?;
1026 let mut transitions_map: HashMap<OpId, Transition> = HashMap::new();
1027 for opid in input_map.values() {
1028 if let Some(transition) = self.rgb_transition(*opid)? {
1029 transitions_map.insert(*opid, transition);
1030 }
1031 }
1032 let known_transitions_len = transitions_map.values().len();
1033 let mut known_transitions: SmallVec<KnownTransition> =
1034 SmallVec::with_capacity(known_transitions_len);
1035 insert_transitions_sorted(&transitions_map, &mut known_transitions)?;
1036
1037 let bundle = TransitionBundle {
1038 input_map,
1039 known_transitions: Confined::try_from(known_transitions.release()).map_err(
1040 |_| RgbPsbtError::InvalidTransitionsNumber(contract_id, known_transitions_len),
1041 )?,
1042 };
1043 map.insert(contract_id, bundle);
1044 }
1045 Ok(map)
1046 }
1047
1048 fn outputs_iter_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut O>
1049 where O: 'a;
1050
1051 fn rgb_bundles_to_mpc(
1052 &mut self,
1053 ) -> Result<Confined<BTreeMap<ContractId, TransitionBundle>, 1, U24>, RgbPsbtError> {
1054 let bundles = self.rgb_bundles()?;
1055
1056 let close_method = self
1057 .rgb_close_method()?
1058 .ok_or(RgbPsbtError::NoCloseMethod)?;
1059
1060 let host = self
1061 .outputs_iter_mut()
1062 .find(|output| match close_method {
1063 CloseMethod::OpretFirst => output.is_opret_host(),
1064 CloseMethod::TapretFirst => output.is_tapret_host(),
1065 })
1066 .ok_or(RgbPsbtError::NoHostOutput(close_method))?;
1067
1068 for (contract_id, bundle) in &bundles {
1069 let protocol_id = mpc::ProtocolId::from(*contract_id);
1070 let message = mpc::Message::from(bundle.bundle_id());
1071 host.set_mpc_message(protocol_id, message)?;
1072 }
1073
1074 let map = Confined::try_from(bundles).map_err(|_| RgbPsbtError::NoContracts)?;
1075
1076 Ok(map)
1077 }
1078
1079 fn proprietary_insert(&mut self, key: P, value: Vec<u8>);
1080
1081 fn proprietary_push(&mut self, key: P, value: Vec<u8>) -> Result<(), MpcPsbtError> {
1082 if self.proprietary_contains(&key) {
1083 return Err(MpcPsbtError::KeyAlreadyPresent);
1084 }
1085 self.proprietary_insert(key, value);
1086 Ok(())
1087 }
1088
1089 fn proprietary_contains_key(&self, key: &P) -> bool;
1090
1091 fn proprietary_contains(&self, key: &P) -> bool { self.proprietary_contains_key(key) }
1092
1093 fn proprietary_get_value(&self, key: &P) -> Option<&[u8]>;
1094
1095 fn proprietary_get(&self, key: &P) -> Option<&[u8]> { self.proprietary_get_value(key) }
1096}