Skip to content

Commit

Permalink
Add genesis hash as extra data to migration ready txn
Browse files Browse the repository at this point in the history
  • Loading branch information
Eligioo committed Jun 12, 2024
1 parent 8ca4a70 commit 0ae73cf
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 25 deletions.
10 changes: 5 additions & 5 deletions genesis-builder/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub struct GenesisConfig {
pub htlc_accounts: Vec<GenesisHTLC>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct GenesisValidator {
pub validator_address: Address,
pub signing_key: SchnorrPublicKey,
Expand All @@ -67,7 +67,7 @@ pub struct GenesisValidator {
pub retired: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct GenesisStaker {
pub staker_address: Address,
pub balance: Coin,
Expand All @@ -77,14 +77,14 @@ pub struct GenesisStaker {
pub inactive_from: Option<u32>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct GenesisAccount {
pub address: Address,
pub balance: Coin,
}

/// Struct that represents a vesting contract in the toml file that is used to generate the genesis
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct GenesisVestingContract {
/// Vesting contract account address
pub address: Address,
Expand All @@ -103,7 +103,7 @@ pub struct GenesisVestingContract {
}

/// Struct that represents an HTLC in the toml file that is used to generate the genesis
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct GenesisHTLC {
/// HTLC account address
pub address: Address,
Expand Down
35 changes: 30 additions & 5 deletions pow-migration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ use std::{

use nimiq_database::DatabaseProxy;
use nimiq_genesis_builder::config::GenesisConfig;
use nimiq_hash::{Blake2bHasher, Hasher};
use nimiq_keys::Address;
use nimiq_primitives::networks::NetworkId;
use nimiq_rpc::Client;
use nimiq_serde::Serialize;
use thiserror::Error;
use tokio::time::sleep;

Expand Down Expand Up @@ -237,7 +239,8 @@ pub async fn migrate(
}

let mut reported_ready = false;
let genesis_config;
let mut genesis_config;
let genesis_config_hash;

loop {
let current_height = pow_client.block_number().await.unwrap();
Expand Down Expand Up @@ -302,7 +305,24 @@ pub async fn migrate(
)
.await?;

log::info!("PoS Genesis generation is completed");
// Sort vectors for a consistent hash digest
genesis_config.validators.sort();
genesis_config.stakers.sort();
genesis_config.basic_accounts.sort();
genesis_config.vesting_accounts.sort();
genesis_config.htlc_accounts.sort();

let mut hasher = Blake2bHasher::new();
genesis_config
.serialize_to_writer(&mut hasher)
.unwrap_or_else(|error| {
exit_with_error(error, "Failed to serialize genesis config")
});
genesis_config_hash = hasher.finish();
log::info!(
genesis_config_hash = %genesis_config_hash,
"PoS Genesis generation is completed"
);

break;
}
Expand Down Expand Up @@ -337,6 +357,7 @@ pub async fn migrate(
pow_client,
validator_address.to_user_friendly_address(),
candidate_block..next_candidate,
&genesis_config_hash,
)
.await;

Expand All @@ -347,7 +368,10 @@ pub async fn migrate(
"We didn't find a ready transaction from our validator in this window"
);
// Report we are ready to the Nimiq PoW chain:
let transaction = generate_ready_tx(validator_address.to_user_friendly_address());
let transaction = generate_ready_tx(
validator_address.to_user_friendly_address(),
&genesis_config_hash,
);

match send_tx(pow_client, transaction).await {
Ok(_) => reported_ready = true,
Expand All @@ -364,6 +388,7 @@ pub async fn migrate(
pow_client,
validators.clone(),
candidate_block..next_candidate,
&genesis_config_hash,
)
.await;
match validators_status {
Expand Down Expand Up @@ -418,12 +443,12 @@ pub fn launch_pos_client(
// Check that we were able to launch the PoS client
match child.try_wait() {
Ok(Some(status)) => {
log::error!(%status, "Pos client unexpectedly exited");
log::error!(%status, "PoS client unexpectedly exited");
Err(Error::PoSUnexpectedExit(status))
}
Ok(None) => {
let pid = child.id();
log::info!(pid, "Pos client running");
log::info!(pid, "PoS client running");
Ok(child.wait()?)
}
Err(error) => {
Expand Down
3 changes: 1 addition & 2 deletions pow-migration/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,7 @@ async fn main() {
}
},
Err(error) => {
log::error!(?error, "Could not migrate");
exit(1);
exit_with_error(error, "Could not migrate");
}
}
}
Expand Down
47 changes: 34 additions & 13 deletions pow-migration/src/monitor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod types;
use std::ops::Range;

use log::{error, info};
use nimiq_hash::Blake2bHash;
use nimiq_keys::Address;
use nimiq_primitives::coin::Coin;
use nimiq_rpc::{
Expand All @@ -24,19 +25,20 @@ pub const READY_PERCENTAGE: u8 = 80;
/// Sender: Validator address
/// Recipient: Burn address
/// Value: 1 Luna
/// Data: TBD
/// Data: Hash of the generated `GenesisConfig`
///
pub fn generate_ready_tx(validator: String) -> OutgoingTransaction {
pub fn generate_ready_tx(validator: String, hash: &Blake2bHash) -> OutgoingTransaction {
info!(
validator_address = validator,
pos_genesis_hash = %hash,
"Generating ready transaction"
);
OutgoingTransaction {
from: validator,
to: Address::burn_address().to_user_friendly_address(),
value: 1, //Lunas
fee: 0,
data: None,
data: Some(hash.to_hex()),
}
}

Expand All @@ -45,23 +47,43 @@ pub async fn get_ready_txns(
client: &Client,
validator: String,
block_window: Range<u32>,
pos_genesis_config_hash: &Blake2bHash,
) -> Vec<TransactionDetails> {
if let Ok(transactions) = client.get_transactions_by_address(&validator, 10).await {
let genesis_config_hash_hex = pos_genesis_config_hash.to_hex();

let filtered_txns: Vec<TransactionDetails> = transactions
.into_iter()
.filter(|txn| {
// Here we filter by current epoch
block_window.contains(&txn.block_number)
&& txn.to_address == Address::burn_address().to_user_friendly_address()
&& txn.value == 1
})
.filter(|txn| is_valid_ready_txn(txn, &block_window, &genesis_config_hash_hex))
.collect();
filtered_txns
} else {
Vec::new()
}
}

/// Checks if the provided transaction meets the criteria in order to be
/// considered a valid ready-transaction
fn is_valid_ready_txn(
txn: &TransactionDetails,
block_window: &Range<u32>,
genesis_config_hash: &String,
) -> bool {
// Check if the txn contains extra data and matches our genesis config hash
if let Some(txn_data) = &txn.data {
if txn_data != genesis_config_hash {
return false;
}
} else {
return false;
}

// Check for the other readiness criteria
block_window.contains(&txn.block_number)
&& txn.to_address == Address::burn_address().to_user_friendly_address()
&& txn.value == 1
}

/// Sends a transaction into the Nimiq PoW chain
pub async fn send_tx(client: &Client, transaction: OutgoingTransaction) -> Result<(), Error> {
match client.send_transaction(&transaction).await {
Expand All @@ -82,6 +104,7 @@ pub async fn check_validators_ready(
client: &Client,
validators: Vec<GenesisValidator>,
activation_block_window: Range<u32>,
pos_genesis_config_hash: &Blake2bHash,
) -> ValidatorsReadiness {
// First calculate the total amount of stake
let total_stake: Coin = validators
Expand All @@ -92,6 +115,7 @@ pub async fn check_validators_ready(
log::debug!(registered_stake = %total_stake);

let mut ready_validators = Vec::new();
let genesis_config_hash_hex = pos_genesis_config_hash.to_hex();

log::info!("Starting to collect transactions from validators...");

Expand All @@ -111,10 +135,7 @@ pub async fn check_validators_ready(
let filtered_txns: Vec<TransactionDetails> = transactions
.into_iter()
.filter(|txn| {
// Here we filter by the readiness criteria, TBD
activation_block_window.contains(&txn.block_number)
&& txn.to_address == Address::burn_address().to_user_friendly_address()
&& txn.value == 1
is_valid_ready_txn(txn, &activation_block_window, &genesis_config_hash_hex)
})
.collect();
info!(
Expand Down

0 comments on commit 0ae73cf

Please sign in to comment.