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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
use super::{Balance, Input, Output, Payload, PayloadSlice};
use crate::fee::FeeAlgorithm;
use crate::value::{Value, ValueError};
use chain_addr::Address;
use std::error;
use std::fmt;

/// Inputs & Outputs for a transaction being built
pub struct InputOutputBuilder {
    inputs: Vec<Input>,
    outputs: Vec<Output<Address>>,
}

/// Inputs & Outputs for a built transaction
pub struct InputOutput {
    pub inputs: Box<[Input]>,
    pub outputs: Box<[Output<Address>]>,
}

/// Possible error for the builder.
#[derive(Debug, Clone)]
pub enum Error {
    TxInvalidNoInput,
    TxInvalidNoOutput,
    TxTooManyInputs,
    TxTooManyOutputs,
    TxNotEnoughTotalInput,
    TxTooMuchTotalInput,
    MathErr(ValueError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::TxInvalidNoInput => write!(f, "transaction has no inputs"),
            Error::TxInvalidNoOutput => write!(f, "transaction has no outputs"),
            Error::TxTooManyInputs => write!(f, "transaction has too many inputs"),
            Error::TxTooManyOutputs => write!(f, "transaction has too many outputs"),
            Error::TxNotEnoughTotalInput => write!(f, "not enough input for making transaction"),
            Error::TxTooMuchTotalInput => write!(f, "too muny input value for making transaction"),
            Error::MathErr(v) => write!(f, "error in arithmetics {:?}", v),
        }
    }
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        None
    }
}

/// Output policy to be used in transaction. This policy is used then
/// there is positive balance on in the OUTPUT+FEE-INPUT. Policy
/// explains how to use that balance. Rember that policy application
/// may change the amount of the fee.
#[derive(Debug, Clone)]
pub enum OutputPolicy {
    /// Send all extra balance to the given address.
    One(Address),
    /// Forget everything, do not try to return money.
    Forget,
}

impl InputOutputBuilder {
    /// Create a new empty builder
    pub fn empty() -> InputOutputBuilder {
        InputOutputBuilder {
            inputs: Vec::new(),
            outputs: Vec::new(),
        }
    }

    /// Create a builder from a given sequence of inputs and outputs
    pub fn new<'a, IITER, OITER>(inputs: IITER, outputs: OITER) -> Result<InputOutputBuilder, Error>
    where
        IITER: Iterator<Item = &'a Input>,
        OITER: Iterator<Item = &'a Output<Address>>,
    {
        let inputs: Vec<_> = inputs.cloned().collect();
        let outputs: Vec<_> = outputs.cloned().collect();
        if inputs.len() > 255 {
            return Err(Error::TxTooManyInputs);
        }
        if outputs.len() > 255 {
            return Err(Error::TxTooManyOutputs);
        }
        Ok(InputOutputBuilder { inputs, outputs })
    }

    /// Build the InputOutput from the Builder
    pub fn build(self) -> InputOutput {
        InputOutput {
            inputs: self.inputs.into(),
            outputs: self.outputs.into(),
        }
    }

    /// Add additional input.
    ///
    /// Each input may extend the size of the required fee.
    pub fn add_input(&mut self, input: &Input) -> Result<(), Error> {
        if self.inputs.len() == 255 {
            return Err(Error::TxTooManyInputs);
        }
        self.inputs.push(input.clone());
        Ok(())
    }

    /// Add additional output.
    ///
    /// Each output may extend the size of the required fee.
    pub fn add_output(&mut self, address: Address, value: Value) -> Result<(), Error> {
        if self.outputs.len() == 255 {
            return Err(Error::TxTooManyOutputs);
        }
        self.outputs.push(Output { address, value });
        Ok(())
    }

    /// Remove input at the index specified starting from the oldest added input.
    pub fn remove_input(&mut self, input: usize) {
        if input < self.inputs.len() {
            let _ = self.inputs.remove(input);
        }
    }

    /// Remove output at the index specified starting from the oldest added output.
    pub fn remove_output(&mut self, output: usize) {
        if output < self.outputs.len() {
            let _ = self.outputs.remove(output);
        }
    }

    pub fn balance(&self, fee: Value) -> Result<Balance, ValueError> {
        let inputs = Value::sum(self.inputs.iter().map(|i| i.value()))?;
        let outputs = Value::sum(self.outputs.iter().map(|o| o.value))?;
        let z = (outputs + fee)?;

        match inputs.cmp(&z) {
            std::cmp::Ordering::Greater => Ok(Balance::Positive((inputs - z)?)),
            std::cmp::Ordering::Less => Ok(Balance::Negative((z - inputs)?)),
            std::cmp::Ordering::Equal => Ok(Balance::Zero),
        }
    }

