ckb_sdk/traits/
default_impls.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3use std::time::Duration;
4
5use anyhow::anyhow;
6use ckb_crypto::secp::Pubkey;
7use lru::LruCache;
8use thiserror::Error;
9use tokio::sync::Mutex;
10
11use ckb_hash::blake2b_256;
12use ckb_jsonrpc_types::{self as json_types, Either};
13use ckb_types::{
14    bytes::Bytes,
15    core::{BlockView, DepType, HeaderView, TransactionView},
16    packed::{Byte32, CellDep, CellOutput, OutPoint, Script, Transaction, TransactionReader},
17    prelude::*,
18    H160,
19};
20
21use super::{
22    offchain_impls::CollectResult, OffchainCellCollector, OffchainCellDepResolver,
23    OffchainTransactionDependencyProvider,
24};
25use crate::{constants::MULTISIG_LEGACY_GROUP_OUTPUT_LOC, types::ScriptId};
26use crate::{constants::MULTISIG_LEGACY_OUTPUT_LOC, SECP256K1};
27use crate::{
28    constants::{MultisigScript, GENESIS_BLOCK_HASH_MAINNET, GENESIS_BLOCK_HASH_TESTNET},
29    rpc::ckb_indexer::{Order, SearchKey, Tip},
30};
31use crate::{
32    constants::{
33        DAO_OUTPUT_LOC, DAO_TYPE_HASH, SIGHASH_GROUP_OUTPUT_LOC, SIGHASH_OUTPUT_LOC,
34        SIGHASH_TYPE_HASH,
35    },
36    util::keccak160,
37};
38use crate::{
39    rpc::{CkbRpcAsyncClient, IndexerRpcAsyncClient},
40    traits::{
41        CellCollector, CellCollectorError, CellDepResolver, CellQueryOptions, HeaderDepResolver,
42        LiveCell, QueryOrder, Signer, SignerError, TransactionDependencyError,
43        TransactionDependencyProvider,
44    },
45};
46use crate::{
47    util::{get_max_mature_number_async, serialize_signature, zeroize_privkey},
48    NetworkInfo,
49};
50use ckb_resource::{
51    CODE_HASH_DAO, CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL,
52    CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL,
53};
54
55/// Parse Genesis Info errors
56#[derive(Error, Debug)]
57pub enum ParseGenesisInfoError {
58    #[error("invalid block number, expected: 0, got: `{0}`")]
59    InvalidBlockNumber(u64),
60    #[error("data not found: `{0}`")]
61    DataHashNotFound(String),
62    #[error("type not found: `{0}`")]
63    TypeHashNotFound(String),
64}
65
66/// A cell_dep resolver use genesis info resolve system scripts and can register more cell_dep info.
67#[derive(Clone)]
68pub struct DefaultCellDepResolver {
69    offchain: OffchainCellDepResolver,
70}
71impl DefaultCellDepResolver {
72    /// You can customize the multisig script's depgroup by these two env variables, for example:
73    /// 1. MULTISIG_LEGACY_DEP_GROUP=0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c,1
74    /// 2. MULTISIG_V2_DEP_GROUP=0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738,2
75    #[cfg(not(target_arch = "wasm32"))]
76    pub fn from_genesis(
77        genesis_block: &BlockView,
78    ) -> Result<DefaultCellDepResolver, ParseGenesisInfoError> {
79        crate::rpc::block_on(Self::from_genesis_async(genesis_block))
80    }
81    /// You can customize the multisig script's depgroup by these two env variables, for example:
82    /// 1. MULTISIG_LEGACY_DEP_GROUP=0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c,1
83    /// 2. MULTISIG_V2_DEP_GROUP=0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738,2
84    pub async fn from_genesis_async(
85        genesis_block: &BlockView,
86    ) -> Result<DefaultCellDepResolver, ParseGenesisInfoError> {
87        let header = genesis_block.header();
88        if header.number() != 0 {
89            return Err(ParseGenesisInfoError::InvalidBlockNumber(header.number()));
90        }
91        let mut sighash_type_hash = None;
92        let mut multisig_legacy_type_hash = None;
93        let mut dao_type_hash = None;
94        let out_points = genesis_block
95            .transactions()
96            .iter()
97            .enumerate()
98            .map(|(tx_index, tx)| {
99                tx.outputs()
100                    .into_iter()
101                    .zip(tx.outputs_data())
102                    .enumerate()
103                    .map(|(index, (output, data))| {
104                        if tx_index == SIGHASH_OUTPUT_LOC.0 && index == SIGHASH_OUTPUT_LOC.1 {
105                            sighash_type_hash = output
106                                .type_()
107                                .to_opt()
108                                .map(|script| script.calc_script_hash());
109                            let data_hash = CellOutput::calc_data_hash(&data.raw_data());
110                            if data_hash != CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL.pack() {
111                                log::error!(
112                                    "System sighash script code hash error! found: {}, expected: {}",
113                                    data_hash,
114                                    CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL,
115                                );
116                            }
117                        }
118                        if tx_index == MULTISIG_LEGACY_OUTPUT_LOC.0 && index == MULTISIG_LEGACY_OUTPUT_LOC.1 {
119                            multisig_legacy_type_hash = output
120                                .type_()
121                                .to_opt()
122                                .map(|script| script.calc_script_hash());
123                            let data_hash = CellOutput::calc_data_hash(&data.raw_data());
124                            if data_hash != CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL.pack() {
125                                log::error!(
126                                    "System multisig script code hash error! found: {}, expected: {}",
127                                    data_hash,
128                                    CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL,
129                                );
130                            }
131                        }
132                        if tx_index == DAO_OUTPUT_LOC.0 && index == DAO_OUTPUT_LOC.1 {
133                            dao_type_hash = output
134                                .type_()
135                                .to_opt()
136                                .map(|script| script.calc_script_hash());
137                            let data_hash = CellOutput::calc_data_hash(&data.raw_data());
138                            if data_hash != CODE_HASH_DAO.pack() {
139                                log::error!(
140                                    "System dao script code hash error! found: {}, expected: {}",
141                                    data_hash,
142                                    CODE_HASH_DAO,
143                                );
144                            }
145                        }
146                        OutPoint::new(tx.hash(), index as u32)
147                    })
148                    .collect::<Vec<_>>()
149            })
150            .collect::<Vec<_>>();
151
152        let sighash_type_hash = sighash_type_hash
153            .ok_or_else(|| "No type hash(sighash) found in txs[0][1]".to_owned())
154            .map_err(ParseGenesisInfoError::TypeHashNotFound)?;
155        let dao_type_hash = dao_type_hash
156            .ok_or_else(|| "No type hash(dao) found in txs[0][2]".to_owned())
157            .map_err(ParseGenesisInfoError::TypeHashNotFound)?;
158
159        let sighash_dep = CellDep::new_builder()
160            .out_point(out_points[SIGHASH_GROUP_OUTPUT_LOC.0][SIGHASH_GROUP_OUTPUT_LOC.1].clone())
161            .dep_type(DepType::DepGroup)
162            .build();
163
164        let multisig_legacy_dep = CellDep::new_builder()
165            .out_point(
166                out_points[MULTISIG_LEGACY_GROUP_OUTPUT_LOC.0][MULTISIG_LEGACY_GROUP_OUTPUT_LOC.1]
167                    .clone(),
168            )
169            .dep_type(DepType::DepGroup)
170            .build();
171
172        let dao_dep = CellDep::new_builder()
173            .out_point(out_points[DAO_OUTPUT_LOC.0][DAO_OUTPUT_LOC.1].clone())
174            .build();
175
176        let mut items = HashMap::default();
177        items.insert(
178            ScriptId::new_type(sighash_type_hash.unpack()),
179            (sighash_dep, "Secp256k1 blake160 sighash all".to_string()),
180        );
181
182        {
183            let network_info: NetworkInfo =
184                if genesis_block.hash().eq(&GENESIS_BLOCK_HASH_MAINNET.pack()) {
185                    NetworkInfo::mainnet()
186                } else if genesis_block.hash().eq(&GENESIS_BLOCK_HASH_TESTNET.pack()) {
187                    NetworkInfo::testnet()
188                } else {
189                    NetworkInfo::devnet()
190                };
191
192            if let Some((v2_dep_hash, v2_dep_index)) = MultisigScript::V2
193                .dep_group_async(network_info, Some(genesis_block.to_owned()))
194                .await
195            {
196                let multisig_v2_dep = CellDep::new_builder()
197                    .out_point(OutPoint::new(v2_dep_hash.pack(), v2_dep_index))
198                    .dep_type(DepType::DepGroup)
199                    .build();
200
201                items.insert(
202                    MultisigScript::V2.script_id(),
203                    (
204                        multisig_v2_dep,
205                        "Secp256k1 blake160 multisig(v2) all".to_string(),
206                    ),
207                );
208            }
209        }
210
211        items.insert(
212            MultisigScript::Legacy.script_id(),
213            (
214                multisig_legacy_dep,
215                "Secp256k1 blake160 multisig(legacy) all".to_string(),
216            ),
217        );
218        items.insert(
219            ScriptId::new_type(dao_type_hash.unpack()),
220            (dao_dep, "Nervos DAO".to_string()),
221        );
222        let offchain = OffchainCellDepResolver { items };
223        Ok(DefaultCellDepResolver { offchain })
224    }
225
226    pub fn insert(
227        &mut self,
228        script_id: ScriptId,
229        cell_dep: CellDep,
230        name: String,
231    ) -> Option<(CellDep, String)> {
232        self.offchain.items.insert(script_id, (cell_dep, name))
233    }
234    pub fn remove(&mut self, script_id: &ScriptId) -> Option<(CellDep, String)> {
235        self.offchain.items.remove(script_id)
236    }
237    pub fn contains(&self, script_id: &ScriptId) -> bool {
238        self.offchain.items.contains_key(script_id)
239    }
240    pub fn get(&self, script_id: &ScriptId) -> Option<&(CellDep, String)> {
241        self.offchain.items.get(script_id)
242    }
243    pub fn sighash_dep(&self) -> Option<&(CellDep, String)> {
244        self.get(&ScriptId::new_type(SIGHASH_TYPE_HASH))
245    }
246    /// TODO: We have found MultisigScript::Legacy's dep from genesis block.
247    /// TODO: then need manually insert MultisigScript::V1's deps to self.
248    pub fn multisig_dep(&self, multisig_script: MultisigScript) -> Option<&(CellDep, String)> {
249        self.get(&multisig_script.script_id())
250    }
251    pub fn dao_dep(&self) -> Option<&(CellDep, String)> {
252        self.get(&ScriptId::new_type(DAO_TYPE_HASH))
253    }
254}
255
256impl CellDepResolver for DefaultCellDepResolver {
257    fn resolve(&self, script: &Script) -> Option<CellDep> {
258        self.offchain.resolve(script)
259    }
260}
261
262/// A header_dep resolver use ckb jsonrpc client as backend
263pub struct DefaultHeaderDepResolver {
264    pub ckb_client: CkbRpcAsyncClient,
265}
266impl DefaultHeaderDepResolver {
267    pub fn new(ckb_client: &str) -> DefaultHeaderDepResolver {
268        let ckb_client = CkbRpcAsyncClient::new(ckb_client);
269        DefaultHeaderDepResolver { ckb_client }
270    }
271}
272
273#[cfg_attr(target_arch="wasm32", async_trait::async_trait(?Send))]
274#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
275impl HeaderDepResolver for DefaultHeaderDepResolver {
276    async fn resolve_by_tx_async(
277        &self,
278        tx_hash: &Byte32,
279    ) -> Result<Option<HeaderView>, anyhow::Error> {
280        if let Some(block_hash) = self
281            .ckb_client
282            .get_transaction(tx_hash.unpack())
283            .await
284            .map_err(|e| anyhow!(e))?
285            .and_then(|tx_with_status| tx_with_status.tx_status.block_hash)
286        {
287            Ok(self
288                .ckb_client
289                .get_header(block_hash)
290                .await
291                .map_err(Box::new)?
292                .map(Into::into))
293        } else {
294            Ok(None)
295        }
296    }
297    async fn resolve_by_number_async(
298        &self,
299        number: u64,
300    ) -> Result<Option<HeaderView>, anyhow::Error> {
301        Ok(self
302            .ckb_client
303            .get_header_by_number(number.into())
304            .await
305            .map_err(|e| anyhow!(e))?
306            .map(Into::into))
307    }
308}
309
310/// A cell collector use ckb-indexer as backend
311#[derive(Clone)]
312pub struct DefaultCellCollector {
313    pub indexer_client: IndexerRpcAsyncClient,
314    pub ckb_client: CkbRpcAsyncClient,
315    pub offchain: OffchainCellCollector,
316    pub acceptable_indexer_leftbehind: u64,
317}
318
319impl DefaultCellCollector {
320    pub fn new(ckb_client: &str) -> DefaultCellCollector {
321        let indexer_client = IndexerRpcAsyncClient::new(ckb_client);
322        let ckb_client = CkbRpcAsyncClient::new(ckb_client);
323        DefaultCellCollector {
324            indexer_client,
325            ckb_client,
326            offchain: OffchainCellCollector::default(),
327            acceptable_indexer_leftbehind: 1,
328        }
329    }
330
331    #[cfg(not(target_arch = "wasm32"))]
332    pub fn new_with_timeout(
333        ckb_client: &str,
334        timeout: std::time::Duration,
335    ) -> Result<Self, anyhow::Error> {
336        let indexer_client =
337            IndexerRpcAsyncClient::with_builder(ckb_client, |builder| builder.timeout(timeout))?;
338        let ckb_client =
339            CkbRpcAsyncClient::with_builder(ckb_client, |builder| builder.timeout(timeout))?;
340        Ok(DefaultCellCollector {
341            indexer_client,
342            ckb_client,
343            offchain: OffchainCellCollector::default(),
344            acceptable_indexer_leftbehind: 1,
345        })
346    }
347
348    /// THe acceptable ckb-indexer leftbehind block number (default = 1)
349    pub fn acceptable_indexer_leftbehind(&self) -> u64 {
350        self.acceptable_indexer_leftbehind
351    }
352    /// Set the acceptable ckb-indexer leftbehind block number
353    pub fn set_acceptable_indexer_leftbehind(&mut self, value: u64) {
354        self.acceptable_indexer_leftbehind = value;
355    }
356    #[cfg(not(target_arch = "wasm32"))]
357    /// wrapper check_ckb_chain_async future
358    pub fn check_ckb_chain(&mut self) -> Result<(), CellCollectorError> {
359        crate::rpc::block_on(self.check_ckb_chain_async())
360    }
361    /// Check if ckb-indexer synced with ckb node. This will check every 50ms for 100 times (more than 5s in total, since ckb-indexer's poll interval is 2.0s).
362    pub async fn check_ckb_chain_async(&mut self) -> Result<(), CellCollectorError> {
363        let tip_number = self
364            .ckb_client
365            .get_tip_block_number()
366            .await
367            .map_err(|err| CellCollectorError::Internal(err.into()))?;
368
369        for _ in 0..100 {
370            match self
371                .indexer_client
372                .get_indexer_tip()
373                .await
374                .map_err(|err| CellCollectorError::Internal(err.into()))?
375            {
376                Some(Tip { block_number, .. }) => {
377                    if tip_number.value()
378                        > block_number.value() + self.acceptable_indexer_leftbehind
379                    {
380                        #[cfg(not(target_arch = "wasm32"))]
381                        tokio::time::sleep(Duration::from_millis(50)).await;
382                        #[cfg(target_arch = "wasm32")]
383                        tokio_with_wasm::time::sleep(Duration::from_millis(50)).await;
384                    } else {
385                        return Ok(());
386                    }
387                }
388                None => {
389                    return Err(CellCollectorError::Other(anyhow!(
390                        "ckb-indexer server not synced"
391                    )));
392                }
393            }
394        }
395        Err(CellCollectorError::Other(anyhow!(
396            "ckb-indexer server inconsistent with currently connected ckb node or not synced!"
397        )))
398    }
399}
400
401#[cfg_attr(target_arch="wasm32", async_trait::async_trait(?Send))]
402#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
403impl CellCollector for DefaultCellCollector {
404    async fn collect_live_cells_async(
405        &mut self,
406        query: &CellQueryOptions,
407        apply_changes: bool,
408    ) -> Result<(Vec<LiveCell>, u64), CellCollectorError> {
409        let max_mature_number = get_max_mature_number_async(&self.ckb_client)
410            .await
411            .map_err(|err| CellCollectorError::Internal(anyhow!(err)))?;
412        self.offchain.max_mature_number = max_mature_number;
413        let tip_num = self
414            .ckb_client
415            .get_tip_block_number()
416            .await
417            .map_err(|err| CellCollectorError::Internal(anyhow!(err)))?
418            .value();
419        let CollectResult {
420            cells,
421            rest_cells,
422            mut total_capacity,
423        } = self.offchain.collect(query, tip_num);
424        let mut cells: Vec<_> = cells.into_iter().map(|c| c.0).collect();
425        if total_capacity < query.min_total_capacity {
426            self.check_ckb_chain_async().await?;
427            let order = match query.order {
428                QueryOrder::Asc => Order::Asc,
429                QueryOrder::Desc => Order::Desc,
430            };
431            let mut ret_cells: HashMap<_, _> = cells
432                .into_iter()
433                .map(|c| (c.out_point.clone(), c))
434                .collect();
435            let locked_cells = self.offchain.locked_cells.clone();
436            let search_key = SearchKey::from(query.clone());
437            const MAX_LIMIT: u32 = 4096;
438            let mut limit: u32 = query.limit.unwrap_or(16);
439            let mut last_cursor: Option<json_types::JsonBytes> = None;
440            while total_capacity < query.min_total_capacity {
441                let page = self
442                    .indexer_client
443                    .get_cells(search_key.clone(), order.clone(), limit.into(), last_cursor)
444                    .await
445                    .map_err(|err| CellCollectorError::Internal(err.into()))?;
446                if page.objects.is_empty() {
447                    break;
448                }
449                for cell in page.objects {
450                    let live_cell = LiveCell::from(cell);
451                    if !query.match_cell(&live_cell, max_mature_number)
452                        || locked_cells.contains_key(&(
453                            live_cell.out_point.tx_hash().unpack(),
454                            live_cell.out_point.index().unpack(),
455                        ))
456                    {
457                        continue;
458                    }
459                    let capacity: u64 = live_cell.output.capacity().unpack();
460                    // use cell from indexer to replace offchain cell
461                    if ret_cells
462                        .insert(live_cell.out_point.clone(), live_cell)
463                        .is_none()
464                    {
465                        total_capacity += capacity;
466                    }
467                    if total_capacity >= query.min_total_capacity {
468                        break;
469                    }
470                }
471                last_cursor = Some(page.last_cursor);
472                if limit < MAX_LIMIT {
473                    limit *= 2;
474                }
475            }
476            cells = ret_cells.into_values().collect();
477        }
478        if apply_changes {
479            self.offchain.live_cells = rest_cells;
480            for cell in &cells {
481                self.lock_cell(cell.out_point.clone(), tip_num)?;
482            }
483        }
484        Ok((cells, total_capacity))
485    }
486
487    fn lock_cell(
488        &mut self,
489        out_point: OutPoint,
490        tip_block_number: u64,
491    ) -> Result<(), CellCollectorError> {
492        self.offchain.lock_cell(out_point, tip_block_number)
493    }
494    fn apply_tx(
495        &mut self,
496        tx: Transaction,
497        tip_block_number: u64,
498    ) -> Result<(), CellCollectorError> {
499        self.offchain.apply_tx(tx, tip_block_number)
500    }
501    fn reset(&mut self) {
502        self.offchain.reset();
503    }
504}
505
506pub struct DefaultTxDepProviderInner {
507    pub rpc_client: CkbRpcAsyncClient,
508    tx_cache: LruCache<Byte32, TransactionView>,
509    cell_cache: LruCache<OutPoint, (CellOutput, Bytes)>,
510    header_cache: LruCache<Byte32, HeaderView>,
511    offchain_cache: OffchainTransactionDependencyProvider,
512}
513
514/// A transaction dependency provider use ckb rpc client as backend, and with LRU cache supported
515pub struct DefaultTransactionDependencyProvider {
516    // since we will mainly deal with LruCache, so use Mutex here
517    pub inner: Arc<Mutex<DefaultTxDepProviderInner>>,
518}
519
520impl Clone for DefaultTransactionDependencyProvider {
521    fn clone(&self) -> DefaultTransactionDependencyProvider {
522        let inner = Arc::clone(&self.inner);
523        DefaultTransactionDependencyProvider { inner }
524    }
525}
526
527impl DefaultTransactionDependencyProvider {
528    /// Arguments:
529    ///   * `url` is the ckb http jsonrpc server url
530    ///   * When `cache_capacity` is 0 for not using cache.
531    pub fn new(url: &str, cache_capacity: usize) -> DefaultTransactionDependencyProvider {
532        let rpc_client = CkbRpcAsyncClient::new(url);
533        let inner = DefaultTxDepProviderInner {
534            rpc_client,
535            tx_cache: LruCache::new(cache_capacity),
536            cell_cache: LruCache::new(cache_capacity),
537            header_cache: LruCache::new(cache_capacity),
538            offchain_cache: OffchainTransactionDependencyProvider::new(),
539        };
540        DefaultTransactionDependencyProvider {
541            inner: Arc::new(Mutex::new(inner)),
542        }
543    }
544    #[cfg(not(target_arch = "wasm32"))]
545    pub fn apply_tx(
546        &mut self,
547        tx: Transaction,
548        tip_block_number: u64,
549    ) -> Result<(), TransactionDependencyError> {
550        crate::rpc::block_on(self.apply_tx_async(tx, tip_block_number))
551    }
552
553    pub async fn apply_tx_async(
554        &mut self,
555        tx: Transaction,
556        tip_block_number: u64,
557    ) -> Result<(), TransactionDependencyError> {
558        let mut inner = self.inner.lock().await;
559        inner.offchain_cache.apply_tx(tx, tip_block_number)?;
560        Ok(())
561    }
562    #[cfg(not(target_arch = "wasm32"))]
563    pub fn get_cell_with_data(
564        &self,
565        out_point: &OutPoint,
566    ) -> Result<(CellOutput, Bytes), TransactionDependencyError> {
567        crate::rpc::block_on(self.get_cell_with_data_async(out_point))
568    }
569
570    pub async fn get_cell_with_data_async(
571        &self,
572        out_point: &OutPoint,
573    ) -> Result<(CellOutput, Bytes), TransactionDependencyError> {
574        let mut inner = self.inner.lock().await;
575        if let Some(pair) = inner.cell_cache.get(out_point) {
576            return Ok(pair.clone());
577        }
578
579        let cell_with_status = inner
580            .rpc_client
581            .get_live_cell(out_point.clone().into(), true)
582            .await
583            .map_err(|err| TransactionDependencyError::Other(err.into()))?;
584        if cell_with_status.status != "live" {
585            return Err(TransactionDependencyError::Other(anyhow!(
586                "invalid cell status: {:?}",
587                cell_with_status.status
588            )));
589        }
590        let cell = cell_with_status.cell.unwrap();
591        let output = CellOutput::from(cell.output);
592        let output_data = cell.data.unwrap().content.into_bytes();
593        inner
594            .cell_cache
595            .put(out_point.clone(), (output.clone(), output_data.clone()));
596        Ok((output, output_data))
597    }
598}
599
600#[cfg_attr(target_arch="wasm32", async_trait::async_trait(?Send))]
601#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
602impl TransactionDependencyProvider for DefaultTransactionDependencyProvider {
603    async fn get_transaction_async(
604        &self,
605        tx_hash: &Byte32,
606    ) -> Result<TransactionView, TransactionDependencyError> {
607        let mut inner = self.inner.lock().await;
608        if let Some(tx) = inner.tx_cache.get(tx_hash) {
609            return Ok(tx.clone());
610        }
611        let ret: Result<TransactionView, TransactionDependencyError> =
612            inner.offchain_cache.get_transaction_async(tx_hash).await;
613        if ret.is_ok() {
614            return ret;
615        }
616        let tx_with_status = inner
617            .rpc_client
618            .get_transaction(tx_hash.unpack())
619            .await
620            .map_err(|err| TransactionDependencyError::Other(err.into()))?
621            .ok_or_else(|| TransactionDependencyError::NotFound("transaction".to_string()))?;
622        if tx_with_status.tx_status.status != json_types::Status::Committed {
623            return Err(TransactionDependencyError::Other(anyhow!(
624                "invalid transaction status: {:?}",
625                tx_with_status.tx_status
626            )));
627        }
628        let tx = match tx_with_status.transaction.unwrap().inner {
629            Either::Left(t) => Transaction::from(t.inner).into_view(),
630            Either::Right(bytes) => TransactionReader::from_slice(bytes.as_bytes())
631                .map(|reader| reader.to_entity().into_view())
632                .map_err(|err| anyhow!("invalid molecule encoded TransactionView: {}", err))?,
633        };
634        inner.tx_cache.put(tx_hash.clone(), tx.clone());
635        Ok(tx)
636    }
637    async fn get_cell_async(
638        &self,
639        out_point: &OutPoint,
640    ) -> Result<CellOutput, TransactionDependencyError> {
641        {
642            let inner = self.inner.lock().await;
643            let ret = inner.offchain_cache.get_cell_async(out_point).await;
644            if ret.is_ok() {
645                return ret;
646            }
647        }
648        self.get_cell_with_data_async(out_point)
649            .await
650            .map(|(output, _)| output)
651    }
652    async fn get_cell_data_async(
653        &self,
654        out_point: &OutPoint,
655    ) -> Result<Bytes, TransactionDependencyError> {
656        {
657            let inner = self.inner.lock().await;
658            let ret = inner.offchain_cache.get_cell_data_async(out_point).await;
659            if ret.is_ok() {
660                return ret;
661            }
662        }
663        self.get_cell_with_data_async(out_point)
664            .await
665            .map(|(_, output_data)| output_data)
666    }
667    async fn get_header_async(
668        &self,
669        block_hash: &Byte32,
670    ) -> Result<HeaderView, TransactionDependencyError> {
671        let mut inner = self.inner.lock().await;
672        if let Some(header) = inner.header_cache.get(block_hash) {
673            return Ok(header.clone());
674        }
675        let header = inner
676            .rpc_client
677            .get_header(block_hash.unpack())
678            .await
679            .map_err(|err| TransactionDependencyError::Other(err.into()))?
680            .map(HeaderView::from)
681            .ok_or_else(|| TransactionDependencyError::NotFound("header".to_string()))?;
682        inner.header_cache.put(block_hash.clone(), header.clone());
683        Ok(header)
684    }
685
686    async fn get_block_extension_async(
687        &self,
688        block_hash: &Byte32,
689    ) -> Result<Option<ckb_types::packed::Bytes>, TransactionDependencyError> {
690        let inner = self.inner.lock().await;
691
692        let block = inner
693            .rpc_client
694            .get_block(block_hash.unpack())
695            .await
696            .map_err(|err| TransactionDependencyError::Other(err.into()))?;
697        match block {
698            Some(block) => Ok(block.extension.map(ckb_types::packed::Bytes::from)),
699            None => Ok(None),
700        }
701    }
702}
703
704/// A signer use secp256k1 raw key, the id is `blake160(pubkey)`.
705#[derive(Default, Clone)]
706pub struct SecpCkbRawKeySigner {
707    keys: HashMap<H160, secp256k1::SecretKey>,
708}
709
710impl SecpCkbRawKeySigner {
711    pub fn new(keys: HashMap<H160, secp256k1::SecretKey>) -> SecpCkbRawKeySigner {
712        SecpCkbRawKeySigner { keys }
713    }
714    pub fn new_with_secret_keys(keys: Vec<secp256k1::SecretKey>) -> SecpCkbRawKeySigner {
715        let mut signer = SecpCkbRawKeySigner::default();
716        for key in keys {
717            signer.add_secret_key(key);
718        }
719        signer
720    }
721    pub fn add_secret_key(&mut self, key: secp256k1::SecretKey) {
722        let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &key);
723        let hash160 = H160::from_slice(&blake2b_256(&pubkey.serialize()[..])[0..20])
724            .expect("Generate hash(H160) from pubkey failed");
725        self.keys.insert(hash160, key);
726    }
727
728    /// Create SecpkRawKeySigner from secret keys for ethereum algorithm.
729    pub fn new_with_ethereum_secret_keys(keys: Vec<secp256k1::SecretKey>) -> SecpCkbRawKeySigner {
730        let mut signer = SecpCkbRawKeySigner::default();
731        for key in keys {
732            signer.add_ethereum_secret_key(key);
733        }
734        signer
735    }
736    /// Add a ethereum secret key
737    pub fn add_ethereum_secret_key(&mut self, key: secp256k1::SecretKey) {
738        let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &key);
739        let hash160 = keccak160(Pubkey::from(pubkey).as_ref());
740        self.keys.insert(hash160, key);
741    }
742}
743
744impl Signer for SecpCkbRawKeySigner {
745    fn match_id(&self, id: &[u8]) -> bool {
746        id.len() == 20 && self.keys.contains_key(&H160::from_slice(id).unwrap())
747    }
748
749    fn sign(
750        &self,
751        id: &[u8],
752        message: &[u8],
753        recoverable: bool,
754        _tx: &TransactionView,
755    ) -> Result<Bytes, SignerError> {
756        if !self.match_id(id) {
757            return Err(SignerError::IdNotFound);
758        }
759        if message.len() != 32 {
760            return Err(SignerError::InvalidMessage(format!(
761                "expected length: 32, got: {}",
762                message.len()
763            )));
764        }
765        let msg =
766            secp256k1::Message::from_digest_slice(message).expect("Convert to message failed");
767        let key = self.keys.get(&H160::from_slice(id).unwrap()).unwrap();
768        if recoverable {
769            let sig = SECP256K1.sign_ecdsa_recoverable(&msg, key);
770            Ok(Bytes::from(serialize_signature(&sig).to_vec()))
771        } else {
772            let sig = SECP256K1.sign_ecdsa(&msg, key);
773            Ok(Bytes::from(sig.serialize_compact().to_vec()))
774        }
775    }
776}
777
778impl Drop for SecpCkbRawKeySigner {
779    fn drop(&mut self) {
780        for (_, mut secret_key) in self.keys.drain() {
781            zeroize_privkey(&mut secret_key);
782        }
783    }
784}
785#[cfg(test)]
786mod anyhow_tests {
787    use anyhow::anyhow;
788    #[test]
789    fn test_parse_genesis_info_error() {
790        let error = super::ParseGenesisInfoError::DataHashNotFound("DataHashNotFound".to_string());
791        let error = anyhow!(error);
792        assert_eq!("data not found: `DataHashNotFound`", error.to_string());
793    }
794}