-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(sequencer): query full denomination from asset ID (#1067)
## Summary implement ABCI query to get full denomination from an asset ID, if it exists in the latest sequencer state. ## Background useful for block explorers/UIs as txs only have asset IDs, not the full denom. ## Changes - implement ABCI query to get full denomination from an asset ID, if it exists in the latest sequencer state ## Testing unit tests ## Related Issues closes #1053
- Loading branch information
Showing
13 changed files
with
302 additions
and
16 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
crates/astria-core/src/generated/astria.protocol.asset.v1alpha1.rs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod v1alpha1; | ||
|
||
use crate::generated::protocol::asset::v1alpha1 as raw; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
use super::raw; | ||
use crate::primitive::v1::asset::Denom; | ||
|
||
/// The sequencer response to a denomination request for a given asset ID. | ||
#[derive(Clone, Debug, PartialEq)] | ||
pub struct DenomResponse { | ||
pub height: u64, | ||
pub denom: Denom, | ||
} | ||
|
||
impl DenomResponse { | ||
/// Converts a protobuf [`raw::DenomResponse`] to an astria | ||
/// native [`DenomResponse`]. | ||
#[must_use] | ||
pub fn from_raw(proto: &raw::DenomResponse) -> Self { | ||
let raw::DenomResponse { | ||
height, | ||
denom, | ||
} = proto; | ||
Self { | ||
height: *height, | ||
denom: denom.clone().into(), | ||
} | ||
} | ||
|
||
/// Converts an astria native [`DenomResponse`] to a | ||
/// protobuf [`raw::DenomResponse`]. | ||
#[must_use] | ||
pub fn into_raw(self) -> raw::DenomResponse { | ||
raw::DenomResponse::from_native(self) | ||
} | ||
} | ||
|
||
impl raw::DenomResponse { | ||
/// Converts an astria native [`DenomResponse`] to a | ||
/// protobuf [`raw::DenomResponse`]. | ||
#[must_use] | ||
pub fn from_native(native: DenomResponse) -> Self { | ||
let DenomResponse { | ||
height, | ||
denom, | ||
} = native; | ||
Self { | ||
height, | ||
denom: denom.to_string(), | ||
} | ||
} | ||
|
||
/// Converts a protobuf [`raw::DenomResponse`] to an astria | ||
/// native [`DenomResponse`]. | ||
#[must_use] | ||
pub fn into_native(self) -> DenomResponse { | ||
DenomResponse::from_raw(&self) | ||
} | ||
|
||
/// Converts a protobuf [`raw::DenomResponse`] to an astria | ||
/// native [`DenomResponse`] by allocating a new [`v1alpha1::DenomResponse`]. | ||
#[must_use] | ||
pub fn to_native(&self) -> DenomResponse { | ||
self.clone().into_native() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub(crate) mod query; | ||
pub(crate) mod state_ext; | ||
|
||
use std::sync::OnceLock; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
use anyhow::Context as _; | ||
use astria_core::{ | ||
primitive::v1::asset, | ||
protocol::abci::AbciErrorCode, | ||
}; | ||
use cnidarium::Storage; | ||
use prost::Message as _; | ||
use tendermint::abci::{ | ||
request, | ||
response, | ||
}; | ||
|
||
use crate::{ | ||
asset::state_ext::StateReadExt as _, | ||
state_ext::StateReadExt, | ||
}; | ||
|
||
// Retrieve the full asset denomination given the asset ID. | ||
// | ||
// Example: | ||
// `abci-cli query --path=asset/denom/<DENOM_ID>` | ||
pub(crate) async fn denom_request( | ||
storage: Storage, | ||
request: request::Query, | ||
params: Vec<(String, String)>, | ||
) -> response::Query { | ||
use astria_core::protocol::asset::v1alpha1::DenomResponse; | ||
|
||
// use the latest snapshot, as this is a lookup of id->denom | ||
let snapshot = storage.latest_snapshot(); | ||
let asset_id = match preprocess_request(¶ms) { | ||
Ok(asset_id) => asset_id, | ||
Err(err_rsp) => return err_rsp, | ||
}; | ||
|
||
let height = match snapshot.get_block_height().await { | ||
Ok(height) => height, | ||
Err(err) => { | ||
return response::Query { | ||
code: AbciErrorCode::INTERNAL_ERROR.into(), | ||
info: AbciErrorCode::INTERNAL_ERROR.to_string(), | ||
log: format!("failed getting block height: {err:#}"), | ||
..response::Query::default() | ||
}; | ||
} | ||
}; | ||
|
||
let maybe_denom = match snapshot.get_ibc_asset(asset_id).await { | ||
Ok(maybe_denom) => maybe_denom, | ||
Err(err) => { | ||
return response::Query { | ||
code: AbciErrorCode::INTERNAL_ERROR.into(), | ||
info: AbciErrorCode::INTERNAL_ERROR.to_string(), | ||
log: format!("failed to retrieve denomination `{asset_id}`: {err:#}"), | ||
..response::Query::default() | ||
}; | ||
} | ||
}; | ||
|
||
let Some(denom) = maybe_denom else { | ||
return response::Query { | ||
code: AbciErrorCode::VALUE_NOT_FOUND.into(), | ||
info: AbciErrorCode::VALUE_NOT_FOUND.to_string(), | ||
log: format!("failed to retrieve value for denomination ID`{asset_id}`"), | ||
..response::Query::default() | ||
}; | ||
}; | ||
|
||
let payload = DenomResponse { | ||
height, | ||
denom, | ||
} | ||
.into_raw() | ||
.encode_to_vec() | ||
.into(); | ||
|
||
let height = tendermint::block::Height::try_from(height).expect("height must fit into an i64"); | ||
response::Query { | ||
code: tendermint::abci::Code::Ok, | ||
key: request.path.into_bytes().into(), | ||
value: payload, | ||
height, | ||
..response::Query::default() | ||
} | ||
} | ||
|
||
fn preprocess_request(params: &[(String, String)]) -> anyhow::Result<asset::Id, response::Query> { | ||
let Some(asset_id) = params.iter().find_map(|(k, v)| (k == "id").then_some(v)) else { | ||
return Err(response::Query { | ||
code: AbciErrorCode::INVALID_PARAMETER.into(), | ||
info: AbciErrorCode::INVALID_PARAMETER.to_string(), | ||
log: "path did not contain asset ID parameter".into(), | ||
..response::Query::default() | ||
}); | ||
}; | ||
let asset_id = hex::decode(asset_id) | ||
.context("failed decoding hex encoded bytes") | ||
.and_then(|addr| { | ||
asset::Id::try_from_slice(&addr).context("failed constructing asset ID from bytes") | ||
}) | ||
.map_err(|err| response::Query { | ||
code: AbciErrorCode::INVALID_PARAMETER.into(), | ||
info: AbciErrorCode::INVALID_PARAMETER.to_string(), | ||
log: format!("asset ID could not be constructed from provided parameter: {err:#}"), | ||
..response::Query::default() | ||
})?; | ||
Ok(asset_id) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.