/// Search for a transaction given its hash
Search for a transaction given its hash
Search for a transaction given its hash
pub async fn search_transactions(
        &self,
        msg: models::SearchTransactionsRequest,
    ) -> Result<SearchTransactionsResponse, ApiError> {
        verify_network_id(self.ledger.ledger_canister_id(), &msg.network_identifier)?;
        if let Some(Operator::Or) = msg.operator {
            return Err(ApiError::invalid_request("Operator OR not supported"));
        }
        if msg.coin_identifier.is_some() {
            return Err(ApiError::invalid_request("coin_identifier not supported"));
        }
        if msg.currency.is_some() {
            return Err(ApiError::invalid_request("currency not supported"));
        }
        if msg.status.is_some() {
            return Err(ApiError::invalid_request("status not supported"));
        }
        if msg._type.is_some() {
            return Err(ApiError::invalid_request("type not supported"));
        }
        if msg.address.is_some() {
            return Err(ApiError::invalid_request("address not supported"));
        }
        if msg.success.is_some() {
            return Err(ApiError::invalid_request("success not supported"));
        }
        let max_block = match msg.max_block {
            Some(x) => Some(
                u64::try_from(x)
                    .map_err(|e| ApiError::invalid_request(format!("Invalid max_block: {}", e)))?,
            ),
            None => None,
        };
        let offset = match msg.offset {
            Some(x) => usize::try_from(x)
                .map_err(|e| ApiError::invalid_request(format!("Invalid offset: {}", e)))?,
            None => 0,
        };
        let limit = match msg.limit {
            Some(x) => usize::try_from(x)
                .map_err(|e| ApiError::invalid_request(format!("Invalid limit: {}", e)))?,
            None => usize::MAX,
        };
        let limit = std::cmp::min(limit, 10_000);
        if msg.transaction_identifier.is_none() && msg.account_identifier.is_none() {
            return self.get_blocks_range(max_block, offset, limit).await;
        }
        let blocks = self.ledger.read_blocks().await;
        let last_idx = blocks
            .last_verified()?
            .ok_or_else(|| ApiError::BlockchainEmpty(true, Default::default()))?
            .index;
        let mut heights = Vec::new();
        let mut total_count = 0;
        if let Some(tid) = &msg.transaction_identifier {
            if msg.account_identifier.is_some() {
                return Err(ApiError::invalid_request(
                    "Only one of transaction_identitier and account_identifier should be populated",
                ));
            }
            let tid = ledger_canister::HashOf::try_from(tid)
                .map_err(|e| ApiError::InvalidTransactionId(false, e.into()))?;
            if let Some(i) = blocks.tx_hash_location.get(&tid) {
                heights.push(*i);
                total_count += 1;
            }
        }
        let mut next_offset = None;
        if let Some(aid) = &msg.account_identifier {
            let acc = from_model_account_identifier(aid)
                .map_err(|e| ApiError::InvalidAccountId(false, e.into()))?;
            let hist = blocks.balance_book.store.get_history(&acc, max_block);
            heights = hist
                .iter()
                .rev()
                .map(|(h, _)| *h)
                .filter(|h| *h <= last_idx)
                .skip(offset)
                .collect();
            let cnt = offset
                .checked_add(heights.len())
                .ok_or_else(|| ApiError::internal_error("total count overflow"))?;
            total_count = i64::try_from(cnt).map_err(|e| {
                ApiError::internal_error(format!("Total count does not fit in i64: {}", e))
            })?;
            if heights.len() > limit {
                let next = offset
                    .checked_add(limit)
                    .ok_or_else(|| ApiError::internal_error("offset + limit overflow"))?;
                next_offset = Some(i64::try_from(next).map_err(|e| {
                    ApiError::internal_error(format!("Next offset cannot fit in i64: {}", e))
                })?);
            }
            heights.truncate(limit);
        }
        let mut txs: Vec<BlockTransaction> = Vec::new();
        for i in heights {
            let hb = blocks.get_verified_at(i)?;
            txs.push(BlockTransaction::new(
                convert::block_id(&hb)?,
                convert::transaction(&hb, self.ledger.token_name())?,
            ));
        }
        Ok(SearchTransactionsResponse::new(
            txs,
            total_count,
            next_offset,
        ))
    }
