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
use crate::{BlockNumber, Capacity, Cycle, Timestamp, TransactionView, Uint64};
use ckb_types::core::service::PoolTransactionEntry as CorePoolTransactionEntry;
use ckb_types::core::tx_pool::{Reject, TxEntryInfo, TxPoolEntryInfo, TxPoolIds as CoreTxPoolIds};
use ckb_types::prelude::Unpack;
use ckb_types::H256;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Transaction pool information.
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub struct TxPoolInfo {
    /// The associated chain tip block hash.
    ///
    /// The transaction pool is stateful. It manages the transactions which are valid to be
    /// committed after this block.
    pub tip_hash: H256,
    /// The block number of the block `tip_hash`.
    pub tip_number: BlockNumber,
    /// Count of transactions in the pending state.
    ///
    /// The pending transactions must be proposed in a new block first.
    pub pending: Uint64,
    /// Count of transactions in the proposed state.
    ///
    /// The proposed transactions are ready to be committed in the new block after the block
    /// `tip_hash`.
    pub proposed: Uint64,
    /// Count of orphan transactions.
    ///
    /// An orphan transaction has an input cell from the transaction which is neither in the chain
    /// nor in the transaction pool.
    pub orphan: Uint64,
    /// Total count of transactions in the pool of all the different kinds of states.
    pub total_tx_size: Uint64,
    /// Total consumed VM cycles of all the transactions in the pool.
    pub total_tx_cycles: Uint64,
    /// Fee rate threshold. The pool rejects transactions which fee rate is below this threshold.
    ///
    /// The unit is Shannons per 1000 bytes transaction serialization size in the block.
    pub min_fee_rate: Uint64,
    /// Last updated time. This is the Unix timestamp in milliseconds.
    pub last_txs_updated_at: Timestamp,
}

/// The transaction entry in the pool.
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub struct PoolTransactionEntry {
    /// The transaction.
    pub transaction: TransactionView,
    /// Consumed cycles.
    pub cycles: Cycle,
    /// The transaction serialized size in block.
    pub size: Uint64,
    /// The transaction fee.
    pub fee: Capacity,
}

impl From<CorePoolTransactionEntry> for PoolTransactionEntry {
    fn from(entry: CorePoolTransactionEntry) -> Self {
        PoolTransactionEntry {
            transaction: entry.transaction.into(),
            cycles: entry.cycles.into(),
            size: (entry.size as u64).into(),
            fee: entry.fee.into(),
        }
    }
}

/// Transaction output validators that prevent common mistakes.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
#[serde(rename_all = "snake_case")]
pub enum OutputsValidator {
    /// "well_known_scripts_only": The default validator which restricts the lock script and type script usage, see more information on https://github.com/nervosnetwork/ckb/wiki/Transaction-%C2%BB-Default-Outputs-Validator
    WellKnownScriptsOnly,
    /// "passthrough": bypass the validator, thus allow any kind of transaction outputs.
    Passthrough,
}

impl OutputsValidator {
    /// Gets the name of the validator when it is serialized into JSON string.
    pub fn json_display(&self) -> String {
        let v = serde_json::to_value(self).expect("OutputsValidator to JSON should never fail");
        v.as_str().unwrap_or_default().to_string()
    }
}

/// Array of transaction ids
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub struct TxPoolIds {
    /// Pending transaction ids
    pub pending: Vec<H256>,
    /// Proposed transaction ids
    pub proposed: Vec<H256>,
}

impl From<CoreTxPoolIds> for TxPoolIds {
    fn from(ids: CoreTxPoolIds) -> Self {
        let CoreTxPoolIds { pending, proposed } = ids;
        TxPoolIds {
            pending: pending.iter().map(Unpack::unpack).collect(),
            proposed: proposed.iter().map(Unpack::unpack).collect(),
        }
    }
}

/// Transaction verbose info
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub struct TxVerbosity {
    /// Consumed cycles.
    pub cycles: Uint64,
    /// The transaction serialized size in block.
    pub size: Uint64,
    /// The transaction fee.
    pub fee: Capacity,
    /// Size of in-tx-pool ancestor transactions
    pub ancestors_size: Uint64,
    /// Cycles of in-tx-pool ancestor transactions
    pub ancestors_cycles: Uint64,
    /// Number of in-tx-pool ancestor transactions
    pub ancestors_count: Uint64,
}

impl From<TxEntryInfo> for TxVerbosity {
    fn from(info: TxEntryInfo) -> Self {
        TxVerbosity {
            cycles: info.cycles.into(),
            size: info.size.into(),
            fee: info.fee.into(),
            ancestors_size: info.ancestors_size.into(),
            ancestors_cycles: info.ancestors_cycles.into(),
            ancestors_count: info.ancestors_count.into(),
        }
    }
}

/// Tx-pool verbose object
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct TxPoolVerbosity {
    /// Pending tx verbose info
    pub pending: HashMap<H256, TxVerbosity>,
    /// Proposed tx verbose info
    pub proposed: HashMap<H256, TxVerbosity>,
}

impl From<TxPoolEntryInfo> for TxPoolVerbosity {
    fn from(info: TxPoolEntryInfo) -> Self {
        let TxPoolEntryInfo { pending, proposed } = info;

        TxPoolVerbosity {
            pending: pending
                .into_iter()
                .map(|(hash, entry)| (hash.unpack(), entry.into()))
                .collect(),
            proposed: proposed
                .into_iter()
                .map(|(hash, entry)| (hash.unpack(), entry.into()))
                .collect(),
        }
    }
}

/// All transactions in tx-pool.
///
/// `RawTxPool` is equivalent to [`TxPoolIds`][] `|` [`TxPoolVerbosity`][].
///
/// [`TxPoolIds`]: struct.TxPoolIds.html
/// [`TxPoolVerbosity`]: struct.TxPoolVerbosity.html
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
#[serde(untagged)]
pub enum RawTxPool {
    /// verbose = false
    Ids(TxPoolIds),
    /// verbose = true
    Verbose(TxPoolVerbosity),
}

/// TX reject message
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type", content = "description")]
pub enum PoolTransactionReject {
    /// Transaction fee lower than config
    LowFeeRate(String),

    /// Transaction exceeded maximum ancestors count limit
    ExceededMaximumAncestorsCount(String),

    /// Transaction pool exceeded maximum size or cycles limit,
    Full(String),

    /// Transaction already exist in transaction_pool
    Duplicated(String),

    /// Malformed transaction
    Malformed(String),

    /// Resolve failed
    Resolve(String),

    /// Verification failed
    Verification(String),
}

impl From<Reject> for PoolTransactionReject {
    fn from(reject: Reject) -> Self {
        match reject {
            Reject::LowFeeRate(..) => Self::LowFeeRate(format!("{}", reject)),
            Reject::ExceededMaximumAncestorsCount => {
                Self::ExceededMaximumAncestorsCount(format!("{}", reject))
            }
            Reject::Full(..) => Self::Full(format!("{}", reject)),
            Reject::Duplicated(_) => Self::Duplicated(format!("{}", reject)),
            Reject::Malformed(_) => Self::Malformed(format!("{}", reject)),
            Reject::Resolve(_) => Self::Resolve(format!("{}", reject)),
            Reject::Verification(_) => Self::Verification(format!("{}", reject)),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_outputs_validator_json_display() {
        assert_eq!(
            "well_known_scripts_only",
            OutputsValidator::WellKnownScriptsOnly.json_display()
        );
        assert_eq!("passthrough", OutputsValidator::Passthrough.json_display());
    }
}