Skip to content

Commit 6485f3d

Browse files
authored
Add a create_application contract API method (#3111)
## Motivation In some applications, we might want to instantiate other application as part of their functionality. For example, the "Requests for Quotes" app will want to instantiate the Matching Engine. ## Proposal Add a `create_application` method to the contract API, callable from within applications, reflecting the flow of manually instantiating a new application. ## Test Plan This will be tested during test runs of the Requests for Quotes application. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 1c8a710 commit 6485f3d

File tree

8 files changed

+314
-42
lines changed

8 files changed

+314
-42
lines changed

linera-execution/src/execution_state_actor.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ use prometheus::HistogramVec;
2323
use reqwest::{header::CONTENT_TYPE, Client};
2424

2525
use crate::{
26-
system::{OpenChainConfig, Recipient},
26+
system::{CreateApplicationResult, OpenChainConfig, Recipient},
2727
util::RespondExt,
28-
ExecutionError, ExecutionRuntimeContext, ExecutionStateView, RawExecutionOutcome,
28+
BytecodeId, ExecutionError, ExecutionRuntimeContext, ExecutionStateView, RawExecutionOutcome,
2929
RawOutgoingMessage, SystemExecutionError, SystemMessage, UserApplicationDescription,
3030
UserApplicationId, UserContractCode, UserServiceCode,
3131
};
@@ -289,6 +289,25 @@ where
289289
}
290290
}
291291

