Skip to content

Commit

Permalink
API:GET /colors should return color_id and block_height pairs sorted …
Browse files Browse the repository at this point in the history
…in descending order of block_height use
  • Loading branch information
Yamaguchi committed Mar 18, 2024
1 parent ef4a407 commit 4f17079
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 54 deletions.
4 changes: 2 additions & 2 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ For example: `{ "1": 87.882, "2": 87.882, "3": 87.882, "4": 87.882, "5": 81.129,

### `GET /colors[/:last_seen_color_id]`

Returns a list of the 25 `color_id`s
Returns a list of the 25 (`color_id`, `block_height`) pairs

if `last_seen_color_id` specified, returns a list of the next 25 `color_id`s after specified `last_seen_color_id`
if `last_seen_color_id` specified, returns a list of the next 25 pairs after specified `last_seen_color_id`


### `GET /color/:color_id`
Expand Down
6 changes: 3 additions & 3 deletions doc/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ Each output results in the following new row:

* `"O{txid}{vout}" → "{scriptpubkey}{value}"`

If transaction output include colored coins, it results in the following new row:
* `c{color_id}` -> ""`

When the indexer is synced up to the tip of the chain, the hash of the tip is saved as following:

* `"t" → "{blockhash}"`
Expand All @@ -57,6 +54,9 @@ Each spending input (except the coinbase) results in the following new rows (`S`

* `"S{funding-txid:vout}{spending-txid:vin}" → ""`

If transaction output include colored coins, it results in the following new row:
* `"B{block-height}c{color-id}" → ""` (block_height is latest block height which colored coin used)

colored coin's issuances results in the following new rows (`I` is for issuing):

* `"C{color-id}{issuance-height}I{issuing-txid}{value}" → ""`
Expand Down
65 changes: 53 additions & 12 deletions src/new_index/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use crate::new_index::db::DBRow;
use crate::new_index::schema::FullHash;
use crate::util::{full_hash, Bytes};

use super::schema::ColorIdRow;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct ColoredTxHistoryKey {
pub color_id: ColorIdentifier,
Expand Down Expand Up @@ -208,11 +210,10 @@ pub fn index_confirmed_colored_tx(
) {
let history = colored_tx_history(tx, previous_txos_map);

rows.extend(
history.into_iter().map(|(color_id, info)| {
colored_history_row(&color_id, confirmed_height, info).into_row()
}),
);
history.into_iter().for_each(|(color_id, info)| {
rows.push(colored_history_row(&color_id, confirmed_height, info).into_row());
rows.push(ColorIdRow::new(confirmed_height, &color_id).into_row());
});
}

fn colored_history_row(
Expand Down Expand Up @@ -526,13 +527,53 @@ mod tests {
let mut rows = vec![];
index_confirmed_colored_tx(&tx, 10, &previous_txos_map, &mut rows);

assert_eq!(rows.len(), 4);
assert_eq!(rows.len(), 8);

rows.sort_by(|a, b| a.key.cmp(&b.key));
let row0 = rows.get(0).unwrap();
let hex = hex::encode::<Vec<u8>>(row0.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'B'(0x42) |
// height | 4 | 10(0x0a000000) |
// code | 1 | 'c'(0x63) |
// color_id | 33 | c12dceb0cedd7c372c838fea8d46ae863a3c47b2ad0fb950e90ac9d531583ad35e |
assert_eq!(hex, "420a00000063c12dceb0cedd7c372c838fea8d46ae863a3c47b2ad0fb950e90ac9d531583ad35e");

let row1 = rows.get(1).unwrap();
let hex = hex::encode::<Vec<u8>>(row1.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'B'(0x42) |
// height | 4 | 10(0x0a000000) |
// code | 1 | 'c'(0x63) |
// color_id | 33 | c12dceb0cedd7c372c838fea8d46ae863a3c47b2ad0fb950e90ac9d531583ad35e |
assert_eq!(hex, "420a00000063c12dceb0cedd7c372c838fea8d46ae863a3c47b2ad0fb950e90ac9d531583ad35e");

let row2 = rows.get(2).unwrap();
let hex = hex::encode::<Vec<u8>>(row2.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'B'(0x42) |
// height | 4 | 10(0x0a000000) |
// code | 1 | 'c'(0x63) |
// color_id | 33 | c271c99cc3bc21757feed5b712744ebb0f770d5c41d99189f9457495747bf11050 |
assert_eq!(hex, "420a00000063c271c99cc3bc21757feed5b712744ebb0f770d5c41d99189f9457495747bf11050");

let row3 = rows.get(3).unwrap();
let hex = hex::encode::<Vec<u8>>(row3.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'B'(0x42) |
// height | 4 | 10(0x0a000000) |
// code | 1 | 'c'(0x63) |
// color_id | 33 | c271c99cc3bc21757feed5b712744ebb0f770d5c41d99189f9457495747bf11050 |
assert_eq!(hex, "420a00000063c271c99cc3bc21757feed5b712744ebb0f770d5c41d99189f9457495747bf11050");

let row4 = rows.get(4).unwrap();
let hex = hex::encode::<Vec<u8>>(row4.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'C'(0x43) |
// color_id | 33 | c12dceb0cedd7c372c838fea8d46ae863a3c47b2ad0fb950e90ac9d531583ad35e |
// height | 4 | 10(0x0a000000) |
Expand All @@ -541,8 +582,8 @@ mod tests {
// value | 8 | 100(0x64) |
assert_eq!(hex, "43c12dceb0cedd7c372c838fea8d46ae863a3c47b2ad0fb950e90ac9d531583ad35e0a0000000100000059abe954f5636c86484e5e2817d29b915e7f9a9f0294e87c438fd060694a8b1c6400000000000000");

let row1 = rows.get(1).unwrap();
let hex = hex::encode::<Vec<u8>>(row1.key.iter().cloned().collect());
let row5 = rows.get(5).unwrap();
let hex = hex::encode::<Vec<u8>>(row5.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'C'(0x43) |
Expand All @@ -553,8 +594,8 @@ mod tests {
// value | 8 | 100(0x64) |
assert_eq!(hex, "43c12dceb0cedd7c372c838fea8d46ae863a3c47b2ad0fb950e90ac9d531583ad35e0a0000000200000059abe954f5636c86484e5e2817d29b915e7f9a9f0294e87c438fd060694a8b1c6400000000000000");

let row2 = rows.get(2).unwrap();
let hex = hex::encode::<Vec<u8>>(row2.key.iter().cloned().collect());
let row6 = rows.get(6).unwrap();
let hex = hex::encode::<Vec<u8>>(row6.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'C'(0x43) |
Expand All @@ -565,8 +606,8 @@ mod tests {
// value | 8 | 100(0x64) |
assert_eq!(hex, "43c271c99cc3bc21757feed5b712744ebb0f770d5c41d99189f9457495747bf110500a0000000000000059abe954f5636c86484e5e2817d29b915e7f9a9f0294e87c438fd060694a8b1c6400000000000000");

let row3 = rows.get(3).unwrap();
let hex = hex::encode::<Vec<u8>>(row3.key.iter().cloned().collect());
let row7 = rows.get(7).unwrap();
let hex = hex::encode::<Vec<u8>>(row7.key.iter().cloned().collect());
// field | size | value |
//---------------------|------|-----------------------------------------------------------------------|
// prefix | 1 | 'C'(0x43) |
Expand Down
13 changes: 11 additions & 2 deletions src/new_index/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,19 @@ impl Query {
Ok(map)
}

pub fn get_colors(&self, last_seen_color_id: Option<ColorIdentifier>, limit: usize) -> Vec<ColorIdentifier> {
pub fn get_colors(&self, last_seen_color_id: Option<ColorIdentifier>, limit: usize) -> Vec<(ColorIdentifier, u32)> {
let (block_height, color_id) = if let Some(color_id) = last_seen_color_id {
match self.chain.get_height_by_color_id(&color_id) {
Some(height) => (height, Some(color_id)),
_ => (self.daemon.getblockchaininfo().ok().unwrap().blocks, None),
}
} else {
(self.daemon.getblockchaininfo().ok().unwrap().blocks, None)
};

let colors = self
.chain
.get_colors(last_seen_color_id, limit)
.get_colors(block_height, &color_id, limit)
.expect("failed to get colors");
colors
}
Expand Down
78 changes: 44 additions & 34 deletions src/new_index/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,30 +676,29 @@ impl ChainQuery {
update_stats(init_stats, &histories)
}

pub fn get_colors(&self, last_seen_color_id: Option<ColorIdentifier>, limit: usize) -> Result<Vec<ColorIdentifier>> {
let colors: Vec<ColorIdentifier> = self
.store
.txstore_db()
.iter_scan(&ColorIdRow::prefix())
.map(|row|{
ColorIdRow::from_row(row)
})
.map(|row| {
row.key.color_id
})
.skip_while(|color_id| {
// skip until we reach the last_seen_color_id
last_seen_color_id.clone().map_or(false, |last_seen_color_id| last_seen_color_id != color_id.clone())
})
.skip(match last_seen_color_id {
Some(_) => 1, // skip the last_seen_color_id itself
None => 0,
pub fn get_colors(&self, block_height: u32, last_seen_color_id: &Option<ColorIdentifier>, limit: usize) -> Result<Vec<(ColorIdentifier, u32)>> {
let colors = self.store.history_db().iter_scan_reverse(
&ColorIdRow::prefix(),
&ColorIdRow::filter_end(block_height, last_seen_color_id)
).filter_map(|row|{
let row = ColorIdRow::from_row(row);
let result = self.get_height_by_color_id(&row.key.color_id);
// Ignore color_id, block_height pair if the block_height is not the latest
match result {
Some(block_height) if (block_height <= row.key.block_height) => Some((row.key.color_id, row.key.block_height)),
_ => None,
}
})
.skip(1) // Ignore the first element, which is the last_seen_color_id itself
.take(limit)
.collect();
.collect::<Vec<_>>();
Ok(colors)
}

pub fn get_height_by_color_id(&self, color_id: &ColorIdentifier) -> Option<u32> {
self.colored_history_iter_scan_reverse(color_id).map(|c| ColoredTxHistoryRow::from_row(c).key.confirmed_height).next()
}

pub fn get_colored_stats(&self, color_id: &ColorIdentifier) -> Result<ColoredStats> {
let cache: Option<(ColoredStats, usize)> = self
.store
Expand Down Expand Up @@ -1044,10 +1043,6 @@ fn add_transaction(
if is_spendable(txo) {
rows.push(TxOutRow::new(&txid, txo_index, txo).into_row());
}

if let Some((color_id, _)) = txo.script_pubkey.split_color() {
rows.push(ColorIdRow::new(&color_id).into_row())
}
}
}

Expand Down Expand Up @@ -1361,40 +1356,55 @@ impl TxOutRow {
}

#[derive(Serialize, Deserialize)]
struct ColorIdKey {
code: u8,
pub struct ColorIdKey {
block_height: u32,// MUST be serialized as big-endian.
color_id: ColorIdentifier,
}
struct ColorIdRow {
// keys is "B{block-height}c{color-id}" (block_height is latest block height which colored coin used)
// value is ""
pub struct ColorIdRow {
key: ColorIdKey,
}

impl ColorIdRow {
fn new(color_id: &ColorIdentifier) -> ColorIdRow {
pub fn new(block_height: u32, color_id: &ColorIdentifier) -> ColorIdRow {
ColorIdRow {
key: ColorIdKey {
code: b'c',
block_height: block_height,
color_id: color_id.clone()
}
}
}

fn prefix() -> Bytes {
b"c".to_vec()
bincode::serialize(&(b'B'))
.unwrap()
}

fn into_row(self) -> DBRow {
fn filter_end(block_height: u32, color_id: &Option<ColorIdentifier>) -> Bytes {
let filter = if let Some(color_id) = color_id {
bincode::serialize(&(b'B', block_height, b'c', &serialize_color_id(&color_id)))
.unwrap()
} else {
bincode::serialize(&(b'B', block_height + 1, b'c'))
.unwrap()
};
filter
}

pub fn into_row(self) -> DBRow {
DBRow {
key: bincode::serialize(&(b'c', &serialize_color_id(&self.key.color_id))).unwrap(),
key: bincode::serialize(&(b'B', self.key.block_height, b'c', &serialize_color_id(&self.key.color_id))).unwrap(),
value: vec![],
}
}
fn from_row(row: DBRow) -> Self {
let (_prefix, token_type, payload): (u8, u8, [u8; 32]) =

pub fn from_row(row: DBRow) -> Self {
let (_prefix, block_height, _prefix2, token_type, payload): (u8, u32, u8, u8, [u8; 32]) =
bincode::deserialize(&row.key).expect("failed to deserialize ColorIdRow");
ColorIdRow {
key: ColorIdKey {
code: b'c',
block_height: block_height,
color_id: deserialize_color_id(token_type, payload),
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ fn handle_request(
let color_id: Option<ColorIdentifier> = last_seen_color_id.map_or(None,|hex| {
ColorIdentifier::from_hex(hex).ok()
});
let colors: Vec<ColorIdentifier> = query.get_colors(color_id, COLOR_IDS_PER_PAGE);
let colors: Vec<(ColorIdentifier, u32)> = query.get_colors(color_id, COLOR_IDS_PER_PAGE);
json_response(colors, TTL_SHORT)
}

Expand Down

0 comments on commit 4f17079

Please sign in to comment.