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
130
131
use crate::key::deserialize_signature;
use crate::transaction::TransactionBindingAuthData;
use crate::value::{Value, ValueError};
use chain_core::packer::Codec;
use chain_core::property::{DeserializeFromSlice, ReadError};
use chain_crypto::{digest::DigestOf, Blake2b256, Ed25519, PublicKey, Signature, Verification};
use thiserror::Error;
use typed_bytes::ByteBuilder;

pub struct TransactionSignData(Box<[u8]>);

impl From<Vec<u8>> for TransactionSignData {
    fn from(v: Vec<u8>) -> TransactionSignData {
        TransactionSignData(v.into())
    }
}

impl AsRef<[u8]> for TransactionSignData {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

pub type TransactionSignDataHash = DigestOf<Blake2b256, TransactionSignData>;

// cannot use TransactionSignDataHash<'a> as a phantom in the signature,
// as the lifetime is still kept behind, so invent a new phantom type
// to track the SignDataHash
#[derive(Debug, Clone)]
pub struct TransactionBindingAuthDataPhantom();

#[derive(Debug, Clone)]
pub struct SingleAccountBindingSignature(
    pub(crate) Signature<TransactionBindingAuthDataPhantom, Ed25519>,
);

impl SingleAccountBindingSignature {
    pub fn verify_slice(
        &self,
        pk: &PublicKey<Ed25519>,
        data: &TransactionBindingAuthData<'_>,
    ) -> Verification {
        self.0.verify_slice(pk, data.0)
    }

    pub fn new<'a, F>(data: &TransactionBindingAuthData<'a>, sign: F) -> Self
    where
        F: FnOnce(
            &TransactionBindingAuthData<'a>,
        ) -> Signature<TransactionBindingAuthDataPhantom, Ed25519>,
    {
        let sig = sign(data);
        SingleAccountBindingSignature(sig) // sk.sign_slice(data.0))
    }

    pub fn serialize_in(&self, bb: ByteBuilder<Self>) -> ByteBuilder<Self> {
        bb.bytes(self.as_ref())
    }
}

impl AsRef<[u8]> for SingleAccountBindingSignature {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl DeserializeFromSlice for SingleAccountBindingSignature {
    fn deserialize_from_slice(codec: &mut Codec<&[u8]>) -> Result<Self, ReadError> {
        deserialize_signature(codec).map(SingleAccountBindingSignature)
    }
}

#[derive(Debug, Clone)]
pub enum AccountBindingSignature {
    Single(SingleAccountBindingSignature),
    Multi(u32), // TODO
}

impl AccountBindingSignature {
    pub fn new_single<'a, F>(data: &TransactionBindingAuthData<'a>, sign: F) -> Self
    where
        F: FnOnce(
            &TransactionBindingAuthData<'a>,
        ) -> Signature<TransactionBindingAuthDataPhantom, Ed25519>,
    {
        AccountBindingSignature::Single(SingleAccountBindingSignature::new(data, sign))
    }

    pub fn serialize_in(&self, bb: ByteBuilder<Self>) -> ByteBuilder<Self> {
        match self {
            AccountBindingSignature::Single(sig) => bb.u8(1).sub(|bb| sig.serialize_in(bb)),
            AccountBindingSignature::Multi(_) => {
                bb.u8(2);
                unimplemented!()
            }
        }
    }
}

impl DeserializeFromSlice for AccountBindingSignature {
    fn deserialize_from_slice(codec: &mut Codec<&[u8]>) -> Result<Self, ReadError> {
        match codec.get_u8()? {
            1 => {
                let sig = deserialize_signature(codec).map(SingleAccountBindingSignature)?;
                Ok(AccountBindingSignature::Single(sig))
            }
            2 => unimplemented!(),
            n => Err(ReadError::UnknownTag(n as u32)),
        }
    }
}

/// Amount of the balance in the transaction.
pub enum Balance {
    /// Balance is positive.
    Positive(Value),
    /// Balance is negative, such transaction can't be valid.
    Negative(Value),
    /// Balance is zero.
    Zero,
}

#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum BalanceError {
    #[error("failed to compute total input")]
    InputsTotalFailed(#[source] ValueError),
    #[error("failed to compute total output")]
    OutputsTotalFailed(#[source] ValueError),
    #[error("transaction value not balanced, has inputs sum {inputs} and outputs sum {outputs}")]
    NotBalanced { inputs: Value, outputs: Value },
}