Skip to content

Commit dce747f

Browse files
Jouzocanonbrother
andauthored
Ocean: Initial Scan support (#2829)
* Fix next not being set * Temporary fix of burn address * Add blocks/:hash/transactions endpoint * Add support for querying list Ascending * handle ocean api network * Don't handle including_start at the iter level * Column implementation to fix ser/deser order issue on usize * Index txs from 0 * Handle including_start through Paginate trait --------- Co-authored-by: canonbrother <w.canonbrother@gmail.com>
1 parent d2e4d63 commit dce747f

25 files changed

Lines changed: 323 additions & 182 deletions

File tree

lib/Cargo.lock

Lines changed: 88 additions & 96 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/ain-db/src/lib.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use std::{
99
use anyhow::format_err;
1010
use bincode;
1111
use rocksdb::{
12-
BlockBasedOptions, Cache, ColumnFamily, ColumnFamilyDescriptor, DBIterator, IteratorMode,
13-
Options, DB,
12+
BlockBasedOptions, Cache, ColumnFamily, ColumnFamilyDescriptor, DBIterator, Direction,
13+
IteratorMode, Options, DB,
1414
};
1515
use serde::{de::DeserializeOwned, Serialize};
1616

@@ -181,16 +181,22 @@ where
181181
pub fn iter(
182182
&self,
183183
from: Option<C::Index>,
184+
direction: Direction,
184185
) -> Result<impl Iterator<Item = Result<(C::Index, C::Type)>> + '_> {
185-
let skip = if from.as_ref().is_some() { 1 } else { 0 };
186186
let index = from
187187
.as_ref()
188188
.map(|i| C::key(i))
189189
.transpose()?
190190
.unwrap_or_default();
191-
let iterator_mode = from.map_or(IteratorMode::End, |_| {
192-
IteratorMode::From(&index, rocksdb::Direction::Reverse)
193-
});
191+
192+
let iterator_mode = match direction {
193+
Direction::Forward => from.map_or(IteratorMode::Start, |_| {
194+
IteratorMode::From(&index, Direction::Forward)
195+
}),
196+
Direction::Reverse => from.map_or(IteratorMode::End, |_| {
197+
IteratorMode::From(&index, Direction::Reverse)
198+
}),
199+
};
194200
Ok(self
195201
.backend
196202
.iterator_cf::<C>(self.handle()?, iterator_mode)
@@ -199,8 +205,7 @@ where
199205
let value = bincode::deserialize(&value)?;
200206
let key = C::get_key(key)?;
201207
Ok((key, value))
202-
})
203-
.skip(skip))
208+
}))
204209
}
205210
}
206211

