1use linera_base::{
9 abi::ContractAbi,
10 data_types::{Amount, ApplicationPermissions, Blob, Epoch, Round, Timestamp},
11 identifiers::{AccountOwner, ApplicationId, ChainId},
12 ownership::TimeoutConfig,
13};
14use linera_chain::{
15 data_types::{
16 IncomingBundle, LiteValue, LiteVote, MessageAction, ProposedBlock, SignatureAggregator,
17 },
18 types::{ConfirmedBlock, ConfirmedBlockCertificate},
19};
20use linera_core::worker::WorkerError;
21use linera_execution::{
22 system::{Recipient, SystemOperation},
23 Operation,
24};
25
26use super::TestValidator;
27
28pub struct BlockBuilder {
31 block: ProposedBlock,
32 validator: TestValidator,
33}
34
35impl BlockBuilder {
36 pub(crate) fn new(
48 chain_id: ChainId,
49 owner: AccountOwner,
50 epoch: Epoch,
51 previous_block: Option<&ConfirmedBlockCertificate>,
52 validator: TestValidator,
53 ) -> Self {
54 let previous_block_hash = previous_block.map(|certificate| certificate.hash());
55 let height = previous_block
56 .map(|certificate| {
57 certificate
58 .inner()
59 .height()
60 .try_add_one()
61 .expect("Block height limit reached")
62 })
63 .unwrap_or_default();
64
65 BlockBuilder {
66 block: ProposedBlock {
67 epoch,
68 chain_id,
69 incoming_bundles: vec![],
70 operations: vec![],
71 previous_block_hash,
72 height,
73 authenticated_signer: Some(owner),
74 timestamp: Timestamp::from(0),
75 },
76 validator,
77 }
78 }
79
80 pub fn with_timestamp(&mut self, timestamp: Timestamp) -> &mut Self {
82 self.block.timestamp = timestamp;
83 self
84 }
85
86 pub fn with_native_token_transfer(
88 &mut self,
89 sender: AccountOwner,
90 recipient: Recipient,
91 amount: Amount,
92 ) -> &mut Self {
93 self.with_system_operation(SystemOperation::Transfer {
94 owner: sender,
95 recipient,
96 amount,
97 })
98 }
99
100 pub(crate) fn with_system_operation(&mut self, operation: SystemOperation) -> &mut Self {
102 self.block.operations.push(operation.into());
103 self
104 }
105
106 pub fn with_owner_change(
108 &mut self,
109 super_owners: Vec<AccountOwner>,
110 owners: Vec<(AccountOwner, u64)>,
111 multi_leader_rounds: u32,
112 open_multi_leader_rounds: bool,
113 timeout_config: TimeoutConfig,
114 ) -> &mut Self {
115 self.with_system_operation(SystemOperation::ChangeOwnership {
116 super_owners,
117 owners,
118 multi_leader_rounds,
119 open_multi_leader_rounds,
120 timeout_config,
121 })
122 }
123
124 pub fn with_change_application_permissions(
126 &mut self,
127 permissions: ApplicationPermissions,
128 ) -> &mut Self {
129 self.with_system_operation(SystemOperation::ChangeApplicationPermissions(permissions))
130 }
131
132 pub fn with_operation<Abi>(
137 &mut self,
138 application_id: ApplicationId<Abi>,
139 operation: Abi::Operation,
140 ) -> &mut Self
141 where
142 Abi: ContractAbi,
143 {
144 let operation = Abi::serialize_operation(&operation)
145 .expect("Failed to serialize `Operation` in BlockBuilder");
146 self.with_raw_operation(application_id.forget_abi(), operation)
147 }
148
149 pub fn with_raw_operation(
151 &mut self,
152 application_id: ApplicationId,
153 operation: impl Into<Vec<u8>>,
154 ) -> &mut Self {
155 self.block.operations.push(Operation::User {
156 application_id,
157 bytes: operation.into(),
158 });
159 self
160 }
161
162 pub(crate) fn with_incoming_bundles(
167 &mut self,
168 bundles: impl IntoIterator<Item = IncomingBundle>,
169 ) -> &mut Self {
170 self.block.incoming_bundles.extend(bundles);
171 self
172 }
173
174 pub fn with_messages_from(&mut self, certificate: &ConfirmedBlockCertificate) -> &mut Self {
176 self.with_messages_from_by_action(certificate, MessageAction::Accept)
177 }
178
179 pub fn with_messages_from_by_action(
181 &mut self,
182 certificate: &ConfirmedBlockCertificate,
183 action: MessageAction,
184 ) -> &mut Self {
185 let origin = certificate.inner().chain_id();
186 let bundles =
187 certificate
188 .message_bundles_for(self.block.chain_id)
189 .map(|(_epoch, bundle)| IncomingBundle {
190 origin,
191 bundle,
192 action,
193 });
194 self.with_incoming_bundles(bundles)
195 }
196
197 pub(crate) async fn try_sign(
200 self,
201 blobs: &[Blob],
202 ) -> Result<ConfirmedBlockCertificate, WorkerError> {
203 let published_blobs = self
204 .block
205 .published_blob_ids()
206 .into_iter()
207 .map(|blob_id| {
208 blobs
209 .iter()
210 .find(|blob| blob.id() == blob_id)
211 .expect("missing published blob")
212 .clone()
213 })
214 .collect();
215 let (block, _) = self
216 .validator
217 .worker()
218 .stage_block_execution(self.block, None, published_blobs)
219 .await?;
220
221 let value = ConfirmedBlock::new(block);
222 let vote = LiteVote::new(
223 LiteValue::new(&value),
224 Round::Fast,
225 self.validator.key_pair(),
226 );
227 let committee = self.validator.committee().await;
228 let mut builder = SignatureAggregator::new(value, Round::Fast, &committee);
229 let certificate = builder
230 .append(vote.public_key, vote.signature)
231 .expect("Failed to sign block")
232 .expect("Committee has more than one test validator");
233
234 Ok(certificate)
235 }
236}