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
//! The error types to unexpected out-points.

use crate::core::{Capacity, Version};
use crate::generated::packed::{Byte32, OutPoint};
use ckb_error::{impl_error_conversion_with_kind, prelude::*, Error, ErrorKind};
use derive_more::Display;

/// Errors due to the fact that the out-point rules are not respected.
#[derive(Error, Debug, PartialEq, Eq, Clone)]
pub enum OutPointError {
    /// The target cell was already dead.
    #[error("Dead({0:?})")]
    Dead(OutPoint),

    /// There are cells which is unknown to the canonical chain.
    #[error("Unknown({0:?})")]
    Unknown(OutPoint),

    /// There is an input out-point or dependency out-point which references a newer cell in the same block.
    #[error("OutOfOrder({0:?})")]
    OutOfOrder(OutPoint),

    /// There is a dependency out-point, which is [`DepGroup`], but its output-data is invalid format. The expected output-data format for [`DepGroup`] is [`OutPointVec`].
    ///
    /// [`DepGroup`]: ../enum.DepType.html#variant.DepGroup
    /// [`OutPointVec`]: ../../packed/struct.OutPointVec.html
    #[error("InvalidDepGroup({0:?})")]
    InvalidDepGroup(OutPoint),

    /// There is a dependency header that is unknown to the canonical chain.
    #[error("InvalidHeader({0})")]
    InvalidHeader(Byte32),

    /// There is a dependency header that is immature yet.
    #[error("ImmatureHeader({0})")]
    ImmatureHeader(Byte32),

    /// Over max dep expansion limit.
    #[error("OverMaxDepExpansionLimit")]
    OverMaxDepExpansionLimit {
        /// If ban nodes for this error.
        ban: bool,
    },
}

impl From<OutPointError> for Error {
    fn from(error: OutPointError) -> Self {
        ErrorKind::OutPoint.because(error)
    }
}

/// Enum represent transaction relate error source
#[derive(Clone, Debug, Display, Eq, PartialEq)]
pub enum TransactionErrorSource {
    /// cell deps
    CellDeps,
    /// header deps
    HeaderDeps,
    /// inputs
    Inputs,
    /// outputs
    Outputs,
    /// outputs data
    OutputsData,
    /// witnesses
    Witnesses,
}

/// The error types to transactions.
#[derive(Error, Debug, PartialEq, Eq, Clone)]
pub enum TransactionError {
    /// There is an erroneous output that its occupied capacity is greater than its capacity (`output.occupied_capacity() > output.capacity()`).
    #[error("InsufficientCellCapacity({inner}[{index}]): expected occupied capacity ({occupied_capacity:#x}) <= capacity ({capacity:#x})")]
    InsufficientCellCapacity {
        /// The transaction field that causes error.
        /// It should always be `TransactionErrorSource::Outputs.`
        inner: TransactionErrorSource,
        /// The index of that erroneous output.
        index: usize,
        /// The occupied capacity of that erroneous output.
        occupied_capacity: Capacity,
        /// The capacity of that erroneous output.
        capacity: Capacity,
    },

    /// The total capacity of outputs is less than the total capacity of inputs (`SUM([o.capacity for o in outputs]) > SUM([i.capacity for i in inputs]`).
    #[error("OutputsSumOverflow: expected outputs capacity ({outputs_sum:#x}) <= inputs capacity ({inputs_sum:#x})")]
    OutputsSumOverflow {
        /// The total capacity of inputs.
        inputs_sum: Capacity,
        /// The total capacity of outputs.
        outputs_sum: Capacity,
    },

    /// Either inputs or outputs of the transaction are empty (`inputs.is_empty() || outputs.is_empty()`).
    #[error("Empty({inner})")]
    Empty {
        /// The transaction field that causes the error.
        /// It should be `TransactionErrorSource::Inputs` or `TransactionErrorSource::Outputs`.
        inner: TransactionErrorSource,
    },

    /// There are duplicated [`CellDep`]s within the same transaction.
    ///
    /// [`CellDep`]: ../ckb_types/packed/struct.CellDep.html
    #[error("DuplicateCellDeps({out_point})")]
    DuplicateCellDeps {
        /// The out-point of that duplicated [`CellDep`].
        ///
        /// [`CellDep`]: ../ckb_types/packed/struct.CellDep.html
        out_point: OutPoint,
    },

