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
//! For for implement offchain operations or for testing purpose

use std::collections::{HashMap, HashSet};

use ckb_types::{
    bytes::Bytes,
    core::{HeaderView, TransactionView},
    packed::{Byte32, CellDep, CellOutput, OutPoint, Script, Transaction},
    prelude::*,
    H256,
};

use crate::traits::{
    CellCollector, CellCollectorError, CellDepResolver, CellQueryOptions, HeaderDepResolver,
    LiveCell, TransactionDependencyError, TransactionDependencyProvider,
};
use crate::types::ScriptId;
use anyhow::anyhow;

/// A offchain cell_dep resolver
#[derive(Default, Clone)]
pub struct OffchainCellDepResolver {
    pub items: HashMap<ScriptId, (CellDep, String)>,
}
impl CellDepResolver for OffchainCellDepResolver {
    fn resolve(&self, script: &Script) -> Option<CellDep> {
        let script_id = ScriptId::from(script);
        self.items
            .get(&script_id)
            .map(|(cell_dep, _)| cell_dep.clone())
    }
}

#[derive(Default, Clone)]
pub struct OffchainHeaderDepResolver {
    pub by_tx_hash: HashMap<H256, HeaderView>,
    pub by_number: HashMap<u64, HeaderView>,
}

impl HeaderDepResolver for OffchainHeaderDepResolver {
    fn resolve_by_tx(&self, tx_hash: &Byte32) -> Result<Option<HeaderView>, anyhow::Error> {
        let tx_hash: H256 = tx_hash.unpack();
        Ok(self.by_tx_hash.get(&tx_hash).cloned())
    }
    fn resolve_by_number(&self, number: u64) -> Result<Option<HeaderView>, anyhow::Error> {
        Ok(self.by_number.get(&number).cloned())
    }
}

/// A cell collector only use offchain data
#[derive(Default, Clone)]
pub struct OffchainCellCollector {
    pub locked_cells: HashSet<(H256, u32)>,
    pub live_cells: Vec<LiveCell>,
    pub max_mature_number: u64,
}

impl OffchainCellCollector {
    pub fn new(
        locked_cells: HashSet<(H256, u32)>,
        live_cells: Vec<LiveCell>,
        max_mature_number: u64,
    ) -> OffchainCellCollector {
        OffchainCellCollector {
            locked_cells,
            live_cells,
            max_mature_number,
        }
    }

    pub fn collect(&self, query: &CellQueryOptions) -> (Vec<LiveCell>, Vec<LiveCell>, u64) {
        let mut total_capacity = 0;
        let (cells, rest_cells): (Vec<_>, Vec<_>) =
            self.live_cells.clone().into_iter().partition(|cell| {
                if total_capacity < query.min_total_capacity
                    && query.match_cell(cell, self.max_mature_number)
                {
                    let capacity: u64 = cell.output.capacity().unpack();
                    total_capacity += capacity;
                    true
                } else {
                    false
                }
            });
        (cells, rest_cells, total_capacity)
    }
}

impl CellCollector for OffchainCellCollector {
    fn collect_live_cells(
        &mut self,
        query: &CellQueryOptions,
        apply_changes: bool,
    ) -> Result<(Vec<LiveCell>, u64), CellCollectorError> {
        let (cells, rest_cells, total_capacity) = self.collect(query);
        if apply_changes {
            self.live_cells = rest_cells;
            for cell in &cells {
                self.lock_cell(cell.out_point.clone())?;
            }
        }
        Ok((cells, total_capacity))
    }

    fn lock_cell(&mut self, out_point: OutPoint) -> Result<(), CellCollectorError> {
        self.locked_cells
            .insert((out_point.tx_hash().unpack(), out_point.index().unpack()));
        Ok(())
    }
    fn apply_tx(&mut self, tx: Transaction) -> Result<(), CellCollectorError> {
        let tx_view = tx.into_view();
        let tx_hash = tx_view.hash();
        for out_point in tx_view.input_pts_iter() {
            self.lock_cell(out_point)?;
        }
        for (output_index, (output, data)) in tx_view.outputs_with_data_iter().enumerate() {
            let out_point = OutPoint::new(tx_hash.clone(), output_index as u32);
            let info = LiveCell {
                output: output.clone(),
                output_data: data.clone(),
                out_point,
                block_number: 0,
                tx_index: 0,
            };
            self.live_cells.push(info);
        }
        Ok(())
    }

    fn reset(&mut self) {
        self.locked_cells.clear();
        self.live_cells.clear();
    }
}

/// offchain transaction dependency provider
#[derive(Default, Clone)]
pub struct OffchainTransactionDependencyProvider {
    pub txs: HashMap<H256, TransactionView>,
    pub cells: HashMap<(H256, u32), (CellOutput, Bytes)>,
    pub headers: HashMap<H256, HeaderView>,
}

impl TransactionDependencyProvider for OffchainTransactionDependencyProvider {
    // For verify certain cell belong to certain transaction
    fn get_transaction(
        &self,
        tx_hash: &Byte32,
    ) -> Result<TransactionView, TransactionDependencyError> {
        let tx_hash: H256 = tx_hash.unpack();
        self.txs
            .get(&tx_hash)
            .cloned()
            .ok_or_else(|| TransactionDependencyError::Other(anyhow!("offchain get_transaction")))
    }
    // For get the output information of inputs or cell_deps, those cell should be live cell
    fn get_cell(&self, out_point: &OutPoint) -> Result<CellOutput, TransactionDependencyError> {
        let tx_hash: H256 = out_point.tx_hash().unpack();
        let index: u32 = out_point.index().unpack();
        self.cells
            .get(&(tx_hash, index))
            .map(|(output, _)| output.clone())
            .ok_or_else(|| TransactionDependencyError::Other(anyhow!("offchain get_cell")))
    }
    // For get the output data information of inputs or cell_deps
    fn get_cell_data(&self, out_point: &OutPoint) -> Result<Bytes, TransactionDependencyError> {
        let tx_hash: H256 = out_point.tx_hash().unpack();
        let index: u32 = out_point.index().unpack();
        self.cells
            .get(&(tx_hash, index))
            .map(|(_, data)| data.clone())
            .ok_or_else(|| TransactionDependencyError::Other(anyhow!("offchain get_cell_data")))
    }
    // For get the header information of header_deps
    fn get_header(&self, block_hash: &Byte32) -> Result<HeaderView, TransactionDependencyError> {
        let block_hash: H256 = block_hash.unpack();
        self.headers
            .get(&block_hash)
            .cloned()
            .ok_or_else(|| TransactionDependencyError::Other(anyhow!("offchain get_header")))
    }
}