292+
CreateApplication {
293+
next_message_id,
294+
bytecode_id,
295+
parameters,
296+
required_application_ids,
297+
callback,
298+
} => {
299+
let create_application_result = self
300+
.system
301+
.create_application(
302+
next_message_id,
303+
bytecode_id,
304+
parameters,
305+
required_application_ids,
306+
)
307+
.await?;
308+
callback.respond(Ok(create_application_result));
309+
}
310+
292311
FetchUrl { url, callback } => {
293312
let bytes = reqwest::get(url).await?.bytes().await?.to_vec();
294313
callback.respond(bytes);
@@ -467,6 +486,15 @@ pub enum ExecutionRequest {
467486
callback: oneshot::Sender<Result<(), ExecutionError>>,
468487
},
469488

489+
CreateApplication {
490+
next_message_id: MessageId,
491+
bytecode_id: BytecodeId,
492+
parameters: Vec<u8>,
493+
required_application_ids: Vec<UserApplicationId>,
494+
#[debug(skip)]
495+
callback: oneshot::Sender<Result<CreateApplicationResult, ExecutionError>>,
496+
},
497+
470498
FetchUrl {
471499
url: String,
472500
#[debug(skip)]

linera-execution/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,15 @@ pub trait ContractRuntime: BaseRuntime {
711711
/// Closes the current chain.
712712
fn close_chain(&mut self) -> Result<(), ExecutionError>;
713713

714+
/// Creates a new application on chain.
715+
fn create_application(
716+
&mut self,
717+
bytecode_id: BytecodeId,
718+
parameters: Vec<u8>,
719+
argument: Vec<u8>,
720+
required_application_ids: Vec<UserApplicationId>,
721+
) -> Result<UserApplicationId, ExecutionError>;
722+
714723
/// Writes a batch of changes.
715724
fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
716725
}

linera-execution/src/runtime.rs

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ use crate::{
2929
execution::UserAction,
3030
execution_state_actor::{ExecutionRequest, ExecutionStateSender},
3131
resources::ResourceController,
32+
system::CreateApplicationResult,
3233
util::{ReceiverExt, UnboundedSenderExt},
33-
BaseRuntime, ContractRuntime, ExecutionError, FinalizeContext, MessageContext,
34+
BaseRuntime, BytecodeId, ContractRuntime, ExecutionError, FinalizeContext, MessageContext,
3435
OperationContext, QueryContext, RawExecutionOutcome, ServiceRuntime, TransactionTracker,
3536
UserApplicationDescription, UserApplicationId, UserContractCode, UserContractInstance,
3637
UserServiceCode, UserServiceInstance, MAX_EVENT_KEY_LEN, MAX_STREAM_NAME_LEN,
@@ -1115,6 +1116,22 @@ impl ContractSyncRuntime {
11151116
chain_id: ChainId,
11161117
action: UserAction,
11171118
) -> Result<(ResourceController, TransactionTracker), ExecutionError> {
1119+
self.deref_mut()
1120+
.run_action(application_id, chain_id, action)?;
1121+
let runtime = self
1122+
.into_inner()
1123+
.expect("Runtime clones should have been freed by now");
1124+
Ok((runtime.resource_controller, runtime.transaction_tracker))
1125+
}
1126+
}
1127+
1128+
impl ContractSyncRuntimeHandle {
1129+
fn run_action(
1130+
&mut self,
1131+
application_id: UserApplicationId,
1132+
chain_id: ChainId,
1133+
action: UserAction,
1134+
) -> Result<(), ExecutionError> {
11181135
let finalize_context = FinalizeContext {
11191136
authenticated_signer: action.signer(),
11201137
chain_id,
@@ -1135,10 +1152,7 @@ impl ContractSyncRuntime {
11351152
UserAction::Message(context, message) => code.execute_message(context, message),
11361153
})?;
11371154
self.finalize(finalize_context)?;
1138-
let runtime = self
1139-
.into_inner()
1140-
.expect("Runtime clones should have been freed by now");
1141-
Ok((runtime.resource_controller, runtime.transaction_tracker))
1155+
Ok(())
11421156
}
11431157

11441158
/// Notifies all loaded applications that execution is finalizing.
@@ -1401,6 +1415,57 @@ impl ContractRuntime for ContractSyncRuntimeHandle {
14011415
.recv_response()?
14021416
}
14031417

1418+
fn create_application(
1419+
&mut self,
1420+
bytecode_id: BytecodeId,
1421+
parameters: Vec<u8>,
1422+
argument: Vec<u8>,
1423+
required_application_ids: Vec<UserApplicationId>,
1424+
) -> Result<UserApplicationId, ExecutionError> {
1425+
let chain_id = self.inner().chain_id;
1426+
let context = OperationContext {
1427+
chain_id,
1428+
authenticated_signer: self.authenticated_signer()?,
1429+
authenticated_caller_id: self.authenticated_caller_id()?,
1430+
height: self.block_height()?,
1431+
index: None,
1432+
};
1433+
1434+
let mut this = self.inner();
1435+
let message_id = MessageId {
1436+
chain_id: context.chain_id,
1437+
height: context.height,
1438+
index: this.transaction_tracker.next_message_index(),
1439+
};
1440+
let CreateApplicationResult {
1441+
app_id,
1442+
message,
1443+
blobs_to_register,
1444+
} = this
1445+
.execution_state_sender
1446+
.send_request(|callback| ExecutionRequest::CreateApplication {
1447+
next_message_id: message_id,
1448+
bytecode_id,
1449+
parameters,
1450+
required_application_ids,
1451+
callback,
1452+
})?
1453+
.recv_response()??;
1454+
for blob_id in blobs_to_register {
1455+
this.transaction_tracker
1456+
.replay_oracle_response(OracleResponse::Blob(blob_id))?;
1457+
}
1458+
let outcome = RawExecutionOutcome::default().with_message(message);
1459+
this.transaction_tracker.add_system_outcome(outcome)?;
1460+
1461+
drop(this);
1462+
1463+
let user_action = UserAction::Instantiate(context, argument);
1464+
self.run_action(app_id, chain_id, user_action)?;
1465+
1466+
Ok(app_id)
1467+
}
1468+
14041469
fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError> {
14051470
let mut this = self.inner();
14061471
let id = this.application_id()?;

linera-execution/src/system.rs

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,13 @@ impl UserData {
343343
}
344344
}
345345

346+
#[derive(Clone, Debug)]
347+
pub struct CreateApplicationResult {
348+
pub app_id: UserApplicationId,
349+
pub message: RawOutgoingMessage<SystemMessage, Amount>,
350+
pub blobs_to_register: Vec<BlobId>,
351+
}
352+
346353
#[derive(Error, Debug)]
347354
pub enum SystemExecutionError {
348355
#[error(transparent)]
@@ -634,31 +641,23 @@ where
634641
instantiation_argument,
635642
required_application_ids,
636643
} => {
637-
let id = UserApplicationId {
638-
bytecode_id,
639-
creation: context.next_message_id(txn_tracker.next_message_index()),
640-
};
641-
for application in required_application_ids.iter().chain(iter::once(&id)) {
642-
self.check_and_record_bytecode_blobs(&application.bytecode_id, txn_tracker)
643-
.await?;
644-
}
645-
self.registry
646-
.register_new_application(
647-
id,
648-
parameters.clone(),
649-
required_application_ids.clone(),
644+
let next_message_id = context.next_message_id(txn_tracker.next_message_index());
645+
let CreateApplicationResult {
646+
app_id,
647+
message,
648+
blobs_to_register,
649+
} = self
650+
.create_application(
651+
next_message_id,
652+
bytecode_id,
653+
parameters,
654+
required_application_ids,
650655
)
651656
.await?;
652-
// Send a message to ourself to increment the message ID.
653-
let message = RawOutgoingMessage {
654-
destination: Destination::Recipient(context.chain_id),
655-
authenticated: false,
656-
grant: Amount::ZERO,
657-
kind: MessageKind::Protected,
658-
message: SystemMessage::ApplicationCreated,
659-
};
657+
self.record_bytecode_blobs(blobs_to_register, txn_tracker)
658+
.await?;
660659
outcome.messages.push(message);
661-
new_application = Some((id, instantiation_argument.clone()));
660+
new_application = Some((app_id, instantiation_argument.clone()));
662661
}
663662
RequestApplication {
664663
chain_id,
@@ -1024,6 +1023,49 @@ where
10241023
Ok(messages)
10251024
}
10261025

1026+
pub async fn create_application(
1027+
&mut self,
1028+
next_message_id: MessageId,
1029+
bytecode_id: BytecodeId,
1030+
parameters: Vec<u8>,
1031+
required_application_ids: Vec<UserApplicationId>,
1032+
) -> Result<CreateApplicationResult, SystemExecutionError> {
1033+
let id = UserApplicationId {
1034+
bytecode_id,
1035+
creation: next_message_id,
1036+
};
1037+
let mut blobs_to_register = vec![];
1038+
for application in required_application_ids.iter().chain(iter::once(&id)) {
1039+
let (contract_bytecode_blob_id, service_bytecode_blob_id) =
1040+
self.check_bytecode_blobs(&application.bytecode_id).await?;
1041+
// We only remember to register the blobs that aren't recorded in `used_blobs`
1042+
// already.
1043+
if !self.used_blobs.contains(&contract_bytecode_blob_id).await? {
1044+
blobs_to_register.push(contract_bytecode_blob_id);
1045+
}
1046+
if !self.used_blobs.contains(&service_bytecode_blob_id).await? {
1047+
blobs_to_register.push(service_bytecode_blob_id);
1048+
}
1049+
}
1050+
self.registry
1051+
.register_new_application(id, parameters.clone(), required_application_ids.clone())
1052+
.await?;
1053+
// Send a message to ourself to increment the message ID.
1054+
let message = RawOutgoingMessage {
1055+
destination: Destination::Recipient(next_message_id.chain_id),
1056+
authenticated: false,
1057+
grant: Amount::ZERO,
1058+
kind: MessageKind::Protected,
1059+
message: SystemMessage::ApplicationCreated,
1060+
};
1061+
1062+
Ok(CreateApplicationResult {
1063+
app_id: id,
1064+
message,
1065+
blobs_to_register,
1066+
})
1067+
}
1068+
10271069
/// Records a blob that is used in this block. If this is the first use on this chain, creates
10281070
/// an oracle response for it.
10291071
pub(crate) async fn blob_used(
@@ -1072,11 +1114,10 @@ where
10721114
}
10731115
}
10741116

1075-
async fn check_and_record_bytecode_blobs(
1117+
async fn check_bytecode_blobs(
10761118
&mut self,
10771119
bytecode_id: &BytecodeId,
1078-
txn_tracker: &mut TransactionTracker,
1079-
) -> Result<(), SystemExecutionError> {
1120+
) -> Result<(BlobId, BlobId), SystemExecutionError> {
10801121
let contract_bytecode_blob_id =
10811122
BlobId::new(bytecode_id.contract_blob_hash, BlobType::ContractBytecode);
10821123

@@ -1105,10 +1146,32 @@ where
11051146
missing_blobs.is_empty(),
11061147
SystemExecutionError::BlobsNotFound(missing_blobs)
11071148
);
1108-
self.blob_used(Some(txn_tracker), contract_bytecode_blob_id)
1109-
.await?;
1110-
self.blob_used(Some(txn_tracker), service_bytecode_blob_id)
1111-
.await?;
1149+
1150+
Ok((contract_bytecode_blob_id, service_bytecode_blob_id))
1151+
}
1152+
1153+
async fn record_bytecode_blobs(
1154+
&mut self,
1155+
blob_ids: Vec<BlobId>,
1156+
txn_tracker: &mut TransactionTracker,
1157+
) -> Result<(), SystemExecutionError> {
1158+
for blob_id in blob_ids {
1159+
self.blob_used(Some(txn_tracker), blob_id).await?;
1160+
}
11121161
Ok(())
11131162
}
1163+
1164+
async fn check_and_record_bytecode_blobs(
1165+
&mut self,
1166+
bytecode_id: &BytecodeId,
1167+
txn_tracker: &mut TransactionTracker,
1168+
) -> Result<(), SystemExecutionError> {
1169+
let (contract_bytecode_blob_id, service_bytecode_blob_id) =
1170+
self.check_bytecode_blobs(bytecode_id).await?;
1171+
self.record_bytecode_blobs(
1172+
vec![contract_bytecode_blob_id, service_bytecode_blob_id],
1173+
txn_tracker,
1174+
)
1175+
.await
1176+
}
11141177
}

linera-execution/src/wasm/system_api.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use tracing::log;
1717

1818
use super::WasmExecutionError;
1919
use crate::{
20-
BaseRuntime, ContractRuntime, ContractSyncRuntimeHandle, ExecutionError, ServiceRuntime,
21-
ServiceSyncRuntimeHandle,
20+
BaseRuntime, BytecodeId, ContractRuntime, ContractSyncRuntimeHandle, ExecutionError,
21+
ServiceRuntime, ServiceSyncRuntimeHandle,
2222
};
2323

2424
/// Common host data used as the `UserData` of the system API implementations.
@@ -301,6 +301,22 @@ where
301301
}
302302
}
303303

304+
/// Creates a new application on the chain, based on the supplied bytecode and
305+
/// parameters.
306+
fn create_application(
307+
caller: &mut Caller,
308+
bytecode_id: BytecodeId,
309+
parameters: Vec<u8>,
310+
argument: Vec<u8>,
311+
required_application_ids: Vec<ApplicationId>,
312+
) -> Result<ApplicationId, RuntimeError> {
313+
caller
314+
.user_data_mut()
315+
.runtime
316+
.create_application(bytecode_id, parameters, argument, required_application_ids)
317+
.map_err(|error| RuntimeError::Custom(error.into()))
318+
}
319+
304320
/// Calls another application.
305321
fn try_call_application(
306322
caller: &mut Caller,

0 commit comments

Comments
 (0)