dubp_wallet/source/
v10.rs1use crate::*;
19
20#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
21pub struct SourceV10 {
22 pub id: SourceIdV10,
23 pub amount: SourceAmount,
24}
25
26#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
27pub enum SourceIdV10 {
28 Ud(UdSourceIdV10),
29 Utxo(UtxoIdV10),
30}
31
32#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
33pub struct UdSourceIdV10 {
34 pub issuer: PublicKey,
35 pub block_number: BlockNumber,
36}
37
38#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
39pub struct UtxoIdV10 {
40 pub tx_hash: Hash,
41 pub output_index: usize,
42}
43
44impl std::fmt::Display for UtxoIdV10 {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{}:{}", self.tx_hash, self.output_index)
47 }
48}
49
50#[derive(Clone, Copy, Debug, Error, PartialEq)]
51pub enum SourceV10NotUnlockableError {
52 #[error("{0}")]
53 ScriptNeverUnlockable(ScriptNeverUnlockableError),
54 #[error("Too long signer index: {0}")]
55 TooLongSignerIndex(usize),
56 #[error("Too many proofs: found {found}, used {used}")]
57 TooManyProofs { found: usize, used: usize },
58}
59
60impl SourceV10 {
61 pub fn unlockable_on(
63 tx_signers: &[PublicKey],
64 proofs: &[WalletUnlockProofV10],
65 source_written_on: u64,
66 utxo_script: &WalletScriptV10,
67 ) -> Result<u64, SourceV10NotUnlockableError> {
68 let mut input_signers = HashSet::with_capacity(proofs.len());
69 let mut codes_hash = HashSet::with_capacity(proofs.len());
70
71 for proof in proofs {
72 match proof {
73 WalletUnlockProofV10::Sig(tx_signers_index) => {
74 if *tx_signers_index >= tx_signers.len() {
75 return Err(SourceV10NotUnlockableError::TooLongSignerIndex(
76 *tx_signers_index,
77 ));
78 } else {
79 input_signers.insert(&tx_signers[*tx_signers_index].as_ref()[..32]);
80 }
81 }
82 WalletUnlockProofV10::Xhx(code) => {
83 codes_hash.insert(Hash::compute(code.as_bytes()));
84 }
85 }
86 }
87
88 let (script_unlockable_on, used_proofs) = utxo_script
89 .unlockable_on(&input_signers, &codes_hash, source_written_on)
90 .map_err(SourceV10NotUnlockableError::ScriptNeverUnlockable)?;
91
92 if used_proofs.len() < proofs.len() {
93 Err(SourceV10NotUnlockableError::TooManyProofs {
94 found: proofs.len(),
95 used: used_proofs.len(),
96 })
97 } else {
98 Ok(script_unlockable_on)
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use dubp_common::crypto::keys::PublicKey as _;
107 use unwrap::unwrap;
108
109 #[inline(always)]
110 fn pk(pk_b58: &str) -> PublicKey {
111 unwrap!(PublicKey::from_base58(pk_b58))
112 }
113
114 #[test]
115 fn test_source_unlockable_on_invariant_with_leading_1() {
116 let p43 = pk("XoFs76G4yidvVY3FZBwYyLXTMjabryhFD8mNQPkQKHk");
117 let p43_with_leading_1 = pk("1XoFs76G4yidvVY3FZBwYyLXTMjabryhFD8mNQPkQKHk");
118 let script = WalletScriptV10::single(WalletConditionV10::Sig(p43));
119 let script_with_leading_1 =
120 WalletScriptV10::single(WalletConditionV10::Sig(p43_with_leading_1));
121 let proofs = vec![WalletUnlockProofV10::Sig(0)];
122
123 assert_eq!(
124 Ok(0),
125 SourceV10::unlockable_on(&[p43_with_leading_1], &proofs, 0, &script)
126 );
127
128 assert_eq!(
129 Ok(0),
130 SourceV10::unlockable_on(&[p43], &proofs, 0, &script_with_leading_1)
131 );
132 }
133
134 #[test]
135 fn test_source_unlockable_on() {
136 let p1 = pk("D7CYHJXjaH4j7zRdWngUbsURPnSnjsCYtvo6f8dvW3C");
137 let p2 = pk("42jMJtb8chXrpHMAMcreVdyPJK7LtWjEeRqkPw4eSEVp");
138 let script = WalletScriptV10::or(WalletConditionV10::Sig(p1), WalletConditionV10::Sig(p2));
139 let signers = vec![p1, p2];
140 let proofs = vec![WalletUnlockProofV10::Sig(0), WalletUnlockProofV10::Sig(1)];
141
142 assert_eq!(
143 Err(SourceV10NotUnlockableError::TooManyProofs { found: 2, used: 1 }),
144 SourceV10::unlockable_on(&signers, &proofs, 0, &script)
145 );
146 assert_eq!(
147 Err(SourceV10NotUnlockableError::TooLongSignerIndex(2)),
148 SourceV10::unlockable_on(&signers, &[WalletUnlockProofV10::Sig(2)], 0, &script)
149 );
150 assert_eq!(
151 Err(SourceV10NotUnlockableError::ScriptNeverUnlockable(
152 ScriptNeverUnlockableError
153 )),
154 SourceV10::unlockable_on(&signers, &[], 0, &script)
155 );
156 assert_eq!(
157 Ok(0),
158 SourceV10::unlockable_on(&signers, &[WalletUnlockProofV10::Sig(1)], 0, &script)
159 );
160 }
161}