Skip to content

Commit

Permalink
feat: support forcing protocol version at epoch 0 (#417)
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega authored Dec 24, 2024
1 parent 96e7c18 commit 4147810
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 44 deletions.
15 changes: 8 additions & 7 deletions docs/pages/configuration/schema.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,19 @@ The `storage` section controls how Dolos stores data in the local file system. T

Dolos requires Cardano genesis data to operate. For simplicity sake, we've decided to follow the schema for the existing .json files used by the Haskell node. The `genesis` section indicates how to locate the different json files with genesis data for each required era. The content of the json files should match the ones used in the Haskell node.

| property | type | example |
| ------------ | ------ | ---------------- |
| byron_path | string | "./byron.json" |
| shelley_path | string | "./shelley.json" |
| alonzo_path | string | "./alonzo.json" |
| conway_path | string | "./conway.json" |
| property | type | example |
| ------------- | ------- | ---------------- |
| byron_path | string | "./byron.json" |
| shelley_path | string | "./shelley.json" |
| alonzo_path | string | "./alonzo.json" |
| conway_path | string | "./conway.json" |
| force_protocol | integer | 2 |

- `byron_path`: file path to the Byron json genesis file
- `shelley_path`: file path to the Shelley json genesis file
- `alonzo_path`: file path to the Alonzo json genesis file
- `conway_path`: file path to the Conway json genesis file

- `force_protocol`: (optional) the protocol version to force the node to start from. This is useful for networks such as `preview` which skips the Byron era.

### `sync` section

Expand Down
1 change: 1 addition & 0 deletions src/bin/dolos/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pub fn open_genesis_files(config: &GenesisConfig) -> miette::Result<Genesis> {
shelley: shelley_genesis,
alonzo: alonzo_genesis,
conway: conway_genesis,
force_protocol: config.force_protocol,
})
}

Expand Down
15 changes: 14 additions & 1 deletion src/bin/dolos/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ impl From<&KnownNetwork> for dolos::model::UpstreamConfig {
}
}

impl From<&KnownNetwork> for crate::GenesisConfig {
fn from(value: &KnownNetwork) -> Self {
match value {
KnownNetwork::CardanoPreview => crate::GenesisConfig {
force_protocol: Some(6), // Preview network starts at Alonzo
..Default::default()
},
// KnownNetwork::CardanoSanchonet => todo!(),
_ => crate::GenesisConfig::default(),
}
}
}

