1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use crate::{BtcAddress, H160};
use bitcoin::{
    json::bitcoin::ScriptBuf, Address, ConversionError, Hash, Network, Payload, PubkeyHash, ScriptHash, WPubkeyHash,
    WScriptHash,
};
pub trait PartialAddress: Sized + Eq + PartialOrd {
    fn from_payload(payload: Payload) -> Result<Self, ConversionError>;
    fn to_payload(&self) -> Result<Payload, ConversionError>;
    fn from_address(address: Address) -> Result<Self, ConversionError>;
    fn to_address(&self, network: Network) -> Result<Address, ConversionError>;
}
impl PartialAddress for BtcAddress {
    fn from_payload(payload: Payload) -> Result<Self, ConversionError> {
        match payload {
            Payload::PubkeyHash(hash) => Ok(Self::P2PKH(H160::from(hash.to_byte_array()))),
            Payload::ScriptHash(hash) => Ok(Self::P2SH(H160::from(hash.to_byte_array()))),
            Payload::WitnessProgram(witness_program) => {
                let program = witness_program.program();
                if program.len() == 20 {
                    Ok(Self::P2WPKHv0(H160::from_slice(program.as_bytes())))
                } else {
                    Err(ConversionError::InvalidPayload)
                }
            }
            _ => {
                Err(ConversionError::InvalidFormat)
            }
        }
    }
    fn to_payload(&self) -> Result<Payload, ConversionError> {
        let script = match self {
            Self::P2PKH(hash) => ScriptBuf::new_p2pkh(&PubkeyHash::from_slice(hash.as_bytes())?),
            Self::P2SH(hash) => ScriptBuf::new_p2sh(&ScriptHash::from_slice(hash.as_bytes())?),
            Self::P2WPKHv0(hash) => ScriptBuf::new_v0_p2wpkh(&WPubkeyHash::from_slice(hash.as_bytes())?),
            Self::P2WSHv0(hash) => ScriptBuf::new_v0_p2wsh(&WScriptHash::from_slice(hash.as_bytes())?),
        };
        Ok(Payload::from_script(&script)?)
    }
    fn from_address(address: Address) -> Result<Self, ConversionError> {
        Self::from_payload(address.payload)
    }
    fn to_address(&self, network: Network) -> Result<Address, ConversionError> {
        let payload = self.to_payload()?;
        Ok(Address::new(network, payload))
    }
}
impl PartialAddress for Payload {
    fn from_payload(payload: Payload) -> Result<Self, ConversionError> {
        Ok(payload)
    }
    fn to_payload(&self) -> Result<Payload, ConversionError> {
        Ok(self.clone())
    }
    fn from_address(address: Address) -> Result<Self, ConversionError> {
        Ok(address.payload)
    }
    fn to_address(&self, network: Network) -> Result<Address, ConversionError> {
        Ok(Address::new(network, self.clone()))
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use std::str::FromStr;
    #[test]
    fn test_encode_and_decode_payload() {
        let addr = "bcrt1q6v2c7q7uv8vu6xle2k9ryfj3y3fuuy4rqnl50f";
        assert_eq!(
            addr,
            Payload::from_address(
                Address::from_str(addr)
                    .unwrap()
                    .require_network(Network::Regtest)
                    .unwrap()
            )
            .unwrap()
            .to_address(Network::Regtest)
            .unwrap()
            .to_string()
        );
    }
}