    /// There are duplicated `HeaderDep` within the same transaction.
    #[error("DuplicateHeaderDeps({hash})")]
    DuplicateHeaderDeps {
        /// The block hash of that duplicated `HeaderDep.`
        hash: Byte32,
    },

    /// The length of outputs is not equal to the length of outputs-data (`outputs.len() != outputs_data.len()`).
    #[error("OutputsDataLengthMismatch: expected outputs data length ({outputs_data_len}) = outputs length ({outputs_len})")]
    OutputsDataLengthMismatch {
        /// The length of outputs.
        outputs_len: usize,
        /// The length of outputs-data.
        outputs_data_len: usize,
    },

    /// Error dues to the the fact that the since rule is not respected.
    ///
    /// See also [0017-tx-valid-since](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0017-tx-valid-since/0017-tx-valid-since.md).
    #[error("InvalidSince(Inputs[{index}]): the field since is invalid")]
    InvalidSince {
        /// The index of input with invalid since field
        index: usize,
    },

    /// The transaction is not mature yet, according to the `since` rule.
    ///
    /// See also [0017-tx-valid-since](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0017-tx-valid-since/0017-tx-valid-since.md).
    #[error(
        "Immature(Inputs[{index}]): the transaction is immature because of the since requirement"
    )]
    Immature {
        /// The index of input with immature `since` field.
        index: usize,
    },

    /// The transaction is not mature yet, according to the cellbase maturity rule.
    #[error("CellbaseImmaturity({inner}[{index}])")]
    CellbaseImmaturity {
        /// The transaction field that causes the error.
        /// It should be `TransactionErrorSource::Inputs` or `TransactionErrorSource::CellDeps`. It does not allow using an immature cell as input out-point and dependency out-point.
        inner: TransactionErrorSource,
        /// The index of immature input out-point or dependency out-point.
        index: usize,
    },

    /// The transaction version does not match with the system expected.
    #[error("MismatchedVersion: expected {}, got {}", expected, actual)]
    MismatchedVersion {
        /// The expected transaction version.
        expected: Version,
        /// The actual transaction version.
        actual: Version,
    },

    /// The transaction size exceeds limit.
    #[error("ExceededMaximumBlockBytes: expected transaction serialized size ({actual}) < block size limit ({limit})")]
    ExceededMaximumBlockBytes {
        /// The limited transaction size.
        limit: u64,
        /// The actual transaction size.
        actual: u64,
    },

    /// The compatible error.
    #[error("Compatible: the feature \"{feature}\" is used in current transaction but not enabled in current chain")]
    Compatible {
        /// The feature name.
        feature: &'static str,
    },

    /// The internal error.
    #[error("Internal: {description}, this error shouldn't happen, please report this bug to developers.")]
    Internal {
        /// The error description
        description: String,
    },
}

impl_error_conversion_with_kind!(TransactionError, ErrorKind::Transaction, Error);

impl TransactionError {
    /// Returns whether this transaction error indicates that the transaction is malformed.
    pub fn is_malformed_tx(&self) -> bool {
        match self {
            TransactionError::OutputsSumOverflow { .. }
            | TransactionError::DuplicateCellDeps { .. }
            | TransactionError::DuplicateHeaderDeps { .. }
            | TransactionError::Empty { .. }
            | TransactionError::InsufficientCellCapacity { .. }
            | TransactionError::InvalidSince { .. }
            | TransactionError::ExceededMaximumBlockBytes { .. }
            | TransactionError::OutputsDataLengthMismatch { .. } => true,

            TransactionError::Immature { .. }
            | TransactionError::CellbaseImmaturity { .. }
            | TransactionError::MismatchedVersion { .. }
            | TransactionError::Compatible { .. }
            | TransactionError::Internal { .. } => false,
        }
    }
}

impl OutPointError {
    /// Returns true if the error is an unknown out_point.
    pub fn is_unknown(&self) -> bool {
        matches!(self, OutPointError::Unknown(_))
    }
}