1use linera_base::{
7 abi::{ContractAbi, ServiceAbi},
8 data_types::{
9 Amount, ApplicationPermissions, BlockHeight, Bytecode, Resources, SendMessageRequest,
10 Timestamp,
11 },
12 ensure, http,
13 identifiers::{
14 Account, AccountOwner, ApplicationId, ChainId, DataBlobHash, ModuleId, StreamName,
15 },
16 ownership::{
17 AccountPermissionError, ChainOwnership, ChangeApplicationPermissionsError, CloseChainError,
18 },
19 vm::VmRuntime,
20};
21use serde::Serialize;
22
23use super::wit::{base_runtime_api as base_wit, contract_runtime_api as contract_wit};
24use crate::{Contract, KeyValueStore, ViewStorageContext};
25
26#[derive(Debug)]
30pub struct ContractRuntime<Application>
31where
32 Application: Contract,
33{
34 application_parameters: Option<Application::Parameters>,
35 application_id: Option<ApplicationId<Application::Abi>>,
36 application_creator_chain_id: Option<ChainId>,
37 chain_id: Option<ChainId>,
38 block_height: Option<BlockHeight>,
39 message_is_bouncing: Option<Option<bool>>,
40 message_origin_chain_id: Option<Option<ChainId>>,
41 timestamp: Option<Timestamp>,
42}
43
44impl<Application> ContractRuntime<Application>
45where
46 Application: Contract,
47{
48 pub(crate) fn new() -> Self {
50 ContractRuntime {
51 application_parameters: None,
52 application_id: None,
53 application_creator_chain_id: None,
54 chain_id: None,
55 block_height: None,
56 message_is_bouncing: None,
57 message_origin_chain_id: None,
58 timestamp: None,
59 }
60 }
61
62 pub fn key_value_store(&self) -> KeyValueStore {
64 KeyValueStore::for_contracts()
65 }
66
67 pub fn root_view_storage_context(&self) -> ViewStorageContext {
69 ViewStorageContext::new_unchecked(self.key_value_store(), Vec::new(), ())
70 }
71}
72
73impl<Application> ContractRuntime<Application>
74where
75 Application: Contract,
76{
77 pub fn application_parameters(&mut self) -> Application::Parameters {
79 self.application_parameters
80 .get_or_insert_with(|| {
81 let bytes = base_wit::application_parameters();
82 serde_json::from_slice(&bytes)
83 .expect("Application parameters must be deserializable")
84 })
85 .clone()
86 }
87
88 pub fn application_id(&mut self) -> ApplicationId<Application::Abi> {
90 *self
91 .application_id
92 .get_or_insert_with(|| ApplicationId::from(base_wit::get_application_id()).with_abi())
93 }
94
95 pub fn application_creator_chain_id(&mut self) -> ChainId {
97 *self
98 .application_creator_chain_id
99 .get_or_insert_with(|| base_wit::get_application_creator_chain_id().into())
100 }
101
102 pub fn chain_id(&mut self) -> ChainId {
104 *self
105 .chain_id
106 .get_or_insert_with(|| base_wit::get_chain_id().into())
107 }
108
109 pub fn block_height(&mut self) -> BlockHeight {
111 *self
112 .block_height
113 .get_or_insert_with(|| base_wit::get_block_height().into())
114 }
115
116 pub fn system_time(&mut self) -> Timestamp {
118 *self
119 .timestamp
120 .get_or_insert_with(|| base_wit::read_system_timestamp().into())
121 }
122
123 pub fn chain_balance(&mut self) -> Amount {
125 base_wit::read_chain_balance().into()
126 }
127
128 pub fn owner_balance(&mut self, owner: AccountOwner) -> Amount {
130 base_wit::read_owner_balance(owner.into()).into()
131 }
132
133 pub fn chain_ownership(&mut self) -> ChainOwnership {
135 base_wit::get_chain_ownership().into()
136 }
137
138 pub fn http_request(&mut self, request: http::Request) -> http::Response {
146 base_wit::perform_http_request(&request.into()).into()
147 }
148
149 pub fn assert_before(&mut self, timestamp: Timestamp) {
155 base_wit::assert_before(timestamp.into());
156 }
157
158 pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec<u8> {
160 base_wit::read_data_blob(hash.into())
161 }
162
163 pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) {
165 base_wit::assert_data_blob_exists(hash.into())
166 }
167}
168
169impl<Application> ContractRuntime<Application>
170where
171 Application: Contract,
172{
173 pub fn authenticated_signer(&mut self) -> Option<AccountOwner> {
175 contract_wit::authenticated_signer().map(AccountOwner::from)
176 }
177
178 pub fn message_is_bouncing(&mut self) -> Option<bool> {
181 *self
182 .message_is_bouncing
183 .get_or_insert_with(contract_wit::message_is_bouncing)
184 }
185
186 pub fn message_origin_chain_id(&mut self) -> Option<ChainId> {
189 *self
190 .message_origin_chain_id
191 .get_or_insert_with(|| contract_wit::message_origin_chain_id().map(ChainId::from))
192 }
193
194 pub fn authenticated_caller_id(&mut self) -> Option<ApplicationId> {
197 contract_wit::authenticated_caller_id().map(ApplicationId::from)
198 }
199
200 pub fn check_account_permission(
202 &mut self,
203 owner: AccountOwner,
204 ) -> Result<(), AccountPermissionError> {
205 ensure!(
206 self.authenticated_signer() == Some(owner)
207 || self.authenticated_caller_id().map(AccountOwner::from) == Some(owner),
208 AccountPermissionError::NotPermitted(owner)
209 );
210 Ok(())
211 }
212
213 pub fn send_message(&mut self, destination: ChainId, message: Application::Message) {
215 self.prepare_message(message).send_to(destination)
216 }
217
218 pub fn prepare_message(
220 &mut self,
221 message: Application::Message,
222 ) -> MessageBuilder<Application::Message> {
223 MessageBuilder::new(message)
224 }
225
226 pub fn transfer(&mut self, source: AccountOwner, destination: Account, amount: Amount) {
229 contract_wit::transfer(source.into(), destination.into(), amount.into())
230 }
231
232 pub fn claim(&mut self, source: Account, destination: Account, amount: Amount) {
234 contract_wit::claim(source.into(), destination.into(), amount.into())
235 }
236
237 pub fn call_application<A: ContractAbi + Send>(
240 &mut self,
241 authenticated: bool,
242 application: ApplicationId<A>,
243 call: &A::Operation,
244 ) -> A::Response
245{
247 let call_bytes = A::serialize_operation(call)
248 .expect("Failed to serialize `Operation` in cross-application call");
249
250 let response_bytes = contract_wit::try_call_application(
251 authenticated,
252 application.forget_abi().into(),
253 &call_bytes,
254 );
255
256 A::deserialize_response(response_bytes)
257 .expect("Failed to deserialize `Response` in cross-application call")
258 }
259
260 pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {
262 contract_wit::emit(
263 &name.into(),
264 &bcs::to_bytes(value).expect("Failed to serialize event"),
265 )
266 }
267
268 pub fn read_event(
272 &mut self,
273 chain_id: ChainId,
274 name: StreamName,
275 index: u32,
276 ) -> Application::EventValue {
277 let event = contract_wit::read_event(chain_id.into(), &name.into(), index);
278 bcs::from_bytes(&event).expect("Failed to deserialize event")
279 }
280
281 pub fn subscribe_to_events(
283 &mut self,
284 chain_id: ChainId,
285 application_id: ApplicationId,
286 name: StreamName,
287 ) {
288 contract_wit::subscribe_to_events(chain_id.into(), application_id.into(), &name.into())
289 }
290
291 pub fn unsubscribe_from_events(
293 &mut self,
294 chain_id: ChainId,
295 application_id: ApplicationId,
296 name: StreamName,
297 ) {
298 contract_wit::unsubscribe_from_events(chain_id.into(), application_id.into(), &name.into())
299 }
300
301 pub fn query_service<A: ServiceAbi + Send>(
309 &mut self,
310 application_id: ApplicationId<A>,
311 query: A::Query,
312 ) -> A::QueryResponse {
313 let query = serde_json::to_vec(&query).expect("Failed to serialize service query");
314 let response = contract_wit::query_service(application_id.forget_abi().into(), &query);
315 serde_json::from_slice(&response).expect("Failed to deserialize service response")
316 }
317
318 pub fn open_chain(
321 &mut self,
322 chain_ownership: ChainOwnership,
323 application_permissions: ApplicationPermissions,
324 balance: Amount,
325 ) -> ChainId {
326 let chain_id = contract_wit::open_chain(
327 &chain_ownership.into(),
328 &application_permissions.into(),
329 balance.into(),
330 );
331 chain_id.into()
332 }
333
334 pub fn close_chain(&mut self) -> Result<(), CloseChainError> {
337 contract_wit::close_chain().map_err(|error| error.into())
338 }
339
340 pub fn change_application_permissions(
342 &mut self,
343 application_permissions: ApplicationPermissions,
344 ) -> Result<(), ChangeApplicationPermissionsError> {
345 contract_wit::change_application_permissions(&application_permissions.into())
346 .map_err(|error| error.into())
347 }
348
349 pub fn create_application<Abi, Parameters, InstantiationArgument>(
351 &mut self,
352 module_id: ModuleId,
353 parameters: &Parameters,
354 argument: &InstantiationArgument,
355 required_application_ids: Vec<ApplicationId>,
356 ) -> ApplicationId<Abi>
357 where
358 Abi: ContractAbi,
359 Parameters: Serialize,
360 InstantiationArgument: Serialize,
361 {
362 let parameters = serde_json::to_vec(parameters)
363 .expect("Failed to serialize `Parameters` type for a cross-application call");
364 let argument = serde_json::to_vec(argument).expect(
365 "Failed to serialize `InstantiationArgument` type for a cross-application call",
366 );
367 let converted_application_ids: Vec<_> = required_application_ids
368 .into_iter()
369 .map(From::from)
370 .collect();
371 let application_id = contract_wit::create_application(
372 module_id.into(),
373 ¶meters,
374 &argument,
375 &converted_application_ids,
376 );
377 ApplicationId::from(application_id).with_abi::<Abi>()
378 }
379
380 pub fn create_data_blob(&mut self, bytes: Vec<u8>) -> DataBlobHash {
382 let hash = contract_wit::create_data_blob(&bytes);
383 hash.into()
384 }
385
386 pub fn publish_module(
388 &mut self,
389 contract: Bytecode,
390 service: Bytecode,
391 vm_runtime: VmRuntime,
392 ) -> ModuleId {
393 contract_wit::publish_module(&contract.into(), &service.into(), vm_runtime.into()).into()
394 }
395
396 pub fn validation_round(&mut self) -> Option<u32> {
398 contract_wit::validation_round()
399 }
400}
401
402#[must_use]
405pub struct MessageBuilder<Message>
406where
407 Message: Serialize,
408{
409 authenticated: bool,
410 is_tracked: bool,
411 grant: Resources,
412 message: Message,
413}
414
415impl<Message> MessageBuilder<Message>
416where
417 Message: Serialize,
418{
419 pub(crate) fn new(message: Message) -> Self {
421 MessageBuilder {
422 authenticated: false,
423 is_tracked: false,
424 grant: Resources::default(),
425 message,
426 }
427 }
428
429 pub fn with_tracking(mut self) -> Self {
432 self.is_tracked = true;
433 self
434 }
435
436 pub fn with_authentication(mut self) -> Self {
438 self.authenticated = true;
439 self
440 }
441
442 pub fn with_grant(mut self, grant: Resources) -> Self {
444 self.grant = grant;
445 self
446 }
447
448 pub fn send_to(self, destination: ChainId) {
450 let serialized_message =
451 bcs::to_bytes(&self.message).expect("Failed to serialize message to be sent");
452
453 let raw_message = SendMessageRequest {
454 destination,
455 authenticated: self.authenticated,
456 is_tracked: self.is_tracked,
457 grant: self.grant,
458 message: serialized_message,
459 };
460
461 contract_wit::send_message(&raw_message.into())
462 }
463}