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
use crate::value::Value;

pub use cardano_legacy_address::Addr as OldAddress;
pub use cardano_legacy_address::AddressMatchXPub as OldAddressMatchXPub;

use chain_core::property::WriteError;
use chain_core::{
    packer::Codec,
    property::{Deserialize, DeserializeFromSlice, ReadError, Serialize},
};
use chain_crypto::{Ed25519, PublicKey};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UtxoDeclaration {
    pub addrs: Vec<(OldAddress, Value)>,
}

pub fn oldaddress_from_xpub(
    address: &OldAddress,
    pk: &PublicKey<Ed25519>,
    some_bytes: &[u8; 32],
) -> OldAddressMatchXPub {
    let mut pkraw = [0u8; 32];
    pkraw.copy_from_slice(pk.as_ref());
    address.identical_with_pubkey_raw(&pkraw, some_bytes)
}

impl DeserializeFromSlice for UtxoDeclaration {
    fn deserialize_from_slice(codec: &mut Codec<&[u8]>) -> Result<Self, ReadError> {
        let nb_entries = codec.get_u8()? as usize;
        if nb_entries >= 0xff {
            return Err(ReadError::StructureInvalid("nb entries".to_string()));
        }

        let mut addrs = Vec::with_capacity(nb_entries);
        for _ in 0..nb_entries {
            let value = Value::deserialize(codec)?;
            let addr_size = codec.get_be_u16()? as usize;
            let addr = OldAddress::try_from(codec.get_slice(addr_size)?)
                .map_err(|err| ReadError::StructureInvalid(format!("{}", err)))?;
            addrs.push((addr, value))
        }

        Ok(UtxoDeclaration { addrs })
    }
}

impl Serialize for UtxoDeclaration {
    fn serialized_size(&self) -> usize {
        assert!(self.addrs.len() < 255);

        let mut res = Codec::u8_size();
        for (b, v) in &self.addrs {
            res += v.serialized_size() + Codec::u16_size() + b.as_ref().len();
        }
        res
    }

    fn serialize<W: std::io::Write>(&self, codec: &mut Codec<W>) -> Result<(), WriteError> {
        assert!(self.addrs.len() < 255);

        codec.put_u8(self.addrs.len() as u8)?;
        for (b, v) in &self.addrs {
            v.serialize(codec)?;
            let bs = b.as_ref();
            codec.put_be_u16(bs.len() as u16)?;
            codec.put_bytes(bs)?;
        }
        Ok(())
    }
}

#[cfg(any(test, feature = "property-test-api"))]
mod tests {
    use super::*;
    use cardano_legacy_address::ExtendedAddr;
    use ed25519_bip32::{XPub, XPUB_SIZE};
    use quickcheck::{Arbitrary, Gen};

    impl Arbitrary for UtxoDeclaration {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            let mut nb: usize = Arbitrary::arbitrary(g);
            nb %= 255;
            let mut addrs = Vec::with_capacity(nb);
            for _ in 0..nb {
                let value = Arbitrary::arbitrary(g);

                let xpub = {
                    let mut buf = [0u8; XPUB_SIZE];
                    for o in buf.iter_mut() {
                        *o = u8::arbitrary(g)
                    }
                    match XPub::from_slice(&buf) {
                        Ok(xpub) => xpub,
                        Err(err) => panic!("xpub not built correctly, {:?}", err),
                    }
                };
                let ea = ExtendedAddr::new_simple(&xpub, None);
                let addr = ea.to_address();

                addrs.push((addr, value))
            }

            UtxoDeclaration { addrs }
        }
    }
}