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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::tokens::policy_hash::{PolicyHash, POLICY_HASH_SIZE};
#[cfg(any(test, feature = "property-test-api"))]
use proptest::prelude::*;

use chain_core::{
    packer::Codec,
    property::{Deserialize, ReadError, Serialize, WriteError},
};
use cryptoxide::{blake2b::Blake2b, digest::Digest};
use thiserror::Error;
use typed_bytes::ByteBuilder;

/// A minting policy consists of multiple entries defining different
/// constraints on the minting process. An empty policy means that new tokens
/// cannot be minted during the chain run.
///
/// Minting policies are meant to be ignored in block0 fragments.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
    any(test, feature = "property-test-api"),
    derive(test_strategy::Arbitrary)
)]
pub struct MintingPolicy(
    #[cfg_attr(
        any(test, feature = "property-test-api"),
        strategy(Just(vec![]))
    )]
    Vec<MintingPolicyEntry>,
);

/// An entry of a minting policy. Currently there are no entries available.
/// This is reserved for the future use.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MintingPolicyEntry {}

/// Error while checking a minting transaction against the current system state.
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum MintingPolicyViolation {
    #[error("the policy of this token does not allow minting")]
    AdditionalMintingNotAllowed,
}

impl MintingPolicy {
    pub fn new() -> Self {
        Self(Vec::new())
    }

    pub fn check_minting_tx(&self) -> Result<(), MintingPolicyViolation> {
        if self.0.is_empty() {
            return Err(MintingPolicyViolation::AdditionalMintingNotAllowed);
        }

        for _entry in &self.0 {
            unreachable!("implement this when we have actual minting policies");
        }

        Ok(())
    }

    pub fn entries(&self) -> &[MintingPolicyEntry] {
        &self.0
    }

    pub fn bytes(&self) -> Vec<u8> {
        let bb: ByteBuilder<Self> = ByteBuilder::new();
        bb.u8(0).finalize_as_vec()
    }

    pub fn hash(&self) -> PolicyHash {
        let mut result = [0u8; POLICY_HASH_SIZE];
        if !self.0.is_empty() {
            let mut hasher = Blake2b::new(POLICY_HASH_SIZE);
            hasher.input(&self.bytes());
            hasher.result(&mut result);
        }
        result.into()
    }
}

impl Default for MintingPolicy {
    fn default() -> Self {
        Self::new()
    }
}

impl Serialize for MintingPolicy {
    fn serialized_size(&self) -> usize {
        Codec::u8_size()
    }

    fn serialize<W: std::io::Write>(&self, codec: &mut Codec<W>) -> Result<(), WriteError> {
        codec.put_u8(0_u8)
    }
}

impl Deserialize for MintingPolicy {
    fn deserialize<R: std::io::Read>(codec: &mut Codec<R>) -> Result<Self, ReadError> {
        let no_entries = codec.get_u8()?;
        if no_entries != 0 {
            return Err(ReadError::InvalidData(
                "non-zero number of minting policy entries, but they are currently unimplemented"
                    .to_string(),
            ));
        }
        Ok(Self::new())
    }
}

#[cfg(any(test, feature = "property-test-api"))]
mod tests {
    use super::*;
    #[cfg(test)]
    use crate::testing::serialization::serialization_bijection;
    #[cfg(test)]
    use quickcheck::TestResult;
    use quickcheck::{Arbitrary, Gen};

    impl Arbitrary for MintingPolicy {
        fn arbitrary<G: Gen>(_g: &mut G) -> Self {
            Self::new()
        }
    }

    quickcheck! {
        fn minting_policy_serialization_bijection(policy: MintingPolicy) -> TestResult {
            serialization_bijection(policy)
        }
    }
}