1mod http_server;
7
8use std::collections::BTreeMap;
9
10use linera_base::{
11 crypto::{CryptoHash, Signer, ValidatorPublicKey},
12 data_types::{Amount, Blob, BlockHeight, Epoch, Event, OracleResponse, Round, Timestamp},
13 identifiers::{Account, AccountOwner, ChainId},
14};
15use linera_execution::{Message, MessageKind, Operation, OutgoingMessage, SystemOperation};
16
17pub use self::http_server::HttpServer;
18use crate::{
19 block::{Block, ConfirmedBlock},
20 data_types::{
21 BlockExecutionOutcome, BlockProposal, IncomingBundle, OperationResult, PostedMessage,
22 ProposedBlock, Transaction, Vote,
23 },
24 types::{CertificateValue, GenericCertificate},
25};
26
27pub fn make_child_block(parent: &ConfirmedBlock) -> ProposedBlock {
29 let parent_header = &parent.block().header;
30 ProposedBlock {
31 epoch: parent_header.epoch,
32 chain_id: parent_header.chain_id,
33 transactions: vec![],
34 previous_block_hash: Some(parent.hash()),
35 height: parent_header.height.try_add_one().unwrap(),
36 authenticated_owner: parent_header.authenticated_owner,
37 timestamp: parent_header.timestamp,
38 }
39}
40
41pub fn make_first_block(chain_id: ChainId) -> ProposedBlock {
43 ProposedBlock {
44 epoch: Epoch::ZERO,
45 chain_id,
46 transactions: vec![],
47 previous_block_hash: None,
48 height: BlockHeight::ZERO,
49 authenticated_owner: None,
50 timestamp: Timestamp::default(),
51 }
52}
53
54pub struct BlockBuilder {
58 block: ProposedBlock,
59 outcome: BlockExecutionOutcome,
60}
61
62impl BlockBuilder {
63 pub fn new(chain_id: ChainId, height: BlockHeight) -> Self {
65 BlockBuilder {
66 block: ProposedBlock {
67 epoch: Epoch::ZERO,
68 chain_id,
69 transactions: vec![],
70 previous_block_hash: None,
71 height,
72 authenticated_owner: None,
73 timestamp: Timestamp::default(),
74 },
75 outcome: BlockExecutionOutcome {
76 state_hash: CryptoHash::default(),
77 messages: vec![],
78 previous_message_blocks: BTreeMap::new(),
79 previous_event_blocks: BTreeMap::new(),
80 oracle_responses: vec![],
81 events: vec![],
82 blobs: vec![],
83 operation_results: vec![],
84 },
85 }
86 }
87
88 pub fn with_state_hash(mut self, state_hash: CryptoHash) -> Self {
90 self.outcome.state_hash = state_hash;
91 self
92 }
93
94 pub fn with_transaction(mut self, transaction: Transaction) -> Self {
96 self.block.transactions.push(transaction);
97 self
98 }
99
100 pub fn with_messages(mut self, messages: Vec<OutgoingMessage>) -> Self {
102 self.outcome.messages.push(messages);
103 self
104 }
105
106 pub fn with_events(mut self, events: Vec<Event>) -> Self {
108 self.outcome.events.push(events);
109 self
110 }
111
112 pub fn with_oracle_responses(mut self, oracle_responses: Vec<OracleResponse>) -> Self {
114 self.outcome.oracle_responses.push(oracle_responses);
115 self
116 }
117
118 pub fn with_blobs(mut self, blobs: Vec<Blob>) -> Self {
120 self.outcome.blobs.push(blobs);
121 self
122 }
123
124 pub fn with_operation_result(mut self, operation_result: OperationResult) -> Self {
126 self.outcome.operation_results.push(operation_result);
127 self
128 }
129
130 pub fn build(self) -> Block {
132 self.outcome.with(self.block)
133 }
134}
135
136#[allow(async_fn_in_trait)]
138pub trait BlockTestExt: Sized {
139 fn with_authenticated_owner(self, authenticated_owner: Option<AccountOwner>) -> Self;
141
142 fn with_operation(self, operation: impl Into<Operation>) -> Self;
144
145 fn with_transfer(self, owner: AccountOwner, recipient: Account, amount: Amount) -> Self;
147
148 fn with_simple_transfer(self, chain_id: ChainId, amount: Amount) -> Self;
150
151 fn with_incoming_bundle(self, incoming_bundle: IncomingBundle) -> Self;
153
154 fn with_incoming_bundles(
156 self,
157 incoming_bundles: impl IntoIterator<Item = IncomingBundle>,
158 ) -> Self;
159
160 fn with_timestamp(self, timestamp: impl Into<Timestamp>) -> Self;
162
163 fn with_epoch(self, epoch: impl Into<Epoch>) -> Self;
165
166 fn with_burn(self, amount: Amount) -> Self;
168
169 async fn into_first_proposal<S: Signer + ?Sized>(
172 self,
173 owner: AccountOwner,
174 signer: &S,
175 ) -> Result<BlockProposal, S::Error> {
176 self.into_proposal_with_round(owner, signer, Round::MultiLeader(0))
177 .await
178 }
179
180 async fn into_proposal_with_round<S: Signer + ?Sized>(
182 self,
183 owner: AccountOwner,
184 signer: &S,
185 round: Round,
186 ) -> Result<BlockProposal, S::Error>;
187}
188
189impl BlockTestExt for ProposedBlock {
190 fn with_authenticated_owner(mut self, authenticated_owner: Option<AccountOwner>) -> Self {
191 self.authenticated_owner = authenticated_owner;
192 self
193 }
194
195 fn with_operation(mut self, operation: impl Into<Operation>) -> Self {
196 self.transactions
197 .push(Transaction::ExecuteOperation(operation.into()));
198 self
199 }
200
201 fn with_transfer(self, owner: AccountOwner, recipient: Account, amount: Amount) -> Self {
202 self.with_operation(SystemOperation::Transfer {
203 owner,
204 recipient,
205 amount,
206 })
207 }
208
209 fn with_simple_transfer(self, chain_id: ChainId, amount: Amount) -> Self {
210 self.with_transfer(AccountOwner::CHAIN, Account::chain(chain_id), amount)
211 }
212
213 fn with_burn(self, amount: Amount) -> Self {
214 let recipient = Account::burn_address(self.chain_id);
215 self.with_operation(SystemOperation::Transfer {
216 owner: AccountOwner::CHAIN,
217 recipient,
218 amount,
219 })
220 }
221
222 fn with_incoming_bundle(mut self, incoming_bundle: IncomingBundle) -> Self {
223 self.transactions
224 .push(Transaction::ReceiveMessages(incoming_bundle));
225 self
226 }
227
228 fn with_incoming_bundles(
229 mut self,
230 incoming_bundles: impl IntoIterator<Item = IncomingBundle>,
231 ) -> Self {
232 self.transactions.extend(
233 incoming_bundles
234 .into_iter()
235 .map(Transaction::ReceiveMessages),
236 );
237 self
238 }
239
240 fn with_timestamp(mut self, timestamp: impl Into<Timestamp>) -> Self {
241 self.timestamp = timestamp.into();
242 self
243 }
244
245 fn with_epoch(mut self, epoch: impl Into<Epoch>) -> Self {
246 self.epoch = epoch.into();
247 self
248 }
249
250 async fn into_proposal_with_round<S: Signer + ?Sized>(
251 self,
252 owner: AccountOwner,
253 signer: &S,
254 round: Round,
255 ) -> Result<BlockProposal, S::Error> {
256 BlockProposal::new_initial(owner, round, self, signer).await
257 }
258}
259
260pub trait VoteTestExt<T: CertificateValue>: Sized {
262 fn into_certificate(self, public_key: ValidatorPublicKey) -> GenericCertificate<T>;
264}
265
266impl<T: CertificateValue> VoteTestExt<T> for Vote<T> {
267 fn into_certificate(self, public_key: ValidatorPublicKey) -> GenericCertificate<T> {
268 GenericCertificate::new_with_payload(
271 self.value,
272 self.round,
273 self.unlocking_round,
274 self.first_round,
275 self.justification_commitment,
276 vec![(public_key, self.signature)],
277 )
278 }
279}
280
281pub trait MessageTestExt: Sized {
283 fn to_posted(self, kind: MessageKind) -> PostedMessage;
285}
286
287impl<T: Into<Message>> MessageTestExt for T {
288 fn to_posted(self, kind: MessageKind) -> PostedMessage {
289 PostedMessage {
290 authenticated_owner: None,
291 grant: Amount::ZERO,
292 refund_grant_to: None,
293 kind,
294 message: self.into(),
295 }
296 }
297}