1use linera_base::{
9 abi::ContractAbi,
10 data_types::{Amount, ApplicationPermissions, Blob, Epoch, Round, Timestamp},
11 identifiers::{Account, AccountOwner, ApplicationId, ChainId},
12 ownership::TimeoutConfig,
13};
14use linera_chain::{
15 data_types::{
16 IncomingBundle, LiteValue, LiteVote, MessageAction, ProposedBlock, SignatureAggregator,
17 Transaction,
18 },
19 types::{ConfirmedBlock, ConfirmedBlockCertificate},
20};
21use linera_core::worker::WorkerError;
22use linera_execution::{system::SystemOperation, Operation, ResourceTracker};
23
24use super::TestValidator;
25
26pub struct BlockBuilder {
29 block: ProposedBlock,
30 validator: TestValidator,
31}
32
33impl BlockBuilder {
34 pub(crate) fn new(
51 chain_id: ChainId,
52 owner: AccountOwner,
53 epoch: Epoch,
54 previous_block: Option<&ConfirmedBlockCertificate>,
55 validator: TestValidator,
56 ) -> Self {
57 let previous_block_hash = previous_block.map(|certificate| certificate.hash());
58 let height = previous_block
59 .map(|certificate| {
60 certificate
61 .inner()
62 .height()
63 .try_add_one()
64 .expect("Block height limit reached")
65 })
66 .unwrap_or_default();
67 let parent_timestamp = previous_block
68 .map(|certificate| certificate.inner().timestamp())
69 .unwrap_or_default();
70 let timestamp = parent_timestamp.max(validator.clock().current_time());
71
72 BlockBuilder {
73 block: ProposedBlock {
74 epoch,
75 chain_id,
76 transactions: vec![],
77 previous_block_hash,
78 height,
79 authenticated_owner: Some(owner),
80 timestamp,
81 },
82 validator,
83 }
84 }
85
86 pub fn with_timestamp(&mut self, timestamp: Timestamp) -> &mut Self {
93 self.block.timestamp = timestamp;
94 self
95 }
96
97 pub fn with_native_token_transfer(
99 &mut self,
100 sender: AccountOwner,
101 recipient: Account,
102 amount: Amount,
103 ) -> &mut Self {
104 self.with_system_operation(SystemOperation::Transfer {
105 owner: sender,
106 recipient,
107 amount,
108 })
109 }
110
111 pub(crate) fn with_system_operation(&mut self, operation: SystemOperation) -> &mut Self {
113 self.block
114 .transactions
115 .push(Transaction::ExecuteOperation(operation.into()));
116 self
117 }
118
119 pub fn with_owner_change(
121 &mut self,
122 super_owners: Vec<AccountOwner>,
123 owners: Vec<(AccountOwner, u64)>,
124 first_leader: Option<AccountOwner>,
125 multi_leader_rounds: u32,
126 open_multi_leader_rounds: bool,
127 timeout_config: TimeoutConfig,
128 ) -> &mut Self {
129 self.with_system_operation(SystemOperation::ChangeOwnership {
130 super_owners,
131 owners,
132 first_leader,
133 multi_leader_rounds,
134 open_multi_leader_rounds,
135 timeout_config,
136 })
137 }
138
139 pub fn with_change_application_permissions(
141 &mut self,
142 permissions: ApplicationPermissions,
143 ) -> &mut Self {
144 self.with_system_operation(SystemOperation::ChangeApplicationPermissions(permissions))
145 }
146
147 pub fn with_operation<Abi>(
152 &mut self,
153 application_id: ApplicationId<Abi>,
154 operation: &Abi::Operation,
155 ) -> &mut Self
156 where
157 Abi: ContractAbi,
158 {
159 let operation = <Abi as ContractAbi>::serialize_operation(operation)
160 .expect("Failed to serialize `Operation` in BlockBuilder");
161 self.with_raw_operation(application_id.forget_abi(), operation)
162 }
163
164 pub fn with_raw_operation(
166 &mut self,
167 application_id: ApplicationId,
168 operation: impl Into<Vec<u8>>,
169 ) -> &mut Self {
170 self.block
171 .transactions
172 .push(Transaction::ExecuteOperation(Operation::User {
173 application_id,
174 bytes: operation.into(),
175 }));
176 self
177 }
178
179 pub(crate) fn with_incoming_bundles(
189 &mut self,
190 bundles: impl IntoIterator<Item = IncomingBundle>,
191 ) -> &mut Self {
192 for bundle in bundles {
193 self.block.timestamp = self.block.timestamp.max(bundle.bundle.timestamp);
194 self.block
195 .transactions
196 .push(Transaction::ReceiveMessages(bundle));
197 }
198 self
199 }
200
201 pub fn with_messages_from(&mut self, certificate: &ConfirmedBlockCertificate) -> &mut Self {
206 self.with_messages_from_by_action(certificate, MessageAction::Accept)
207 }
208
209 pub fn with_messages_from_by_action(
214 &mut self,
215 certificate: &ConfirmedBlockCertificate,
216 action: MessageAction,
217 ) -> &mut Self {
218 let origin = certificate.inner().chain_id();
219 let bundles =
220 certificate
221 .message_bundles_for(self.block.chain_id)
222 .map(|(_epoch, bundle)| IncomingBundle {
223 origin,
224 bundle,
225 action,
226 });
227 self.with_incoming_bundles(bundles)
228 }
229
230 pub(crate) async fn try_sign(
234 self,
235 blobs: &[Blob],
236 ) -> Result<(ConfirmedBlockCertificate, ResourceTracker), WorkerError> {
237 let published_blobs = self
238 .block
239 .published_blob_ids()
240 .into_iter()
241 .map(|blob_id| {
242 blobs
243 .iter()
244 .find(|blob| blob.id() == blob_id)
245 .expect("missing published blob")
246 .clone()
247 })
248 .collect();
249 let (block, _, resource_tracker) = self
250 .validator
251 .worker()
252 .stage_block_execution(self.block, None, published_blobs)
253 .await?;
254
255 let value = ConfirmedBlock::new(block);
256 let vote = LiteVote::new(
257 LiteValue::new(&value),
258 Round::Fast,
259 self.validator.key_pair(),
260 );
261 let committee = self.validator.committee().await;
262 let public_key = self.validator.key_pair().public();
263 let mut builder = SignatureAggregator::new(value, Round::Fast, &committee);
264 let certificate = builder
265 .append(public_key, vote.signature)
266 .expect("Failed to sign block")
267 .expect("Committee has more than one test validator");
268
269 Ok((certificate, resource_tracker))
270 }
271}