Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Relativize genesis block #119

Merged
merged 5 commits into from
Apr 9, 2024
Merged
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
9 changes: 5 additions & 4 deletions src/bin/dashboard_src/history_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use super::{dashboard_app::DashboardEvent, screen::Screen};
use crossterm::event::{Event, KeyCode, KeyEventKind};
use itertools::Itertools;
use neptune_core::{
models::blockchain::{
block::block_height::BlockHeight, type_scripts::neptune_coins::NeptuneCoins,
models::{
blockchain::{block::block_height::BlockHeight, type_scripts::neptune_coins::NeptuneCoins},
consensus::timestamp::Timestamp,
},
rpc_server::RPCClient,
};
Expand All @@ -24,7 +25,7 @@ use tokio::time::sleep;
use tokio::{select, task::JoinHandle};
use unicode_width::UnicodeWidthStr;

type BalanceUpdate = (BlockHeight, Duration, NeptuneCoins, NeptuneCoins);
type BalanceUpdate = (BlockHeight, Timestamp, NeptuneCoins, NeptuneCoins);
type BalanceUpdateArc = Arc<std::sync::Mutex<Vec<BalanceUpdate>>>;
type DashboardEventArc = Arc<std::sync::Mutex<Option<DashboardEvent>>>;
type JoinHandleArc = Arc<Mutex<JoinHandle<()>>>;
Expand Down Expand Up @@ -269,7 +270,7 @@ impl Widget for HistoryScreen {
let (height, timestamp, amount, balance) = *bu;
vec![
height.to_string(),
neptune_core::utc_timestamp_to_localtime(timestamp.as_millis()).to_string(),
timestamp.standard_format(),
if !amount.is_negative() {
"⭸".to_string()
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/bin/dashboard_src/overview_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl OverviewData {
is_mining: Some(false),
syncing: false,
block_header: Some(
neptune_core::models::blockchain::block::Block::genesis_block()
neptune_core::models::blockchain::block::Block::genesis_block(Network::Testnet)
.kernel
.header,
),
Expand Down
43 changes: 43 additions & 0 deletions src/config_models/network.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,63 @@
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH};
use strum::EnumIter;
use tasm_lib::twenty_first::shared_math::b_field_element::BFieldElement;

use crate::models::consensus::timestamp::Timestamp;

#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Default, EnumIter)]
pub enum Network {
/// First iteration of testnet. Not feature-complete. Soon to be deprecated.
#[default]
Alpha,

/// Upcoming iteration of testnet. Not feature-complete either but moreso than
/// Alpha. Soon to be set as default.
Beta,

/// Main net. Feature-complete. Fixed launch date. Not ready yet.
Main,

/// Feature-complete (or as feature-complete as possible) test network separate
/// from whichever network is currently running. For integration tests involving
/// multiple nodes over a network.
Testnet,

/// Network for individual unit and integration tests. The timestamp for the
/// RegTest genesis block is set to now, rounded down to the first block of
/// 10 minutes. As a result, there is a small probability that tests fail
/// because they generate the genesis block twice on two opposite sides of a
/// round timestamp.
RegTest,
}
impl Network {
pub(crate) fn launch_date(&self) -> Timestamp {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
let ten_minutes = 1000 * 60 * 10;
let now_rounded = (now / ten_minutes) * ten_minutes;
match self {
Network::RegTest => Timestamp(BFieldElement::new(now_rounded)),
// 1 July 2024 (might be revised though)
Network::Alpha | Network::Testnet | Network::Beta | Network::Main => {
Timestamp(BFieldElement::new(1719792000000u64))
}
}
}
}

impl fmt::Display for Network {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let string = match self {
Network::Alpha => "alpha".to_string(),
Network::Testnet => "testnet".to_string(),
Network::RegTest => "regtest".to_string(),
Network::Beta => "beta".to_string(),
Network::Main => "main".to_string(),
};
write!(f, "{}", string)
}
Expand All @@ -29,6 +70,8 @@ impl FromStr for Network {
"alpha" => Ok(Network::Alpha),
"testnet" => Ok(Network::Testnet),
"regtest" => Ok(Network::RegTest),
"beta" => Ok(Network::Beta),
"main" => Ok(Network::Main),
_ => Err(format!("Failed to parse {} as network", input)),
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ pub async fn initialize(cli_args: cli_args::Args) -> Result<()> {
let archival_mutator_set = ArchivalState::initialize_mutator_set(&data_dir).await?;
info!("Got archival mutator set");

let archival_state = ArchivalState::new(data_dir, block_index_db, archival_mutator_set).await;
let archival_state = ArchivalState::new(
data_dir,
block_index_db,
archival_mutator_set,
cli_args.network,
)
.await;

// Get latest block. Use hardcoded genesis block if nothing is in database.
let latest_block: Block = archival_state.get_latest_block().await;
Expand Down
5 changes: 1 addition & 4 deletions src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,7 @@ impl MainLoopHandler {
"Storing block {} in database. Height: {}, Mined: {}",
new_block.hash().emojihash(),
new_block.kernel.header.height,
crate::utc_timestamp_to_localtime(
new_block.kernel.header.timestamp.value()
)
.to_string()
new_block.kernel.header.timestamp.standard_format()
);

global_state_mut.store_block(new_block).await?;
Expand Down
41 changes: 21 additions & 20 deletions src/mine_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::models::blockchain::type_scripts::neptune_coins::NeptuneCoins;
use crate::models::blockchain::type_scripts::TypeScript;
use crate::models::channel::*;
use crate::models::consensus::mast_hash::MastHash;
use crate::models::consensus::timestamp::Timestamp;
use crate::models::shared::SIZE_20MB_IN_BYTES;
use crate::models::state::wallet::utxo_notification_pool::{ExpectedUtxo, UtxoNotifier};
use crate::models::state::wallet::WalletSecret;
Expand All @@ -29,7 +30,6 @@ use rand::Rng;
use rand::SeedableRng;
use std::ops::Deref;
use std::time::Duration;
use std::time::{SystemTime, UNIX_EPOCH};
use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
use tasm_lib::twenty_first::util_types::mmr::mmr_trait::Mmr;
use tokio::select;
Expand All @@ -51,7 +51,7 @@ const MOCK_MAX_BLOCK_SIZE: u32 = 1_000_000;
fn make_block_template(
previous_block: &Block,
transaction: Transaction,
timestamp: Duration,
mut block_timestamp: Timestamp,
) -> (BlockHeader, BlockBody) {
let additions = transaction.kernel.outputs.clone();
let removals = transaction.kernel.inputs.clone();
Expand Down Expand Up @@ -80,18 +80,17 @@ fn make_block_template(
let new_pow_line: U32s<5> =
previous_block.kernel.header.proof_of_work_family + previous_block.kernel.header.difficulty;
let next_block_height = previous_block.kernel.header.height.next();
let mut block_timestamp = timestamp.as_millis() as u64;
if block_timestamp < previous_block.kernel.header.timestamp.value() {
if block_timestamp < previous_block.kernel.header.timestamp {
warn!("Received block is timestamped in the future; mining on future-timestamped block.");
block_timestamp = previous_block.kernel.header.timestamp.value() + 1;
block_timestamp = previous_block.kernel.header.timestamp + Timestamp::seconds(1);
}
let difficulty: U32s<5> = Block::difficulty_control(previous_block, block_timestamp);

let block_header = BlockHeader {
version: zero,
height: next_block_height,
prev_block_digest: previous_block.kernel.mast_hash(),
timestamp: BFieldElement::new(block_timestamp),
timestamp: block_timestamp,
nonce: [zero, zero, zero],
max_block_size: MOCK_MAX_BLOCK_SIZE,
proof_of_work_line: new_pow_line,
Expand Down Expand Up @@ -183,7 +182,7 @@ fn make_coinbase_transaction(
wallet_secret: &WalletSecret,
block_height: BlockHeight,
mutator_set_accumulator: MutatorSetAccumulator,
timestamp: Duration,
timestamp: Timestamp,
) -> (Transaction, Digest) {
let sender_randomness: Digest =
wallet_secret.generate_sender_randomness(block_height, receiver_digest);
Expand All @@ -196,7 +195,7 @@ fn make_coinbase_transaction(
*NeptuneCoins::decode(&coin.state)
.expect("Make coinbase transaction: failed to parse coin state as amount.")
})
.sum();
.sum::<NeptuneCoins>();
let coinbase_addition_record = commit(
Hash::hash(coinbase_utxo),
sender_randomness,
Expand All @@ -208,8 +207,8 @@ fn make_coinbase_transaction(
outputs: vec![coinbase_addition_record],
public_announcements: vec![],
fee: NeptuneCoins::zero(),
timestamp: BFieldElement::new(timestamp.as_millis() as u64),
coinbase: Some(coinbase_amount),
timestamp,
mutator_set_hash: mutator_set_accumulator.hash(),
};

Expand Down Expand Up @@ -239,7 +238,7 @@ fn make_coinbase_transaction(
fn create_block_transaction(
latest_block: &Block,
global_state: &GlobalState,
timestamp: Duration,
timestamp: Timestamp,
) -> (Transaction, ExpectedUtxo) {
let block_capacity_for_transactions = SIZE_20MB_IN_BYTES;

Expand Down Expand Up @@ -328,7 +327,7 @@ pub async fn mine(
None
} else {
// Build the block template and spawn the worker thread to mine on it
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let now = Timestamp::now();
let (transaction, coinbase_utxo_info) = create_block_transaction(
&latest_block,
global_state_lock.lock_guard().await.deref(),
Expand Down Expand Up @@ -418,7 +417,7 @@ pub async fn mine(

// The block, however, *must* be valid on other parameters. So here, we should panic
// if it is not.
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let now = Timestamp::now();
assert!(new_block_info.block.is_valid(&latest_block, now), "Own mined block must be valid. Failed validity check after successful PoW check.");

info!("Found new {} block with block height {}. Hash: {}", global_state_lock.cli().network, new_block_info.block.kernel.header.height, new_block_info.block.hash().emojihash());
Expand Down Expand Up @@ -453,7 +452,8 @@ mod mine_loop_tests {
use tracing_test::traced_test;

use crate::{
config_models::network::Network, models::state::UtxoReceiverData,
config_models::network::Network,
models::{consensus::timestamp::Timestamp, state::UtxoReceiverData},
tests::shared::get_mock_global_state,
};

Expand All @@ -463,8 +463,9 @@ mod mine_loop_tests {
#[tokio::test]
async fn block_template_is_valid_test() -> Result<()> {
// Verify that a block template made with transaction from the mempool is a valid block
let network = Network::RegTest;
let premine_receiver_global_state_lock =
get_mock_global_state(Network::Alpha, 2, WalletSecret::devnet_wallet()).await;
get_mock_global_state(network, 2, WalletSecret::devnet_wallet()).await;
let mut premine_receiver_global_state =
premine_receiver_global_state_lock.lock_guard_mut().await;
assert!(
Expand All @@ -473,8 +474,8 @@ mod mine_loop_tests {
);

// Verify constructed coinbase transaction and block template when mempool is empty
let genesis_block = Block::genesis_block();
let now = Duration::from_millis(genesis_block.kernel.header.timestamp.value());
let genesis_block = Block::genesis_block(network);
let now = genesis_block.kernel.header.timestamp;
let (transaction_empty_mempool, _coinbase_sender_randomness) =
create_block_transaction(&genesis_block, &premine_receiver_global_state, now);
assert_eq!(
Expand Down Expand Up @@ -518,7 +519,7 @@ mod mine_loop_tests {
}),
],
NeptuneCoins::new(1),
now + Duration::from_millis(7 * 30 * 24 * 60 * 60 * 1000),
now + Timestamp::months(7),
)
.await
.unwrap();
Expand All @@ -532,7 +533,7 @@ mod mine_loop_tests {
create_block_transaction(
&genesis_block,
&premine_receiver_global_state,
now + Duration::from_millis(7 * 30 * 24 * 60 * 60 * 1000 + 1000),
now + Timestamp::months(7),
);
assert_eq!(
3,
Expand All @@ -545,7 +546,7 @@ mod mine_loop_tests {
let (block_header_template, block_body) = make_block_template(
&genesis_block,
transaction_non_empty_mempool,
now + Duration::from_millis(7 * 30 * 24 * 60 * 60 * 1000 + 2000),
now + Timestamp::months(7),
);
let block_template_non_empty_mempool = Block::new(
block_header_template,
Expand All @@ -555,7 +556,7 @@ mod mine_loop_tests {
assert!(
block_template_non_empty_mempool.is_valid(
&genesis_block,
now + Duration::from_millis(7 * 30 * 24 * 60 * 60 * 1000 + 2000)
now + Timestamp::months(7) + Timestamp::seconds(2)
),
"Block template created by miner with non-empty mempool must be valid"
);
Expand Down
3 changes: 2 additions & 1 deletion src/models/blockchain/block/block_header.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::models::consensus::timestamp::Timestamp;
use crate::prelude::twenty_first;

use crate::models::consensus::mast_hash::HasDiscriminant;
Expand Down Expand Up @@ -26,7 +27,7 @@ pub struct BlockHeader {

// TODO: Reject blocks that are more than 10 seconds into the future
// number of milliseconds since unix epoch
pub timestamp: BFieldElement,
pub timestamp: Timestamp,

// TODO: Consider making a type for `nonce`
pub nonce: [BFieldElement; 3],
Expand Down
Loading
Loading