use linera_base::{
data_types::{Amount, ApplicationPermissions, Round, Timestamp},
hashed::Hashed,
identifiers::{ApplicationId, ChainId, ChannelFullName, GenericApplicationId, Owner},
ownership::TimeoutConfig,
};
use linera_chain::{
data_types::{
IncomingBundle, LiteValue, LiteVote, Medium, MessageAction, Origin, ProposedBlock,
SignatureAggregator,
},
types::{ConfirmedBlock, ConfirmedBlockCertificate},
};
use linera_execution::{
system::{Recipient, SystemChannel, SystemOperation},
Operation,
};
use super::TestValidator;
use crate::ToBcsBytes;
pub struct BlockBuilder {
block: ProposedBlock,
validator: TestValidator,
}
impl BlockBuilder {
pub(crate) fn new(
chain_id: ChainId,
owner: Owner,
previous_block: Option<&ConfirmedBlockCertificate>,
validator: TestValidator,
) -> Self {
let previous_block_hash = previous_block.map(|certificate| certificate.hash());
let height = previous_block
.map(|certificate| {
certificate
.inner()
.height()
.try_add_one()
.expect("Block height limit reached")
})
.unwrap_or_default();
BlockBuilder {
block: ProposedBlock {
epoch: 0.into(),
chain_id,
incoming_bundles: vec![],
operations: vec![],
previous_block_hash,
height,
authenticated_signer: Some(owner),
timestamp: Timestamp::from(0),
},
validator,
}
}
pub fn with_timestamp(&mut self, timestamp: Timestamp) -> &mut Self {
self.block.timestamp = timestamp;
self
}
pub fn with_native_token_transfer(
&mut self,
sender: Option<Owner>,
recipient: Recipient,
amount: Amount,
) -> &mut Self {
self.with_system_operation(SystemOperation::Transfer {
owner: sender,
recipient,
amount,
})
}
pub(crate) fn with_system_operation(&mut self, operation: SystemOperation) -> &mut Self {
self.block.operations.push(operation.into());
self
}
pub fn with_request_for_application<Abi>(
&mut self,
application: ApplicationId<Abi>,
) -> &mut Self {
self.with_system_operation(SystemOperation::RequestApplication {
chain_id: application.creation.chain_id,
application_id: application.forget_abi(),
})
}
pub fn with_owner_change(
&mut self,
super_owners: Vec<Owner>,
owners: Vec<(Owner, u64)>,
multi_leader_rounds: u32,
open_multi_leader_rounds: bool,
timeout_config: TimeoutConfig,
) -> &mut Self {
self.with_system_operation(SystemOperation::ChangeOwnership {
super_owners,
owners,
multi_leader_rounds,
open_multi_leader_rounds,
timeout_config,
})
}
pub fn with_change_application_permissions(
&mut self,
permissions: ApplicationPermissions,
) -> &mut Self {
self.with_system_operation(SystemOperation::ChangeApplicationPermissions(permissions))
}
pub fn with_operation<Abi>(
&mut self,
application_id: ApplicationId<Abi>,
operation: impl ToBcsBytes,
) -> &mut Self {
self.block.operations.push(Operation::User {
application_id: application_id.forget_abi(),
bytes: operation
.to_bcs_bytes()
.expect("Failed to serialize operation"),
});
self
}
pub(crate) fn with_incoming_bundles(
&mut self,
bundles: impl IntoIterator<Item = IncomingBundle>,
) -> &mut Self {
self.block.incoming_bundles.extend(bundles);
self
}
pub fn with_system_messages_from(
&mut self,
certificate: &ConfirmedBlockCertificate,
channel: SystemChannel,
) -> &mut Self {
let medium = Medium::Channel(ChannelFullName {
application_id: GenericApplicationId::System,
name: channel.name(),
});
self.with_messages_from_by_medium(certificate, &medium, MessageAction::Accept)
}
pub fn with_messages_from(&mut self, certificate: &ConfirmedBlockCertificate) -> &mut Self {
self.with_messages_from_by_medium(certificate, &Medium::Direct, MessageAction::Accept)
}
pub fn with_messages_from_by_medium(
&mut self,
certificate: &ConfirmedBlockCertificate,
medium: &Medium,
action: MessageAction,
) -> &mut Self {
let origin = Origin {
sender: certificate.inner().chain_id(),
medium: medium.clone(),
};
let bundles = certificate
.message_bundles_for(medium, self.block.chain_id)
.map(|(_epoch, bundle)| IncomingBundle {
origin: origin.clone(),
bundle,
action,
});
self.with_incoming_bundles(bundles)
}
pub(crate) async fn try_sign(self) -> anyhow::Result<ConfirmedBlockCertificate> {
let (executed_block, _) = self
.validator
.worker()
.stage_block_execution(self.block, None)
.await?;
let value = Hashed::new(ConfirmedBlock::new(executed_block));
let vote = LiteVote::new(
LiteValue::new(&value),
Round::Fast,
self.validator.key_pair(),
);
let mut builder = SignatureAggregator::new(value, Round::Fast, self.validator.committee());
let certificate = builder
.append(vote.public_key, vote.signature)
.expect("Failed to sign block")
.expect("Committee has more than one test validator");
Ok(certificate)
}
}