lib/ain-evm/src/storage/block_store.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,10 @@ impl Rollback for BlockStore {
237237
let block_deployed_codes_cf = self.column::<columns::BlockDeployedCodeHashes>();
238238
let address_codes_cf = self.column::<columns::AddressCodeMap>();
239239

240-
for item in block_deployed_codes_cf.iter(Some((block.header.number, H160::zero())))? {
240+
for item in block_deployed_codes_cf.iter(
241+
Some((block.header.number, H160::zero())),
242+
rocksdb::Direction::Reverse,
243+
)? {
241244
let ((block_number, address), hash) = item?;
242245

243246
if block_number == block.header.number {
@@ -325,7 +328,11 @@ impl BlockStore {
325328
let response_max_size = usize::try_from(ain_cpp_imports::get_max_response_byte_size())
326329
.map_err(|_| format_err!("failed to convert response size limit to usize"))?;
327330

328-
for item in self.column::<C>().iter(from)?.take(limit) {
331+
for item in self
332+
.column::<C>()
333+
.iter(from, rocksdb::Direction::Reverse)?
334+
.take(limit)
335+
{
329336
let (k, v) = item?;
330337

331338
if out.len() > response_max_size {

lib/ain-macros/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ pub fn repository_derive(input: TokenStream) -> TokenStream {
113113
Ok(self.col.delete(id)?)
114114
}
115115

116-
fn list<'a>(&'a self, from: Option<#key_type_ident>) -> Result<Box<dyn Iterator<Item = std::result::Result<(#key_type_ident, #value_type_ident), ain_db::DBError>> + 'a>>
116+
fn list<'a>(&'a self, from: Option<#key_type_ident>, dir: crate::storage::SortOrder) -> Result<Box<dyn Iterator<Item = std::result::Result<(#key_type_ident, #value_type_ident), ain_db::DBError>> + 'a>>
117117
{
118-
let it = self.col.iter(from)?;
118+
let it = self.col.iter(from, dir.into())?;
119119
Ok(Box::new(it))
120120
}
121121
}

lib/ain-ocean/src/api/block.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ use super::{
1212
AppContext,
1313
};
1414
use crate::{
15+
api::common::Paginate,
1516
error::{ApiError, Error},
16-
model::Block,
17+
model::{Block, Transaction},
1718
repository::RepositoryOps,
19+
storage::SortOrder,
1820
Result,
1921
};
2022

@@ -46,6 +48,7 @@ async fn list_blocks(
4648
) -> Result<ApiPagedResponse<Block>> {
4749
let next = query
4850
.next
51+
.as_ref()
4952
.map(|q| {
5053
let height = q
5154
.parse::<u32>()
@@ -58,8 +61,8 @@ async fn list_blocks(
5861
.services
5962
.block
6063
.by_height
61-
.list(next)?
62-
.take(query.size)
64+
.list(next, SortOrder::Descending)?
65+
.paginate(&query)
6366
.map(|item| {
6467
let (_, id) = item?;
6568
let b = ctx
@@ -95,11 +98,43 @@ async fn get_block(
9598
Ok(Response::new(block))
9699
}
97100

101+
#[ocean_endpoint]
98102
async fn get_transactions(
99103
Path(hash): Path<BlockHash>,
104+
Query(query): Query<PaginationQuery>,
100105
Extension(ctx): Extension<Arc<AppContext>>,
101-
) -> String {
102-
format!("Transactions for block with hash {}", hash)
106+
) -> Result<ApiPagedResponse<Transaction>> {
107+
let next = query.next.as_ref().map_or(Ok((hash, 0)), |q| {
108+
let height = q
109+
.parse::<usize>()
110+
.map_err(|_| format_err!("Invalid height"))?;
111+
Ok::<(BlockHash, usize), Error>((hash, height))
112+
})?;
113+
114+
let txs = ctx
115+
.services
116+
.transaction
117+
.by_block_hash
118+
.list(Some(next), SortOrder::Ascending)?
119+
.paginate(&query)
120+
.take_while(|item| match item {
121+
Ok(((h, _), _)) => h == &hash,
122+
_ => true,
123+
})
124+
.map(|item| {
125+
let (_, id) = item?;
126+
let tx = ctx
127+
.services
128+
.transaction
129+
.by_id
130+
.get(&id)?
131+
.ok_or("Missing tx index")?;
132+
133+
Ok(tx)
134+
})
135+
.collect::<Result<Vec<_>>>()?;
136+
137+
Ok(ApiPagedResponse::of(txs, query.size, |tx| tx.order))
103138
}
104139

105140
// Get highest indexed block

lib/ain-ocean/src/api/common.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,27 +107,32 @@ pub fn find_token_balance(tokens: Vec<String>, symbol: &str) -> Decimal {
107107
/// .list_loan_schemes()
108108
/// .await?
109109
/// .into_iter()
110-
/// .paginate(&query, skip_while)
110+
/// .fake_paginate(&query, skip_while)
111111
/// .collect();
112112
///
113113
/// assert!(res.len() <= query.size, "The result should not contain more items than the specified limit");
114114
/// assert!(res[0].id > query.next.unwrap(), "The result should start after the requested start id");
115115
/// ```
116116
pub trait Paginate<'a, T>: Iterator<Item = T> + Sized {
117-
fn paginate<F>(
117+
fn fake_paginate<F>(
118118
self,
119119
query: &PaginationQuery,
120120
skip_while: F,
121121
) -> Box<dyn Iterator<Item = T> + 'a>
122122
where
123123
F: FnMut(&T) -> bool + 'a;
124+
fn paginate(self, query: &PaginationQuery) -> Box<dyn Iterator<Item = T> + 'a>;
124125
}
125126

126127
impl<'a, T, I> Paginate<'a, T> for I
127128
where
128129
I: Iterator<Item = T> + 'a,
129130
{
130-
fn paginate<F>(self, query: &PaginationQuery, skip_while: F) -> Box<dyn Iterator<Item = T> + 'a>
131+
fn fake_paginate<F>(
132+
self,
133+
query: &PaginationQuery,
134+
skip_while: F,
135+
) -> Box<dyn Iterator<Item = T> + 'a>
131136
where
132137
F: FnMut(&T) -> bool + 'a,
133138
{
@@ -137,4 +142,7 @@ where
137142
.take(query.size),
138143
)
139144
}
145+
fn paginate(self, query: &PaginationQuery) -> Box<dyn Iterator<Item = T> + 'a> {
146+
Box::new(self.skip(query.next.is_some() as usize).take(query.size))
147+
}
140148
}

lib/ain-ocean/src/api/loan.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use crate::{
2727
error::{ApiError, Error, NotFoundKind},
2828
model::VaultAuctionBatchHistory,
2929
repository::RepositoryOps,
30+
storage::SortOrder,
3031
Result,
3132
};
3233

@@ -63,7 +64,7 @@ async fn list_scheme(
6364
.list_loan_schemes()
6465
.await?
6566
.into_iter()
66-
.paginate(&query, skip_while)
67+
.fake_paginate(&query, skip_while)
6768
.map(Into::into)
6869
.collect();
6970
Ok(ApiPagedResponse::of(res, query.size, |loan_scheme| {
@@ -76,7 +77,6 @@ async fn get_scheme(
7677
Path(scheme_id): Path<String>,
7778
Extension(ctx): Extension<Arc<AppContext>>,
7879
) -> Result<Response<LoanSchemeData>> {
79-
println!("[get_scheme]");
8080
Ok(Response::new(
8181
ctx.client.get_loan_scheme(scheme_id).await?.into(),
8282
))
@@ -120,7 +120,7 @@ async fn list_collateral_token(
120120

121121
let fut = tokens
122122
.into_iter()
123-
.paginate(&query, skip_while)
123+
.fake_paginate(&query, skip_while)
124124
.map(|v| async {
125125
let (id, info) = get_token_cached(&ctx, &v.token_id).await?;
126126
Ok::<CollateralToken, Error>(CollateralToken::from_with_id(id, v, info))
@@ -187,7 +187,7 @@ async fn list_loan_token(
187187
interest: el.interest,
188188
})
189189
})
190-
.paginate(&query, |token| match &query.next {
190+
.fake_paginate(&query, |token| match &query.next {
191191
None => false,
192192
Some(v) => v != &token.id,
193193
})
@@ -259,7 +259,10 @@ async fn list_vault_auction_history(
259259
.services
260260
.auction
261261
.by_height
262-
.list(Some((vault_id, batch_index, next.0, next.1)))?
262+
.list(
263+
Some((vault_id, batch_index, next.0, next.1)),
264+
SortOrder::Descending,
265+
)?
263266
.take(size)
264267
.take_while(|item| match item {
265268
Ok((k, _)) => k.0 == vault_id && k.1 == batch_index,

lib/ain-ocean/src/api/masternode.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ use super::{
1616
AppContext,
1717
};
1818
use crate::{
19+
api::common::Paginate,
1920
error::{ApiError, Error, NotFoundKind},
2021
model::Masternode,
2122
repository::RepositoryOps,
23+
storage::SortOrder,
2224
Result,
2325
};
2426

@@ -105,6 +107,7 @@ async fn list_masternodes(
105107
) -> Result<ApiPagedResponse<MasternodeData>> {
106108
let next = query
107109
.next
110+
.as_ref()
108111
.map(|q| {
109112
let height = q[0..8]
110113
.parse::<u32>()
@@ -121,8 +124,8 @@ async fn list_masternodes(
121124
.services
122125
.masternode
123126
.by_height
124-
.list(next)?
125-
.take(query.size)
127+
.list(next, SortOrder::Descending)?
128+
.paginate(&query)
126129
.map(|item| {
127130
let ((_, id), _) = item?;
128131
let mn = ctx

lib/ain-ocean/src/api/mod.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mod query;
1818
mod response;
1919
mod stats;
2020
mod tokens;
21-
// mod transactions;
21+
mod transactions;
2222

2323
use defichain_rpc::Client;
2424
use serde::{Deserialize, Serialize};
@@ -59,19 +59,24 @@ pub async fn ocean_router(services: &Arc<Services>, client: Arc<Client>) -> Resu
5959
services: services.clone(),
6060
});
6161

62-
Ok(Router::new()
63-
// .nest("/address", address::router(Arc::clone(&context)))
64-
.nest("/governance", governance::router(Arc::clone(&context)))
65-
.nest("/loans", loan::router(Arc::clone(&context)))
66-
.nest("/fee", fee::router(Arc::clone(&context)))
67-
.nest("/masternodes", masternode::router(Arc::clone(&context)))
68-
// .nest("/oracles", oracle::router(Arc::clone(&context)))
69-
// .nest("/poolpairs", poolpairs::router(Arc::clone(&context)))
70-
// .nest("/prices", prices::router(Arc::clone(&context)))
71-
// .nest("/rawtx", rawtx::router(Arc::clone(&context)))
72-
.nest("/stats", stats::router(Arc::clone(&context)))
73-
.nest("/tokens", tokens::router(Arc::clone(&context)))
74-
// .nest("/transactions", transactions::router(Arc::clone(&context)))
75-
.nest("/blocks", block::router(Arc::clone(&context)))
76-
.fallback(not_found))
62+
let network = ain_cpp_imports::get_network();
63+
64+
Ok(Router::new().nest(
65+
format!("/v0/{network}").as_str(),
66+
Router::new()
67+
// .nest("/address/", address::router(Arc::clone(&context)))
68+
.nest("/governance", governance::router(Arc::clone(&context)))
69+
.nest("/loans", loan::router(Arc::clone(&context)))
70+
.nest("/fee", fee::router(Arc::clone(&context)))
71+
.nest("/masternodes", masternode::router(Arc::clone(&context)))
72+
// .nest("/oracles", oracle::router(Arc::clone(&context)))
73+
// .nest("/poolpairs", poolpairs::router(Arc::clone(&context)))
74+
// .nest("/prices", prices::router(Arc::clone(&context)))
75+
// .nest("/rawtx", rawtx::router(Arc::clone(&context)))
76+
.nest("/stats", stats::router(Arc::clone(&context)))
77+
.nest("/tokens", tokens::router(Arc::clone(&context)))
78+
.nest("/transactions", transactions::router(Arc::clone(&context)))
79+
.nest("/blocks", block::router(Arc::clone(&context)))
80+
.fallback(not_found),
81+
))
7782
}

lib/ain-ocean/src/api/query.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub struct PaginationQuery {
2323
#[serde_as(as = "DisplayFromStr")]
2424
#[serde(default = "default_pagination_size")]
2525
pub size: usize,
26+
#[serde(default)]
2627
#[serde(deserialize_with = "undefined_to_none")]
2728
pub next: Option<String>,
2829
}

0 commit comments

Comments
 (0)