2023-10-23 18:14:40 +00:00
|
|
|
use std::{
|
|
|
|
future::Future,
|
|
|
|
pin::Pin,
|
|
|
|
sync::Arc,
|
|
|
|
task::{Context, Poll},
|
|
|
|
};
|
2023-10-15 19:35:33 +00:00
|
|
|
|
2023-10-23 18:14:40 +00:00
|
|
|
use futures::FutureExt;
|
2023-12-17 03:13:30 +00:00
|
|
|
use monero_serai::block::Block;
|
2023-12-27 23:50:18 +00:00
|
|
|
use monero_serai::transaction::Input;
|
2023-10-23 18:14:40 +00:00
|
|
|
use tower::{Service, ServiceExt};
|
2023-10-15 19:35:33 +00:00
|
|
|
|
2023-12-16 23:03:02 +00:00
|
|
|
use monero_consensus::{
|
2024-01-07 01:15:33 +00:00
|
|
|
blocks::{calculate_pow_hash, check_block, check_block_pow, BlockError, RandomX},
|
|
|
|
miner_tx::MinerTxError,
|
2023-12-16 23:03:02 +00:00
|
|
|
ConsensusError, HardFork,
|
|
|
|
};
|
|
|
|
|
2023-10-23 18:14:40 +00:00
|
|
|
use crate::{
|
2023-12-02 22:57:34 +00:00
|
|
|
context::{BlockChainContextRequest, BlockChainContextResponse},
|
2023-10-23 18:14:40 +00:00
|
|
|
transactions::{TransactionVerificationData, VerifyTxRequest, VerifyTxResponse},
|
2023-12-16 23:03:02 +00:00
|
|
|
ExtendedConsensusError, TxNotInPool, TxPoolRequest, TxPoolResponse,
|
2023-10-23 18:14:40 +00:00
|
|
|
};
|
2023-10-05 16:54:19 +00:00
|
|
|
|
2023-11-11 01:55:15 +00:00
|
|
|
#[derive(Debug)]
|
2024-01-05 22:36:47 +00:00
|
|
|
pub struct PrePreparedBlockExPOW {
|
2023-11-11 01:55:15 +00:00
|
|
|
pub block: Block,
|
|
|
|
pub block_blob: Vec<u8>,
|
|
|
|
|
|
|
|
pub hf_vote: HardFork,
|
|
|
|
pub hf_version: HardFork,
|
|
|
|
|
|
|
|
pub block_hash: [u8; 32],
|
|
|
|
|
|
|
|
pub miner_tx_weight: usize,
|
|
|
|
}
|
|
|
|
|
2024-01-05 22:36:47 +00:00
|
|
|
impl PrePreparedBlockExPOW {
|
|
|
|
pub fn new(block: Block) -> Result<PrePreparedBlockExPOW, ConsensusError> {
|
2023-12-27 23:50:18 +00:00
|
|
|
let (hf_version, hf_vote) =
|
|
|
|
HardFork::from_block_header(&block.header).map_err(BlockError::HardForkError)?;
|
|
|
|
|
2024-01-05 22:36:47 +00:00
|
|
|
Ok(PrePreparedBlockExPOW {
|
2023-12-27 23:50:18 +00:00
|
|
|
block_blob: block.serialize(),
|
|
|
|
hf_vote,
|
|
|
|
hf_version,
|
|
|
|
|
|
|
|
block_hash: block.hash(),
|
|
|
|
|
|
|
|
miner_tx_weight: block.miner_tx.weight(),
|
|
|
|
block,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-05 22:36:47 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PrePreparedBlock {
|
|
|
|
pub block: Block,
|
|
|
|
pub block_blob: Vec<u8>,
|
|
|
|
|
|
|
|
pub hf_vote: HardFork,
|
|
|
|
pub hf_version: HardFork,
|
|
|
|
|
|
|
|
pub block_hash: [u8; 32],
|
|
|
|
pub pow_hash: [u8; 32],
|
|
|
|
|
|
|
|
pub miner_tx_weight: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PrePreparedBlock {
|
2024-01-09 22:39:29 +00:00
|
|
|
pub fn new(block: Block) -> Result<PrePreparedBlock, ConsensusError> {
|
|
|
|
struct DummyRX;
|
|
|
|
|
|
|
|
impl RandomX for DummyRX {
|
|
|
|
type Error = ();
|
|
|
|
fn calculate_hash(&self, _: &[u8]) -> Result<[u8; 32], Self::Error> {
|
|
|
|
panic!("DummyRX cant calculate hash")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let (hf_version, hf_vote) =
|
|
|
|
HardFork::from_block_header(&block.header).map_err(BlockError::HardForkError)?;
|
|
|
|
|
|
|
|
let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else {
|
|
|
|
Err(ConsensusError::Block(BlockError::MinerTxError(
|
|
|
|
MinerTxError::InputNotOfTypeGen,
|
|
|
|
)))?
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(PrePreparedBlock {
|
|
|
|
block_blob: block.serialize(),
|
|
|
|
hf_vote,
|
|
|
|
hf_version,
|
|
|
|
|
|
|
|
block_hash: block.hash(),
|
|
|
|
|
|
|
|
pow_hash: calculate_pow_hash::<DummyRX>(
|
|
|
|
None,
|
|
|
|
&block.serialize_hashable(),
|
|
|
|
*height,
|
|
|
|
&hf_version,
|
|
|
|
)?,
|
|
|
|
miner_tx_weight: block.miner_tx.weight(),
|
|
|
|
block,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_rx<R: RandomX>(
|
2024-01-05 22:36:47 +00:00
|
|
|
block: PrePreparedBlockExPOW,
|
|
|
|
randomx_vm: &R,
|
|
|
|
) -> Result<PrePreparedBlock, ConsensusError> {
|
|
|
|
let Some(Input::Gen(height)) = block.block.miner_tx.prefix.inputs.first() else {
|
|
|
|
Err(ConsensusError::Block(BlockError::MinerTxError(
|
|
|
|
MinerTxError::InputNotOfTypeGen,
|
|
|
|
)))?
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(PrePreparedBlock {
|
|
|
|
block_blob: block.block_blob,
|
|
|
|
hf_vote: block.hf_vote,
|
|
|
|
hf_version: block.hf_version,
|
|
|
|
|
|
|
|
block_hash: block.block_hash,
|
|
|
|
pow_hash: calculate_pow_hash(
|
2024-01-09 22:39:29 +00:00
|
|
|
Some(randomx_vm),
|
2024-01-05 22:36:47 +00:00
|
|
|
&block.block.serialize_hashable(),
|
|
|
|
*height,
|
|
|
|
&block.hf_version,
|
|
|
|
)?,
|
|
|
|
|
|
|
|
miner_tx_weight: block.block.miner_tx.weight(),
|
|
|
|
block: block.block,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 18:14:40 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct VerifiedBlockInformation {
|
|
|
|
pub block: Block,
|
|
|
|
pub hf_vote: HardFork,
|
|
|
|
pub txs: Vec<Arc<TransactionVerificationData>>,
|
|
|
|
pub block_hash: [u8; 32],
|
|
|
|
pub pow_hash: [u8; 32],
|
|
|
|
pub height: u64,
|
|
|
|
pub generated_coins: u64,
|
|
|
|
pub weight: usize,
|
|
|
|
pub long_term_weight: usize,
|
2023-10-24 01:25:11 +00:00
|
|
|
pub cumulative_difficulty: u128,
|
2023-10-23 18:14:40 +00:00
|
|
|
}
|
2023-10-15 19:35:33 +00:00
|
|
|
|
2023-10-23 18:14:40 +00:00
|
|
|
pub enum VerifyBlockRequest {
|
2023-11-05 18:44:41 +00:00
|
|
|
MainChain(Block),
|
2023-12-27 23:50:18 +00:00
|
|
|
MainChainPrepared(PrePreparedBlock),
|
2023-11-11 01:55:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum VerifyBlockResponse {
|
|
|
|
MainChain(VerifiedBlockInformation),
|
2023-10-15 19:35:33 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 18:14:40 +00:00
|
|
|
// TODO: it is probably a bad idea for this to derive clone, if 2 places (RPC, P2P) receive valid but different blocks
|
|
|
|
// then they will both get approved but only one should go to main chain.
|
|
|
|
#[derive(Clone)]
|
2023-11-05 18:44:41 +00:00
|
|
|
pub struct BlockVerifierService<C: Clone, TxV: Clone, TxP: Clone> {
|
2023-10-23 18:14:40 +00:00
|
|
|
context_svc: C,
|
2023-11-05 18:44:41 +00:00
|
|
|
tx_verifier_svc: TxV,
|
|
|
|
tx_pool: TxP,
|
2023-10-15 19:35:33 +00:00
|
|
|
}
|
|
|
|
|
2023-11-05 18:44:41 +00:00
|
|
|
impl<C, TxV, TxP> BlockVerifierService<C, TxV, TxP>
|
2023-10-23 18:14:40 +00:00
|
|
|
where
|
2023-12-02 22:57:34 +00:00
|
|
|
C: Service<BlockChainContextRequest, Response = BlockChainContextResponse>
|
|
|
|
+ Clone
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
2023-12-16 23:03:02 +00:00
|
|
|
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>
|
2023-11-05 18:44:41 +00:00
|
|
|
+ Clone
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
|
|
|
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
|
2023-10-23 18:14:40 +00:00
|
|
|
+ Clone
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
|
|
|
{
|
2023-11-05 18:44:41 +00:00
|
|
|
pub fn new(
|
|
|
|
context_svc: C,
|
|
|
|
tx_verifier_svc: TxV,
|
|
|
|
tx_pool: TxP,
|
|
|
|
) -> BlockVerifierService<C, TxV, TxP> {
|
2023-10-23 18:14:40 +00:00
|
|
|
BlockVerifierService {
|
|
|
|
context_svc,
|
|
|
|
tx_verifier_svc,
|
2023-11-05 18:44:41 +00:00
|
|
|
tx_pool,
|
2023-10-23 18:14:40 +00:00
|
|
|
}
|
2023-10-15 19:35:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-05 18:44:41 +00:00
|
|
|
impl<C, TxV, TxP> Service<VerifyBlockRequest> for BlockVerifierService<C, TxV, TxP>
|
2023-10-23 18:14:40 +00:00
|
|
|
where
|
2023-12-02 22:57:34 +00:00
|
|
|
C: Service<
|
|
|
|
BlockChainContextRequest,
|
|
|
|
Response = BlockChainContextResponse,
|
|
|
|
Error = tower::BoxError,
|
|
|
|
> + Clone
|
2023-10-23 18:14:40 +00:00
|
|
|
+ Send
|
|
|
|
+ 'static,
|
|
|
|
C::Future: Send + 'static,
|
2023-11-05 18:44:41 +00:00
|
|
|
|
2023-12-16 23:03:02 +00:00
|
|
|
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>
|
2023-10-23 18:14:40 +00:00
|
|
|
+ Clone
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
2023-11-05 18:44:41 +00:00
|
|
|
TxV::Future: Send + 'static,
|
|
|
|
|
|
|
|
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
|
|
|
|
+ Clone
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
|
|
|
TxP::Future: Send + 'static,
|
2023-10-23 18:14:40 +00:00
|
|
|
{
|
2023-11-11 01:55:15 +00:00
|
|
|
type Response = VerifyBlockResponse;
|
2023-12-16 23:03:02 +00:00
|
|
|
type Error = ExtendedConsensusError;
|
2023-10-23 18:14:40 +00:00
|
|
|
type Future =
|
|
|
|
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
|
|
|
|
2023-12-02 22:57:34 +00:00
|
|
|
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
|
|
Poll::Ready(Ok(()))
|
2023-10-23 18:14:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn call(&mut self, req: VerifyBlockRequest) -> Self::Future {
|
|
|
|
let context_svc = self.context_svc.clone();
|
|
|
|
let tx_verifier_svc = self.tx_verifier_svc.clone();
|
2023-11-05 18:44:41 +00:00
|
|
|
let tx_pool = self.tx_pool.clone();
|
2023-10-23 18:14:40 +00:00
|
|
|
|
|
|
|
async move {
|
|
|
|
match req {
|
2023-11-05 18:44:41 +00:00
|
|
|
VerifyBlockRequest::MainChain(block) => {
|
|
|
|
verify_main_chain_block(block, context_svc, tx_verifier_svc, tx_pool).await
|
2023-10-26 02:16:03 +00:00
|
|
|
}
|
2023-12-27 23:50:18 +00:00
|
|
|
VerifyBlockRequest::MainChainPrepared(prepped_block) => {
|
|
|
|
verify_main_chain_block_prepared(
|
|
|
|
prepped_block,
|
|
|
|
context_svc,
|
|
|
|
tx_verifier_svc,
|
|
|
|
tx_pool,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
2023-10-23 18:14:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.boxed()
|
2023-10-15 19:35:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-27 23:50:18 +00:00
|
|
|
async fn verify_main_chain_block_prepared<C, TxV, TxP>(
|
|
|
|
prepped_block: PrePreparedBlock,
|
|
|
|
context_svc: C,
|
|
|
|
tx_verifier_svc: TxV,
|
|
|
|
tx_pool: TxP,
|
|
|
|
) -> Result<VerifyBlockResponse, ExtendedConsensusError>
|
|
|
|
where
|
|
|
|
C: Service<
|
|
|
|
BlockChainContextRequest,
|
|
|
|
Response = BlockChainContextResponse,
|
|
|
|
Error = tower::BoxError,
|
|
|
|
> + Send
|
|
|
|
+ 'static,
|
|
|
|
C::Future: Send + 'static,
|
|
|
|
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>,
|
|
|
|
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
|
|
|
|
+ Clone
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
|
|
|
{
|
|
|
|
tracing::debug!("getting blockchain context");
|
|
|
|
let BlockChainContextResponse::Context(checked_context) = context_svc
|
|
|
|
.oneshot(BlockChainContextRequest::Get)
|
|
|
|
.await
|
|
|
|
.map_err(Into::<ExtendedConsensusError>::into)?
|
|
|
|
else {
|
|
|
|
panic!("Context service returned wrong response!");
|
|
|
|
};
|
|
|
|
|
|
|
|
let context = checked_context.unchecked_blockchain_context().clone();
|
|
|
|
|
|
|
|
tracing::debug!("got blockchain context: {:?}", context);
|
|
|
|
|
|
|
|
let TxPoolResponse::Transactions(txs) = tx_pool
|
|
|
|
.oneshot(TxPoolRequest::Transactions(prepped_block.block.txs.clone()))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
tx_verifier_svc
|
|
|
|
.oneshot(VerifyTxRequest::Block {
|
|
|
|
txs: txs.clone(),
|
|
|
|
current_chain_height: context.chain_height,
|
|
|
|
time_for_time_lock: context.current_adjusted_timestamp_for_time_lock(),
|
|
|
|
hf: context.current_hf,
|
|
|
|
re_org_token: context.re_org_token.clone(),
|
|
|
|
})
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let block_weight =
|
|
|
|
prepped_block.miner_tx_weight + txs.iter().map(|tx| tx.tx_weight).sum::<usize>();
|
|
|
|
let total_fees = txs.iter().map(|tx| tx.fee).sum::<u64>();
|
|
|
|
|
|
|
|
let (hf_vote, generated_coins) = check_block(
|
|
|
|
&prepped_block.block,
|
|
|
|
total_fees,
|
|
|
|
block_weight,
|
|
|
|
prepped_block.block_blob.len(),
|
|
|
|
&context.context_to_verify_block,
|
|
|
|
)
|
|
|
|
.map_err(ConsensusError::Block)?;
|
|
|
|
|
|
|
|
check_block_pow(&prepped_block.pow_hash, context.next_difficulty)
|
|
|
|
.map_err(ConsensusError::Block)?;
|
|
|
|
|
|
|
|
Ok(VerifyBlockResponse::MainChain(VerifiedBlockInformation {
|
|
|
|
block_hash: prepped_block.block_hash,
|
|
|
|
block: prepped_block.block,
|
|
|
|
txs,
|
|
|
|
pow_hash: prepped_block.pow_hash,
|
|
|
|
generated_coins,
|
|
|
|
weight: block_weight,
|
|
|
|
height: context.chain_height,
|
|
|
|
long_term_weight: context.next_block_long_term_weight(block_weight),
|
|
|
|
hf_vote,
|
|
|
|
cumulative_difficulty: context.cumulative_difficulty + context.next_difficulty,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2023-11-05 18:44:41 +00:00
|
|
|
async fn verify_main_chain_block<C, TxV, TxP>(
|
2024-01-07 01:15:33 +00:00
|
|
|
_block: Block,
|
|
|
|
_context_svc: C,
|
|
|
|
_tx_verifier_svc: TxV,
|
|
|
|
_tx_pool: TxP,
|
2023-12-16 23:03:02 +00:00
|
|
|
) -> Result<VerifyBlockResponse, ExtendedConsensusError>
|
2023-10-26 02:16:03 +00:00
|
|
|
where
|
2023-12-02 22:57:34 +00:00
|
|
|
C: Service<
|
|
|
|
BlockChainContextRequest,
|
|
|
|
Response = BlockChainContextResponse,
|
|
|
|
Error = tower::BoxError,
|
|
|
|
> + Send
|
2023-10-26 02:16:03 +00:00
|
|
|
+ 'static,
|
|
|
|
C::Future: Send + 'static,
|
2023-12-16 23:03:02 +00:00
|
|
|
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>,
|
2023-11-05 18:44:41 +00:00
|
|
|
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
|
|
|
|
+ Clone
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
2023-10-26 02:16:03 +00:00
|
|
|
{
|
2024-01-07 01:15:33 +00:00
|
|
|
todo!("Single main chain block.");
|
|
|
|
|
|
|
|
/*
|
2023-10-26 02:16:03 +00:00
|
|
|
tracing::debug!("getting blockchain context");
|
2023-12-02 22:57:34 +00:00
|
|
|
let BlockChainContextResponse::Context(checked_context) = context_svc
|
|
|
|
.oneshot(BlockChainContextRequest::Get)
|
2023-10-26 02:16:03 +00:00
|
|
|
.await
|
2023-12-16 23:03:02 +00:00
|
|
|
.map_err(Into::<ExtendedConsensusError>::into)?
|
2023-12-02 22:57:34 +00:00
|
|
|
else {
|
|
|
|
panic!("Context service returned wrong response!");
|
|
|
|
};
|
2023-10-26 02:16:03 +00:00
|
|
|
|
2023-11-11 01:55:15 +00:00
|
|
|
let context = checked_context.unchecked_blockchain_context().clone();
|
2023-10-31 02:59:31 +00:00
|
|
|
|
2023-10-26 02:16:03 +00:00
|
|
|
tracing::debug!("got blockchain context: {:?}", context);
|
|
|
|
|
2023-11-05 18:44:41 +00:00
|
|
|
let TxPoolResponse::Transactions(txs) = tx_pool
|
|
|
|
.oneshot(TxPoolRequest::Transactions(block.txs.clone()))
|
|
|
|
.await?;
|
|
|
|
|
2023-10-26 02:16:03 +00:00
|
|
|
tx_verifier_svc
|
|
|
|
.oneshot(VerifyTxRequest::Block {
|
|
|
|
txs: txs.clone(),
|
|
|
|
current_chain_height: context.chain_height,
|
|
|
|
time_for_time_lock: context.current_adjusted_timestamp_for_time_lock(),
|
2023-12-16 23:03:02 +00:00
|
|
|
hf: context.current_hf,
|
2023-11-05 18:44:41 +00:00
|
|
|
re_org_token: context.re_org_token.clone(),
|
2023-10-26 02:16:03 +00:00
|
|
|
})
|
|
|
|
.await?;
|
|
|
|
|
2023-12-16 23:03:02 +00:00
|
|
|
let block_weight = block.miner_tx.weight() + txs.iter().map(|tx| tx.tx_weight).sum::<usize>();
|
|
|
|
let total_fees = txs.iter().map(|tx| tx.fee).sum::<u64>();
|
|
|
|
|
|
|
|
let (hf_vote, generated_coins) = check_block(
|
|
|
|
&block,
|
2023-10-26 02:16:03 +00:00
|
|
|
total_fees,
|
|
|
|
block_weight,
|
2023-12-16 23:03:02 +00:00
|
|
|
block.serialize().len(),
|
|
|
|
&context.context_to_verify_block,
|
|
|
|
)
|
|
|
|
.map_err(ConsensusError::Block)?;
|
2023-10-26 02:16:03 +00:00
|
|
|
|
|
|
|
let hashing_blob = block.serialize_hashable();
|
|
|
|
|
2023-10-24 22:02:19 +00:00
|
|
|
// do POW test last
|
2023-12-16 23:03:02 +00:00
|
|
|
let chain_height = context.chain_height;
|
|
|
|
let current_hf = context.current_hf;
|
2024-01-05 22:36:47 +00:00
|
|
|
let pow_hash = todo!();
|
|
|
|
/*
|
|
|
|
rayon_spawn_async(move || calculate_pow_hash(, &hashing_blob, chain_height, ¤t_hf))
|
|
|
|
.await
|
|
|
|
.map_err(ConsensusError::Block)?;
|
|
|
|
|
|
|
|
*/
|
2023-10-23 18:14:40 +00:00
|
|
|
|
2023-12-16 23:03:02 +00:00
|
|
|
check_block_pow(&pow_hash, context.next_difficulty).map_err(ConsensusError::Block)?;
|
2023-10-24 22:02:19 +00:00
|
|
|
|
2023-11-11 01:55:15 +00:00
|
|
|
Ok(VerifyBlockResponse::MainChain(VerifiedBlockInformation {
|
2023-10-23 18:14:40 +00:00
|
|
|
block_hash: block.hash(),
|
|
|
|
block,
|
|
|
|
txs,
|
|
|
|
pow_hash,
|
|
|
|
generated_coins,
|
|
|
|
weight: block_weight,
|
|
|
|
height: context.chain_height,
|
2023-10-24 01:25:11 +00:00
|
|
|
long_term_weight: context.next_block_long_term_weight(block_weight),
|
2023-12-16 23:03:02 +00:00
|
|
|
hf_vote,
|
2023-10-24 01:25:11 +00:00
|
|
|
cumulative_difficulty: context.cumulative_difficulty + context.next_difficulty,
|
2023-11-11 01:55:15 +00:00
|
|
|
}))
|
2024-01-07 01:15:33 +00:00
|
|
|
*/
|
2023-10-15 19:35:33 +00:00
|
|
|
}
|