1mod http_server;
7
8use linera_base::{
9 crypto::{AccountPublicKey, Signer, ValidatorPublicKey},
10 data_types::{Amount, BlockHeight, Epoch, Round, Timestamp},
11 identifiers::{Account, AccountOwner, ChainId},
12};
13use linera_execution::{
14 committee::{Committee, ValidatorState},
15 Message, MessageKind, Operation, ResourceControlPolicy, SystemOperation,
16};
17
18pub use self::http_server::HttpServer;
19use crate::{
20 block::ConfirmedBlock,
21 data_types::{
22 BlockProposal, IncomingBundle, PostedMessage, ProposedBlock, SignatureAggregator,
23 Transaction, Vote,
24 },
25 types::{CertificateValue, GenericCertificate},
26};
27
28pub fn make_child_block(parent: &ConfirmedBlock) -> ProposedBlock {
30 let parent_header = &parent.block().header;
31 ProposedBlock {
32 epoch: parent_header.epoch,
33 chain_id: parent_header.chain_id,
34 transactions: vec![],
35 previous_block_hash: Some(parent.hash()),
36 height: parent_header.height.try_add_one().unwrap(),
37 authenticated_owner: parent_header.authenticated_owner,
38 timestamp: parent_header.timestamp,
39 }
40}
41
42pub fn make_first_block(chain_id: ChainId) -> ProposedBlock {
44 ProposedBlock {
45 epoch: Epoch::ZERO,
46 chain_id,
47 transactions: vec![],
48 previous_block_hash: None,
49 height: BlockHeight::ZERO,
50 authenticated_owner: None,
51 timestamp: Timestamp::default(),
52 }
53}
54
55#[allow(async_fn_in_trait)]
57pub trait BlockTestExt: Sized {
58 fn with_authenticated_owner(self, authenticated_owner: Option<AccountOwner>) -> Self;
60
61 fn with_operation(self, operation: impl Into<Operation>) -> Self;
63
64 fn with_transfer(self, owner: AccountOwner, recipient: Account, amount: Amount) -> Self;
66
67 fn with_simple_transfer(self, chain_id: ChainId, amount: Amount) -> Self;
69
70 fn with_incoming_bundle(self, incoming_bundle: IncomingBundle) -> Self;
72
73 fn with_incoming_bundles(
75 self,
76 incoming_bundles: impl IntoIterator<Item = IncomingBundle>,
77 ) -> Self;
78
79 fn with_timestamp(self, timestamp: impl Into<Timestamp>) -> Self;
81
82 fn with_epoch(self, epoch: impl Into<Epoch>) -> Self;
84
85 fn with_burn(self, amount: Amount) -> Self;
87
88 async fn into_first_proposal<S: Signer + ?Sized>(
91 self,
92 owner: AccountOwner,
93 signer: &S,
94 ) -> Result<BlockProposal, S::Error> {
95 self.into_proposal_with_round(owner, signer, Round::MultiLeader(0))
96 .await
97 }
98
99 async fn into_proposal_with_round<S: Signer + ?Sized>(
101 self,
102 owner: AccountOwner,
103 signer: &S,
104 round: Round,
105 ) -> Result<BlockProposal, S::Error>;
106}
107
108impl BlockTestExt for ProposedBlock {
109 fn with_authenticated_owner(mut self, authenticated_owner: Option<AccountOwner>) -> Self {
110 self.authenticated_owner = authenticated_owner;
111 self
112 }
113
114 fn with_operation(mut self, operation: impl Into<Operation>) -> Self {
115 self.transactions
116 .push(Transaction::ExecuteOperation(operation.into()));
117 self
118 }
119
120 fn with_transfer(self, owner: AccountOwner, recipient: Account, amount: Amount) -> Self {
121 self.with_operation(SystemOperation::Transfer {
122 owner,
123 recipient,
124 amount,
125 })
126 }
127
128 fn with_simple_transfer(self, chain_id: ChainId, amount: Amount) -> Self {
129 self.with_transfer(AccountOwner::CHAIN, Account::chain(chain_id), amount)
130 }
131
132 fn with_burn(self, amount: Amount) -> Self {
133 let recipient = Account::burn_address(self.chain_id);
134 self.with_operation(SystemOperation::Transfer {
135 owner: AccountOwner::CHAIN,
136 recipient,
137 amount,
138 })
139 }
140
141 fn with_incoming_bundle(mut self, incoming_bundle: IncomingBundle) -> Self {
142 self.transactions
143 .push(Transaction::ReceiveMessages(incoming_bundle));
144 self
145 }
146
147 fn with_incoming_bundles(
148 mut self,
149 incoming_bundles: impl IntoIterator<Item = IncomingBundle>,
150 ) -> Self {
151 self.transactions.extend(
152 incoming_bundles
153 .into_iter()
154 .map(Transaction::ReceiveMessages),
155 );
156 self
157 }
158
159 fn with_timestamp(mut self, timestamp: impl Into<Timestamp>) -> Self {
160 self.timestamp = timestamp.into();
161 self
162 }
163
164 fn with_epoch(mut self, epoch: impl Into<Epoch>) -> Self {
165 self.epoch = epoch.into();
166 self
167 }
168
169 async fn into_proposal_with_round<S: Signer + ?Sized>(
170 self,
171 owner: AccountOwner,
172 signer: &S,
173 round: Round,
174 ) -> Result<BlockProposal, S::Error> {
175 BlockProposal::new_initial(owner, round, self, signer).await
176 }
177}
178
179pub trait VoteTestExt<T: CertificateValue>: Sized {
180 fn into_certificate(self, public_key: ValidatorPublicKey) -> GenericCertificate<T>;
182}
183
184impl<T: CertificateValue> VoteTestExt<T> for Vote<T> {
185 fn into_certificate(self, public_key: ValidatorPublicKey) -> GenericCertificate<T> {
186 let state = ValidatorState {
187 network_address: "".to_string(),
188 votes: 100,
189 account_public_key: AccountPublicKey::test_key(1),
190 };
191 let committee = Committee::new(
192 vec![(public_key, state)].into_iter().collect(),
193 ResourceControlPolicy::only_fuel(),
194 );
195 SignatureAggregator::new(self.value, self.round, &committee)
196 .append(public_key, self.signature)
197 .unwrap()
198 .unwrap()
199 }
200}
201
202pub trait MessageTestExt: Sized {
204 fn to_posted(self, index: u32, kind: MessageKind) -> PostedMessage;
205}
206
207impl<T: Into<Message>> MessageTestExt for T {
208 fn to_posted(self, index: u32, kind: MessageKind) -> PostedMessage {
209 PostedMessage {
210 authenticated_owner: None,
211 grant: Amount::ZERO,
212 refund_grant_to: None,
213 kind,
214 index,
215 message: self.into(),
216 }
217 }
218}