Skip to content

Commit

Permalink
Take into account the previous block timestamp during block production (
Browse files Browse the repository at this point in the history
#1059)

Updated the logic of the `PoA` to take into account the `last_timestamp`
during block production. If the user manually specified the block with
the future timestamp, the block production will use a new timestamp as a
base to produce blocks.

Changed the API of the `produce_blocks` endpoint to accept only the
start time and the number of blocks(The user can't specify the interval
between blocks, it is defined by the `Trigger::Interval` mode).

Allowed enabling of the `manual_blocks_enabled` for the
`Trigger::Interval` production mode.

---------

Co-authored-by: ControlCplusControlV <44706811+ControlCplusControlV@users.noreply.github.com>
Co-authored-by: Brandon Kite <brandonkite92@gmail.com>
  • Loading branch information
3 people committed Mar 17, 2023
1 parent 3f0f56e commit c19bd95
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 227 deletions.
5 changes: 1 addition & 4 deletions bin/e2e-test-client/src/test_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,7 @@ impl Wallet {
if let TransactionStatus::Failure { .. } | TransactionStatus::SqueezedOut { .. } =
&status
{
return Err(anyhow!(format!(
"unexpected transaction status {:?}",
status
)))
return Err(anyhow!(format!("unexpected transaction status {status:?}")))
}

Ok(())
Expand Down
19 changes: 7 additions & 12 deletions crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,13 @@ type Mutation {
Submits transaction to the txpool
"""
submit(tx: HexString!): Transaction!
produceBlocks(blocksToProduce: U64!, time: TimeParameters): U64!
"""
Sequentially produces `blocks_to_produce` blocks. The first block starts with
`start_timestamp`. If the block production in the [`crate::service::Config`] is
`Trigger::Interval { block_time }`, produces blocks with `block_time ` intervals between
them. The `start_timestamp` is the timestamp in seconds.
"""
produceBlocks(startTimestamp: Tai64Timestamp, blocksToProduce: U64!): U64!
}

type NodeInfo {
Expand Down Expand Up @@ -678,17 +684,6 @@ type SuccessStatus {

scalar Tai64Timestamp

input TimeParameters {
"""
The time to set on the first block
"""
startTime: U64!
"""
The time interval between subsequent blocks
"""
blockTimeInterval: U64!
}

type Transaction {
id: TransactionId!
inputAssetIds: [AssetId!]
Expand Down
12 changes: 6 additions & 6 deletions crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::client::schema::{
contract::ContractBalanceQueryArgs,
resource::ExcludeInput,
tx::DryRunArg,
Tai64Timestamp,
};
use anyhow::Context;
#[cfg(feature = "subscriptions")]
Expand Down Expand Up @@ -79,6 +80,7 @@ use std::{
FromStr,
},
};
use tai64::Tai64;
use tracing as _;
use types::{
TransactionResponse,
Expand All @@ -92,10 +94,7 @@ pub use schema::{
};

use self::schema::{
block::{
ProduceBlockArgs,
TimeParameters,
},
block::ProduceBlockArgs,
message::MessageProofArgs,
};

Expand Down Expand Up @@ -561,11 +560,12 @@ impl FuelClient {
pub async fn produce_blocks(
&self,
blocks_to_produce: u64,
time: Option<TimeParameters>,
start_timestamp: Option<u64>,
) -> io::Result<u64> {
let query = schema::block::BlockMutation::build(ProduceBlockArgs {
blocks_to_produce: blocks_to_produce.into(),
time,
start_timestamp: start_timestamp
.map(|timestamp| Tai64Timestamp::from(Tai64(timestamp))),
});

let new_height = self.query(query).await?.produce_blocks;
Expand Down
13 changes: 3 additions & 10 deletions crates/client/src/client/schema/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,10 @@ pub struct BlockIdFragment {
pub id: BlockId,
}

#[derive(cynic::InputObject, Clone, Debug)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct TimeParameters {
pub start_time: U64,
pub block_time_interval: U64,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct ProduceBlockArgs {
pub start_timestamp: Option<Tai64Timestamp>,
pub blocks_to_produce: U64,
pub time: Option<TimeParameters>,
}

#[derive(cynic::QueryFragment, Debug)]
Expand All @@ -120,7 +113,7 @@ pub struct ProduceBlockArgs {
graphql_type = "Mutation"
)]
pub struct BlockMutation {
#[arguments(blocksToProduce: $blocks_to_produce, time: $time)]
#[arguments(blocksToProduce: $blocks_to_produce, startTimestamp: $start_timestamp)]
pub produce_blocks: U64,
}

Expand Down Expand Up @@ -206,7 +199,7 @@ mod tests {
use cynic::MutationBuilder;
let operation = BlockMutation::build(ProduceBlockArgs {
blocks_to_produce: U64(0),
time: None,
start_timestamp: None,
});
insta::assert_snapshot!(operation.query)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
source: crates/client/src/client/schema/block.rs
expression: operation.query
---
mutation($blocksToProduce: U64!, $time: TimeParameters) {
produceBlocks(blocksToProduce: $blocksToProduce, time: $time)
mutation($startTimestamp: Tai64Timestamp, $blocksToProduce: U64!) {
produceBlocks(blocksToProduce: $blocksToProduce, startTimestamp: $startTimestamp)
}


8 changes: 4 additions & 4 deletions crates/fuel-core/src/graphql_api/ports.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use anyhow::Result;
use async_trait::async_trait;
use fuel_core_services::stream::BoxStream;
use fuel_core_storage::{
Expand Down Expand Up @@ -158,7 +157,7 @@ pub trait TxPoolPort: Send + Sync {

fn tx_update_subscribe(
&self,
) -> BoxStream<Result<TxUpdate, BroadcastStreamRecvError>>;
) -> BoxStream<anyhow::Result<TxUpdate, BroadcastStreamRecvError>>;
}

#[async_trait]
Expand All @@ -175,8 +174,9 @@ pub trait BlockProducerPort: Send + Sync + DryRunExecution {}

#[async_trait::async_trait]
pub trait ConsensusModulePort: Send + Sync {
async fn manual_produce_block(
async fn manually_produce_blocks(
&self,
block_times: Vec<Option<Tai64>>,
start_time: Option<Tai64>,
number_of_blocks: u32,
) -> anyhow::Result<()>;
}
89 changes: 10 additions & 79 deletions crates/fuel-core/src/schema/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ use async_graphql::{
EmptyFields,
},
Context,
InputObject,
Object,
SimpleObject,
Union,
Expand All @@ -51,7 +50,6 @@ use fuel_core_types::{
header::BlockHeader,
},
fuel_types,
tai64::Tai64,
};

pub struct Block(pub(crate) CompressedBlock);
Expand Down Expand Up @@ -283,21 +281,17 @@ where
#[derive(Default)]
pub struct BlockMutation;

#[derive(InputObject)]
struct TimeParameters {
/// The time to set on the first block
start_time: U64,
/// The time interval between subsequent blocks
block_time_interval: U64,
}

#[Object]
impl BlockMutation {
/// Sequentially produces `blocks_to_produce` blocks. The first block starts with
/// `start_timestamp`. If the block production in the [`crate::service::Config`] is
/// `Trigger::Interval { block_time }`, produces blocks with `block_time ` intervals between
/// them. The `start_timestamp` is the timestamp in seconds.
async fn produce_blocks(
&self,
ctx: &Context<'_>,
start_timestamp: Option<Tai64Timestamp>,
blocks_to_produce: U64,
time: Option<TimeParameters>,
) -> async_graphql::Result<U64> {
let query: &Database = ctx.data_unchecked();
let consensus_module = ctx.data_unchecked::<ConsensusModule>();
Expand All @@ -309,9 +303,11 @@ impl BlockMutation {
)
}

let latest_block = query.latest_block()?;
let block_times = get_time_closure(&latest_block, time, blocks_to_produce.0)?;
consensus_module.manual_produce_block(block_times).await?;
let start_time = start_timestamp.map(|timestamp| timestamp.0);
let blocks_to_produce: u64 = blocks_to_produce.into();
consensus_module
.manually_produce_blocks(start_time, blocks_to_produce as u32)
.await?;

query
.latest_block_height()
Expand All @@ -320,71 +316,6 @@ impl BlockMutation {
}
}

fn get_time_closure(
latest_block: &CompressedBlock,
time_parameters: Option<TimeParameters>,
blocks_to_produce: u64,
) -> anyhow::Result<Vec<Option<Tai64>>> {
if let Some(params) = time_parameters {
let start_time = params.start_time.into();
check_start_after_latest_block(latest_block, start_time)?;
check_block_time_overflow(&params, blocks_to_produce)?;
let interval: u64 = params.block_time_interval.into();

let vec = (0..blocks_to_produce)
.into_iter()
.map(|idx| {
let (timestamp, _) = params
.start_time
.0
.overflowing_add(interval.overflowing_mul(idx).0);
Some(Tai64(timestamp))
})
.collect();
return Ok(vec)
};

Ok(vec![None; blocks_to_produce as usize])
}

fn check_start_after_latest_block(
latest_block: &CompressedBlock,
start_time: u64,
) -> anyhow::Result<()> {
let current_height = *latest_block.header().height();

if current_height.as_usize() == 0 {
return Ok(())
}

let latest_time = latest_block.header().time();
if latest_time.0 > start_time {
return Err(anyhow!(
"The start time must be set after the latest block time: {:?}",
latest_time
))
}

Ok(())
}

fn check_block_time_overflow(
params: &TimeParameters,
blocks_to_produce: u64,
) -> anyhow::Result<()> {
let (final_offset, overflow_mul) = params
.block_time_interval
.0
.overflowing_mul(blocks_to_produce);
let (_, overflow_add) = params.start_time.0.overflowing_add(final_offset);

if overflow_mul || overflow_add {
return Err(anyhow!("The provided time parameters lead to an overflow"))
};

Ok(())
}

impl From<CompressedBlock> for Block {
fn from(block: CompressedBlock) -> Self {
Block(block)
Expand Down
9 changes: 5 additions & 4 deletions crates/fuel-core/src/service/adapters/consensus_module/poa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@ impl PoAAdapter {

#[async_trait::async_trait]
impl ConsensusModulePort for PoAAdapter {
async fn manual_produce_block(
async fn manually_produce_blocks(
&self,
block_times: Vec<Option<Tai64>>,
start_time: Option<Tai64>,
number_of_blocks: u32,
) -> anyhow::Result<()> {
self.shared_state
.as_ref()
.ok_or(anyhow!("The block production is disabled"))?
.manually_produce_block(block_times)
.manually_produce_block(start_time, number_of_blocks)
.await
}
}
Expand Down Expand Up @@ -89,7 +90,7 @@ impl fuel_core_poa::ports::BlockProducer for BlockProducerAdapter {
async fn produce_and_execute_block(
&self,
height: BlockHeight,
block_time: Option<Tai64>,
block_time: Tai64,
max_gas: Word,
) -> anyhow::Result<UncommittedResult<StorageTransaction<Database>>> {
self.block_producer
Expand Down
7 changes: 5 additions & 2 deletions crates/fuel-core/src/service/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,11 @@ impl TryFrom<&Config> for fuel_core_poa::Config {
// If manual block production then require trigger never or instant.
anyhow::ensure!(
!config.manual_blocks_enabled
|| matches!(config.block_production, Trigger::Never | Trigger::Instant),
"Cannot use manual block production unless trigger mode is never or instant."
|| matches!(
config.block_production,
Trigger::Never | Trigger::Instant | Trigger::Interval { .. }
),
"Cannot use manual block production unless trigger mode is never, instant or interval."
);

Ok(fuel_core_poa::Config {
Expand Down
8 changes: 5 additions & 3 deletions crates/fuel-core/src/service/sub_services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ pub fn init_sub_services(
config: &Config,
database: &Database,
) -> anyhow::Result<(SubServices, SharedState)> {
let last_height = database.latest_height()?;
let last_block = database.get_current_block()?.ok_or(anyhow::anyhow!(
"The blockchain is not initialized with any block"
))?;
#[cfg(feature = "relayer")]
let relayer_service = if config.relayer.eth_client.is_some() {
Some(fuel_core_relayer::new_service(
Expand Down Expand Up @@ -129,7 +131,7 @@ pub fn init_sub_services(
!matches!(poa_config.trigger, Trigger::Never) || config.manual_blocks_enabled;
let poa = (production_enabled).then(|| {
fuel_core_poa::new_service(
last_height,
last_block.header(),
poa_config,
tx_pool_adapter.clone(),
producer_adapter.clone(),
Expand All @@ -142,7 +144,7 @@ pub fn init_sub_services(
let sync = (!production_enabled)
.then(|| {
fuel_core_sync::service::new_service(
last_height,
*last_block.header().height(),
p2p_adapter,
importer_adapter.clone(),
verifier,
Expand Down
2 changes: 1 addition & 1 deletion crates/services/consensus_module/poa/src/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub trait BlockProducer: Send + Sync {
async fn produce_and_execute_block(
&self,
height: BlockHeight,
block_time: Option<Tai64>,
block_time: Tai64,
max_gas: Word,
) -> anyhow::Result<UncommittedExecutionResult<StorageTransaction<Self::Database>>>;

Expand Down
Loading

0 comments on commit c19bd95

Please sign in to comment.