Skip to content

Commit

Permalink
Merge pull request #119 from Neptune-Crypto/aszepieniec/relativize-ge…
Browse files Browse the repository at this point in the history
…nesis-block

feat: Relativize genesis block
  • Loading branch information
dan-da authored Apr 9, 2024
2 parents a40aee0 + 7de6c67 commit 8f31ff5
Show file tree
Hide file tree
Showing 29 changed files with 544 additions and 431 deletions.
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

0 comments on commit 8f31ff5

Please sign in to comment.