pub async fn search_transactions ( & self , msg : models :: SearchTransactionsRequest , ) -> Result < SearchTransactionsResponse , ApiError > { verify_network_id ( self . ledger . ledger_canister_id ( ) , & msg . network_identifier ) ? ; if let Some ( Operator :: Or ) = msg . operator { return Err ( ApiError :: invalid_request ( " " ) ) ; } if msg . coin_identifier . is_some ( ) { return Err ( ApiError :: invalid_request ( " " ) ) ; } if msg . currency . is_some ( ) { return Err ( ApiError :: invalid_request ( " " ) ) ; } if msg . status . is_some ( ) { return Err ( ApiError :: invalid_request ( " " ) ) ; } if msg . _type . is_some ( ) { return Err ( ApiError :: invalid_request ( " " ) ) ; } if msg . address . is_some ( ) { return Err ( ApiError :: invalid_request ( " " ) ) ; } if msg . success . is_some ( ) { return Err ( ApiError :: invalid_request ( " " ) ) ; } let max_block = match msg . max_block { Some ( x ) => Some ( u64 :: try_from ( x ) . map_err ( | e | ApiError :: invalid_request ( format ! ( " " e ) ) ) ? , ) , None => None , } ; let offset = match msg . offset { Some ( x ) => usize :: try_from ( x ) . map_err ( | e | ApiError :: invalid_request ( format ! ( " " e ) ) ) ? , None => 0 , } ; let limit = match msg . limit { Some ( x ) => usize :: try_from ( x ) . map_err ( | e | ApiError :: invalid_request ( format ! ( " " e ) ) ) ? , None => usize :: MAX , } ; let limit = std :: cmp :: min ( limit , 10_000 ) ; if msg . transaction_identifier . is_none ( ) && msg . account_identifier . is_none ( ) { return self . get_blocks_range ( max_block , offset , limit ) . await ; } let blocks = self . ledger . read_blocks ( ) . await ; let last_idx = blocks . last_verified ( ) ? . ok_or_else ( | | ApiError :: BlockchainEmpty ( true , Default :: default ( ) ) ) ? . index ; let mut heights = Vec :: new ( ) ; let mut total_count = 0 ; if let Some ( tid ) = & msg . transaction_identifier { if msg . account_identifier . is_some ( ) { return Err ( ApiError :: invalid_request ( " " , ) ) ; } let tid = ledger_canister :: HashOf :: try_from ( tid ) . map_err ( | e | ApiError :: InvalidTransactionId ( false , e . into ( ) ) ) ? ; if let Some ( i ) = blocks . tx_hash_location . get ( & tid ) { heights . push ( * i ) ; total_count += 1 ; } } let mut next_offset = None ; if let Some ( aid ) = & msg . account_identifier { let acc = from_model_account_identifier ( aid ) . map_err ( | e | ApiError :: InvalidAccountId ( false , e . into ( ) ) ) ? ; let hist = blocks . balance_book . store . get_history ( & acc , max_block ) ; heights = hist . iter ( ) . rev ( ) . map ( | ( h , _ ) | * h ) . filter ( | h | * h <= last_idx ) . skip ( offset ) . collect ( ) ; let cnt = offset . checked_add ( heights . len ( ) ) . ok_or_else ( | | ApiError :: internal_error ( " " ) ) ? ; total_count = i64 :: try_from ( cnt ) . map_err ( | e | { ApiError :: internal_error ( format ! ( " " e ) ) } ) ? ; if heights . len ( ) > limit { let next = offset . checked_add ( limit ) . ok_or_else ( | | ApiError :: internal_error ( " " ) ) ? ; next_offset = Some ( i64 :: try_from ( next ) . map_err ( | e | { ApiError :: internal_error ( format ! ( " " e ) ) } ) ? ) ; } heights . truncate ( limit ) ; } let mut txs : Vec < BlockTransaction > = Vec :: new ( ) ; for i in heights { let hb = blocks . get_verified_at ( i ) ? ; txs . push ( BlockTransaction :: new ( convert :: block_id ( & hb ) ? , convert :: transaction ( & hb , self . ledger . token_name ( ) ) ? , ) ) ; } Ok ( SearchTransactionsResponse :: new ( txs , total_count , next_offset , ) ) }
Search for a transaction given its hash
Search for a transaction given its hash
&self
msg
models::SearchTransactionsRequest