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_owner: 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 first_leader: Option<AccountOwner>,
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 first_leader,
119 multi_leader_rounds,
120 open_multi_leader_rounds,
121 timeout_config,
122 })
123 }
124
125 pub fn with_change_application_permissions(
127 &mut self,
128 permissions: ApplicationPermissions,
129 ) -> &mut Self {
130 self.with_system_operation(SystemOperation::ChangeApplicationPermissions(permissions))
131 }
132
133 pub fn with_operation<Abi>(
138 &mut self,
139 application_id: ApplicationId<Abi>,
140 operation: Abi::Operation,
141 ) -> &mut Self
142 where
143 Abi: ContractAbi,
144 {
145 let operation = Abi::serialize_operation(&operation)
146 .expect("Failed to serialize `Operation` in BlockBuilder");
147 self.with_raw_operation(application_id.forget_abi(), operation)
148 }
149
150 pub fn with_raw_operation(
152 &mut self,
153 application_id: ApplicationId,
154 operation: impl Into<Vec<u8>>,
155 ) -> &mut Self {
156 self.block
157 .transactions
158 .push(Transaction::ExecuteOperation(Operation::User {
159 application_id,
160 bytes: operation.into(),
161 }));
162 self
163 }
164
165 pub(crate) fn with_incoming_bundles(
170 &mut self,
171 bundles: impl IntoIterator<Item = IncomingBundle>,
172 ) -> &mut Self {
173 self.block
174 .transactions
175 .extend(bundles.into_iter().map(Transaction::ReceiveMessages));
176 self
177 }
178
179 pub fn with_messages_from(&mut self, certificate: &ConfirmedBlockCertificate) -> &mut Self {
181 self.with_messages_from_by_action(certificate, MessageAction::Accept)
182 }
183
184 pub fn with_messages_from_by_action(
186 &mut self,
187 certificate: &ConfirmedBlockCertificate,
188 action: MessageAction,
189 ) -> &mut Self {
190 let origin = certificate.inner().chain_id();
191 let bundles =
192 certificate
193 .message_bundles_for(self.block.chain_id)
194 .map(|(_epoch, bundle)| IncomingBundle {
195 origin,
196 bundle,
197 action,
198 });
199 self.with_incoming_bundles(bundles)
200 }
201
202 pub(crate) async fn try_sign(
205 self,
206 blobs: &[Blob],
207 ) -> Result<ConfirmedBlockCertificate, WorkerError> {
208 let published_blobs = self
209 .block
210 .published_blob_ids()
211 .into_iter()
212 .map(|blob_id| {
213 blobs
214 .iter()
215 .find(|blob| blob.id() == blob_id)
216 .expect("missing published blob")
217 .clone()
218 })
219 .collect();
220 let (block, _) = self
221 .validator
222 .worker()
223 .stage_block_execution(self.block, None, published_blobs)
224 .await?;
225
226 let value = ConfirmedBlock::new(block);
227 let vote = LiteVote::new(
228 LiteValue::new(&value),
229 Round::Fast,
230 self.validator.key_pair(),
231 );
232 let committee = self.validator.committee().await;
233 let public_key = self.validator.key_pair().public();
234 let mut builder = SignatureAggregator::new(value, Round::Fast, &committee);
235 let certificate = builder
236 .append(public_key, vote.signature)
237 .expect("Failed to sign block")
238 .expect("Committee has more than one test validator");
239
240 Ok(certificate)
241 }
242}