    /// Calculate the fees on a given fee algorithm for the current transaction
    pub fn estimate_fee<P: Payload, F: FeeAlgorithm>(
        &self,
        payload: PayloadSlice<'_, P>,
        fee_algorithm: &F,
    ) -> Value {
        fee_algorithm.calculate(
            payload.into_certificate_slice(),
            self.inputs.len() as u8,
            self.outputs.len() as u8,
        )
    }

    /// Get balance including current fee.
    pub fn get_balance<P: Payload, F: FeeAlgorithm>(
        &self,
        payload: PayloadSlice<'_, P>,
        fee_algorithm: &F,
    ) -> Result<Balance, ValueError> {
        let fee = self.estimate_fee(payload, fee_algorithm);
        self.balance(fee)
    }

    /// Get balance including current fee.
    pub fn get_balance_with_placeholders<P: Payload, F: FeeAlgorithm>(
        &self,
        payload: PayloadSlice<'_, P>,
        fee_algorithm: &F,
        inputs_placeholders: u8,
        outputs_placeholders: u8,
    ) -> Result<Balance, Error> {
        if self.inputs.len() + inputs_placeholders as usize >= 256 {
            return Err(Error::TxTooManyInputs);
        }
        if self.outputs.len() + outputs_placeholders as usize >= 256 {
            return Err(Error::TxTooManyOutputs);
        }

        let nb_inputs = self.inputs.len() as u8 + inputs_placeholders;
        let nb_outputs = self.outputs.len() as u8 + outputs_placeholders;

        let fee = fee_algorithm.calculate(payload.into_certificate_slice(), nb_inputs, nb_outputs);
        self.balance(fee).map_err(Error::MathErr)
    }

    /// Get transaction balance without fee included.
    pub fn get_balance_without_fee(&self) -> Result<Balance, ValueError> {
        self.balance(Value::zero())
    }

    /// Seal the transaction checking that the transaction fits the fee algorithm
    pub fn seal<P: Payload, F: FeeAlgorithm>(
        self,
        payload: PayloadSlice<P>,
        fee_algorithm: &F,
    ) -> Result<InputOutput, Error> {
        match self.get_balance(payload, fee_algorithm) {
            Err(err) => Err(Error::MathErr(err)),
            Ok(Balance::Negative(_)) => Err(Error::TxNotEnoughTotalInput),
            Ok(Balance::Positive(_)) => Err(Error::TxTooMuchTotalInput),
            Ok(Balance::Zero) => Ok(self.build()),
        }
    }

    /// Seal the transaction by passing fee rule and the output policy
    ///
    /// Along with the transaction, this return the balance unassigned to output policy
    /// if any
    pub fn seal_with_output_policy<P: Payload, F: FeeAlgorithm>(
        mut self,
        payload: PayloadSlice<P>,
        fee_algorithm: &F,
        policy: OutputPolicy,
    ) -> Result<(Balance, Vec<Output<Address>>, InputOutput), Error> {
        // calculate initial fee, maybe we can fit it without any
        // additional calculations.
        let fee = self.estimate_fee(payload.clone(), fee_algorithm);
        let pos = match self.balance(fee) {
            Ok(Balance::Negative(_)) => return Err(Error::TxNotEnoughTotalInput),
            Ok(Balance::Positive(v)) => v,
            Ok(Balance::Zero) => {
                return Ok((Balance::Zero, vec![], self.build()));
            }
            Err(err) => return Err(Error::MathErr(err)),
        };
        // we have more money in the inputs then fee and outputs
        // so we need to return some money back to us.
        match policy {
            OutputPolicy::Forget => Ok((Balance::Positive(pos), vec![], self.build())),
            // We will try to find the best matching value, for
            // this reason we will try to reduce the set using
            // value estimated by the current fee.
            //
            // We are searching in a range
            //   [min_value, max_value)
            OutputPolicy::One(address) => {
                // This is simplified version of the algorithm that
                // works only in case in fee can perfectly estimate
                // the required cost. We add an additional empty output
                // hoping that it doesn't change fee count.
                //
                // Otherwise better estimation algorithm is needed.
                self.add_output(address.clone(), Value::zero())?;
                let fee = self.estimate_fee(payload, fee_algorithm);
                match self.balance(fee) {
                    Ok(Balance::Positive(value)) => {
                        let _ = self.outputs.pop();
                        let output = Output { address, value };
                        self.outputs.push(output.clone());
                        Ok((Balance::Zero, vec![output], self.build()))
                    }
                    _ => {
                        let _ = self.outputs.pop();
                        Ok((Balance::Positive(pos), vec![], self.build()))
                    }
                }
            }
        }
    }
}