impl From<&KnownNetwork> for crate::MithrilConfig {
fn from(value: &KnownNetwork) -> Self {
match value {
Expand Down Expand Up @@ -207,7 +220,7 @@ impl Default for ConfigEditor {
impl ConfigEditor {
fn apply_known_network(mut self, network: Option<&KnownNetwork>) -> Self {
if let Some(network) = network {
self.0.genesis = Default::default();
self.0.genesis = network.into();
self.0.upstream = network.into();
self.0.mithril = Some(network.into());
self.1 = Some(network.clone());
Expand Down
5 changes: 3 additions & 2 deletions src/bin/dolos/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,14 @@ impl Default for StorageConfig {
}
}

// TODO: add hash of genesis for runtime verification
#[derive(Serialize, Deserialize)]
pub struct GenesisConfig {
byron_path: PathBuf,
shelley_path: PathBuf,
alonzo_path: PathBuf,
conway_path: PathBuf,
// TODO: add hash of genesis for runtime verification
// hash: String,
force_protocol: Option<usize>,
}

impl Default for GenesisConfig {
Expand All @@ -105,6 +105,7 @@ impl Default for GenesisConfig {
shelley_path: PathBuf::from("shelley.json"),
alonzo_path: PathBuf::from("alonzo.json"),
conway_path: PathBuf::from("conway.json"),
force_protocol: None,
}
}
}
Expand Down
94 changes: 61 additions & 33 deletions src/ledger/pparams/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ macro_rules! apply_field {
($target:ident, $update:ident, $field:ident) => {
paste::paste! {
if let Some(new) = $update.[<first_proposed_ $field>]() {
warn!(?new, concat!("found new ", stringify!($param), " update proposal"));
debug!(
?new,
param = stringify!($field),
"found new update proposal"
);

$target.$field = new;
}
}
Expand All @@ -31,6 +36,7 @@ pub struct Genesis {
pub shelley: shelley::GenesisFile,
pub alonzo: alonzo::GenesisFile,
pub conway: conway::GenesisFile,
pub force_protocol: Option<usize>,
}

fn bootstrap_byron_pparams(byron: &byron::GenesisFile) -> ByronProtParams {
Expand Down Expand Up @@ -282,21 +288,21 @@ fn apply_param_update(
match current {
MultiEraProtocolParameters::Byron(mut pparams) => {
if let Some(new) = update.byron_proposed_block_version() {
warn!(?new, "found new block version");
debug!(?new, "found new block version");
pparams.block_version = new;
}

if let Some(pallas::ledger::primitives::byron::TxFeePol::Variant0(new)) =
update.byron_proposed_fee_policy()
{
warn!("found new byron fee policy update proposal");
debug!("found new byron fee policy update proposal");
let (summand, multiplier) = new.unwrap();
pparams.summand = summand as u64;
pparams.multiplier = multiplier as u64;
}

if let Some(new) = update.byron_proposed_max_tx_size() {
warn!("found new byron max tx size update proposal");
debug!("found new byron max tx size update proposal");
pparams.max_tx_size = new;
}

Expand All @@ -311,7 +317,6 @@ fn apply_param_update(
apply_field!(pparams, update, key_deposit);
apply_field!(pparams, update, pool_deposit);
apply_field!(pparams, update, desired_number_of_stake_pools);
apply_field!(pparams, update, protocol_version);
apply_field!(pparams, update, min_pool_cost);
apply_field!(pparams, update, expansion_rate);
apply_field!(pparams, update, treasury_growth_rate);
Expand All @@ -331,7 +336,6 @@ fn apply_param_update(
apply_field!(pparams, update, key_deposit);
apply_field!(pparams, update, pool_deposit);
apply_field!(pparams, update, desired_number_of_stake_pools);
apply_field!(pparams, update, protocol_version);
apply_field!(pparams, update, min_pool_cost);
apply_field!(pparams, update, ada_per_utxo_byte);
apply_field!(pparams, update, execution_costs);
Expand Down Expand Up @@ -367,7 +371,6 @@ fn apply_param_update(
apply_field!(pparams, update, key_deposit);
apply_field!(pparams, update, pool_deposit);
apply_field!(pparams, update, desired_number_of_stake_pools);
apply_field!(pparams, update, protocol_version);
apply_field!(pparams, update, min_pool_cost);
apply_field!(pparams, update, ada_per_utxo_byte);
apply_field!(pparams, update, execution_costs);
Expand Down Expand Up @@ -403,7 +406,6 @@ fn apply_param_update(
apply_field!(pparams, update, key_deposit);
apply_field!(pparams, update, pool_deposit);
apply_field!(pparams, update, desired_number_of_stake_pools);
apply_field!(pparams, update, protocol_version);
apply_field!(pparams, update, min_pool_cost);
apply_field!(pparams, update, ada_per_utxo_byte);
apply_field!(pparams, update, execution_costs);
Expand Down Expand Up @@ -441,7 +443,7 @@ fn apply_param_update(
}
}

fn advance_hardfork(
fn migrate_pparams(
current: MultiEraProtocolParameters,
genesis: &Genesis,
next_protocol: usize,
Expand Down Expand Up @@ -485,11 +487,14 @@ fn advance_hardfork(
MultiEraProtocolParameters::Babbage(current) if next_protocol == 8 => {
MultiEraProtocolParameters::Babbage(current)
}
// Protocol version 9 will transition from Babbage to Conway; not yet implemented
// Protocol version 9 transitions from Babbage to Conway
MultiEraProtocolParameters::Babbage(current) if next_protocol == 9 => {
MultiEraProtocolParameters::Conway(bootstrap_conway_pparams(current, &genesis.conway))
}
_ => unimplemented!("don't know how to handle hardfork"),
x => unimplemented!(
"don't know how to handle hardfork (protocol: {})",
x.protocol_version()
),
}
}

Expand All @@ -498,42 +503,63 @@ pub fn fold_until_epoch(
updates: &[MultiEraUpdate],
for_epoch: u64,
) -> (MultiEraProtocolParameters, ChainSummary) {
let mut pparams = match &updates[0] {
MultiEraUpdate::Byron(_, _) => {
debug!("Initializing with Byron parameters");
MultiEraProtocolParameters::Byron(bootstrap_byron_pparams(&genesis.byron))
}
_ => {
debug!("Initializing with Shelley parameters");
MultiEraProtocolParameters::Shelley(bootstrap_shelley_pparams(&genesis.shelley))
}
};
debug!("Initializing with Byron parameters");
let mut pparams = MultiEraProtocolParameters::Byron(bootstrap_byron_pparams(&genesis.byron));

let mut summary = ChainSummary::start(&pparams);

let mut last_protocol = 0;
if let Some(force_protocol) = genesis.force_protocol {
while summary.current_protocol_version() < force_protocol {
let next_protocol = summary.current_protocol_version() + 1;
pparams = migrate_pparams(pparams, genesis, next_protocol);
summary.advance(0, &pparams);

for epoch in 1..for_epoch {
debug!("Processing epoch {}", epoch);
debug!(
protocol = summary.current_protocol_version(),
"forced hardfork"
);
}
}

for epoch in 1..for_epoch {
let epoch_updates: Vec<_> = updates
.iter()
.filter(|e| e.epoch() == (epoch - 1))
.collect();

debug!("Found {} updates for epoch {}", epoch_updates.len(), epoch);
if !epoch_updates.is_empty() {
debug!(
epoch,
count = epoch_updates.len(),
"found updates for epoch",
);
}

for update in epoch_updates {
pparams = apply_param_update(pparams, update);
}

for next_protocol in last_protocol + 1..=pparams.protocol_version() {
debug!("advancing hardfork {:?}", next_protocol);
pparams = advance_hardfork(pparams, genesis, next_protocol);
let byron_version_change = update
.byron_proposed_block_version()
.map(|(v, _, _)| (v as usize));

let post_byron_version_change = update
.first_proposed_protocol_version()
.map(|(v, _)| v as usize);

summary.advance(epoch, &pparams);
let version_change = byron_version_change.or(post_byron_version_change);

last_protocol = next_protocol;
if let Some(next_protocol) = version_change {
while summary.current_protocol_version() < next_protocol {
let next_protocol = summary.current_protocol_version() + 1;
pparams = migrate_pparams(pparams, genesis, next_protocol);
summary.advance(epoch, &pparams);

debug!(
protocol = summary.current_protocol_version(),
"hardfork executed"
);
}
}
}
}

Expand Down Expand Up @@ -577,6 +603,7 @@ mod tests {
shelley: load_json(format!("{test_data}/genesis/shelley_genesis.json")),
alonzo: load_json(format!("{test_data}/genesis/alonzo_genesis.json")),
conway: load_json(format!("{test_data}/genesis/conway_genesis.json")),
force_protocol: None,
};

// Then load each mainnet example update proposal as buffers
Expand Down Expand Up @@ -628,8 +655,9 @@ mod tests {

// TODO: implement serialize/deserialize, and get full protocol param json files
let expected = load_json::<usize, _>(filename);
let (actual, _) = fold_until_epoch(&genesis, &chained_updates, epoch);
assert_eq!(expected, actual.protocol_version())
let (_, summary) = fold_until_epoch(&genesis, &chained_updates, epoch);

assert_eq!(expected, summary.current_protocol_version())

//assert_eq!(expected, actual)
}
Expand Down
8 changes: 7 additions & 1 deletion src/ledger/pparams/summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,21 @@ impl ChainSummary {
timestamp: end_timestamp,
};

let next_protocol = current.protocol_version + 1;

current.end = Some(boundary.clone());
self.past.push(current);

self.current = Some(EraSummary {
start: boundary.clone(),
end: None,
protocol_version: pparams.protocol_version(),
protocol_version: next_protocol,
epoch_length: pparams.epoch_length(),
slot_length: pparams.slot_length(),
});
}

pub fn current_protocol_version(&self) -> usize {
self.current.as_ref().unwrap().protocol_version
}
}

0 comments on commit 4147810

Please sign in to comment.