Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions crates/cardano/src/eras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,28 @@ impl ChainSummary {
/// `EraTransition::entering_conway`). `None` when no known era has
/// reached Conway.
pub fn first_conway_epoch(&self) -> Option<u64> {
for (protocol, era) in self.iter_past_with_protocol() {
if *protocol >= 9 {
self.first_epoch_with_protocol(9)
}

/// Epoch at which the chain entered Mary — the first era with
/// protocol >= 4, which is where native assets appear. Nothing before
/// it can mint, so listings of assets can start their scan here. `None`
/// when no known era has reached Mary.
pub fn first_mary_epoch(&self) -> Option<u64> {
self.first_epoch_with_protocol(4)
}

/// Start epoch of the first era whose protocol is at least `protocol`.
/// A hard fork can jump over a version, so this is a threshold rather
/// than an equality check.
fn first_epoch_with_protocol(&self, protocol: u16) -> Option<u64> {
for (era_protocol, era) in self.iter_past_with_protocol() {
if *era_protocol >= protocol {
return Some(era.start.epoch);
}
}
match self.protocols.last() {
Some(last) if *last >= 9 => Some(self.edge().start.epoch),
Some(last) if *last >= protocol => Some(self.edge().start.epoch),
_ => None,
}
}
Expand Down Expand Up @@ -367,4 +382,32 @@ mod tests {
jumped.append_era(10, era(10, 480));
assert_eq!(jumped.first_conway_epoch(), Some(480));
}

#[test]
fn first_mary_epoch_finds_first_multi_asset_era() {
// empty summary
assert_eq!(ChainSummary::default().first_mary_epoch(), None);

// Byron, Shelley and Allegra have no native assets
let mut summary = ChainSummary::default();
summary.append_era(1, era(1, 0));
summary.append_era(2, era(2, 208));
summary.append_era(3, era(3, 236));
assert_eq!(summary.first_mary_epoch(), None);

// Mary at the edge
summary.append_era(4, era(4, 251));
assert_eq!(summary.first_mary_epoch(), Some(251));

// later eras keep the original Mary entry
summary.append_era(6, era(6, 290));
summary.append_era(9, era(9, 507));
assert_eq!(summary.first_mary_epoch(), Some(251));

// a chain that starts past Mary (testnets) has assets from epoch 0
let mut recent = ChainSummary::default();
recent.append_era(6, era(6, 0));
recent.append_era(9, era(9, 100));
assert_eq!(recent.first_mary_epoch(), Some(0));
}
}
11 changes: 11 additions & 0 deletions crates/minibf/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum Error {
InvalidBlockNumber,
InvalidBlockHash,
InvalidEpochNumber,
/// An archive scan ran out of block budget before covering the page.
ScanBudgetExceeded,
}

#[derive(Serialize)]
Expand Down Expand Up @@ -129,6 +131,15 @@ impl IntoResponse for Error {
)),
)
.into_response(),
Error::ScanBudgetExceeded => (
StatusCode::SERVICE_UNAVAILABLE,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if "service unavailable" is the right message, consumers might interpret that this is a transient error which is ok to retry later.

Json(ErrorBody::new(
503,
"Service Unavailable",
"The requested page needs more archive blocks than this node is willing to scan.",
)),
)
.into_response(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/minibf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ where
"/txs/{tx_hash}/stakes",
get(routes::txs::by_hash_stakes::<D>),
)
.route("/assets", get(routes::assets::all::<D>))
.route(
"/assets/policy/{policy_id}",
get(routes::assets::by_policy::<D>),
Expand Down
3 changes: 3 additions & 0 deletions crates/minibf/src/routes/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ use crate::{
Facade,
};

mod all;
pub use all::all;

struct OnchainMetadata {
version: Option<OnchainMetadataStandard>,
metadata: HashMap<String, serde_json::Value>,
Expand Down
Loading
Loading