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