diff --git a/CLI.md b/CLI.md index 6925e5e77179..12ae13ba5a23 100644 --- a/CLI.md +++ b/CLI.md @@ -32,7 +32,6 @@ This document contains the help content for the `linera` command-line program. * [`linera read-data-blob`↴](#linera-read-data-blob) * [`linera create-application`↴](#linera-create-application) * [`linera publish-and-create`↴](#linera-publish-and-create) -* [`linera request-application`↴](#linera-request-application) * [`linera keygen`↴](#linera-keygen) * [`linera assign`↴](#linera-assign) * [`linera retry-pending-block`↴](#linera-retry-pending-block) @@ -94,7 +93,6 @@ A Byzantine-fault tolerant sidechain with low-latency finality and high throughp * `read-data-blob` — Verify that a data blob is readable * `create-application` — Create an application * `publish-and-create` — Create an application, and publish the required bytecode -* `request-application` — Request an application from another chain, so it can be used on this one * `keygen` — Create an unassigned key pair * `assign` — Link an owner with a key pair in the wallet to a chain that was created for that owner * `retry-pending-block` — Retry a block we unsuccessfully tried to propose earlier @@ -681,23 +679,6 @@ Create an application, and publish the required bytecode -## `linera request-application` - -Request an application from another chain, so it can be used on this one - -**Usage:** `linera request-application [OPTIONS] ` - -###### **Arguments:** - -* `` — The ID of the application to request - -###### **Options:** - -* `--target-chain-id ` — The target chain on which the application is already registered. If not specified, the chain on which the application was created is used -* `--requester-chain-id ` — The owned chain on which the application is missing - - - ## `linera keygen` Create an unassigned key pair diff --git a/examples/crowd-funding/tests/campaign_lifecycle.rs b/examples/crowd-funding/tests/campaign_lifecycle.rs index bad603b7cdaf..7ccc05bf1f4f 100644 --- a/examples/crowd-funding/tests/campaign_lifecycle.rs +++ b/examples/crowd-funding/tests/campaign_lifecycle.rs @@ -64,8 +64,6 @@ async fn collect_pledges() { let mut pledges_and_transfers = Vec::new(); for (backer_chain, backer_account, _balance) in &backers { - backer_chain.register_application(campaign_id).await; - let pledge_certificate = backer_chain .add_block(|block| { block.with_operation( @@ -78,7 +76,7 @@ async fn collect_pledges() { }) .await; - assert_eq!(pledge_certificate.outgoing_message_count(), 3); + assert_eq!(pledge_certificate.outgoing_message_count(), 2); pledges_and_transfers.push(pledge_certificate); } @@ -168,8 +166,6 @@ async fn cancel_successful_campaign() { let mut pledges_and_transfers = Vec::new(); for (backer_chain, backer_account, _balance) in &backers { - backer_chain.register_application(campaign_id).await; - let pledge_certificate = backer_chain .add_block(|block| { block.with_operation( @@ -182,7 +178,7 @@ async fn cancel_successful_campaign() { }) .await; - assert_eq!(pledge_certificate.outgoing_message_count(), 3); + assert_eq!(pledge_certificate.outgoing_message_count(), 2); pledges_and_transfers.push(pledge_certificate); } diff --git a/examples/fungible/src/lib.rs b/examples/fungible/src/lib.rs index 7578ada5e3a0..0a3afa4095d1 100644 --- a/examples/fungible/src/lib.rs +++ b/examples/fungible/src/lib.rs @@ -75,8 +75,6 @@ pub async fn create_with_accounts( .await; for (chain, account, initial_amount) in &accounts { - chain.register_application(application_id).await; - let claim_certificate = chain .add_block(|block| { block.with_operation( @@ -96,7 +94,7 @@ pub async fn create_with_accounts( }) .await; - assert_eq!(claim_certificate.outgoing_message_count(), 2); + assert_eq!(claim_certificate.outgoing_message_count(), 1); let transfer_certificate = token_chain .add_block(|block| { @@ -104,7 +102,7 @@ pub async fn create_with_accounts( }) .await; - assert_eq!(transfer_certificate.outgoing_message_count(), 2); + assert_eq!(transfer_certificate.outgoing_message_count(), 1); chain .add_block(|block| { diff --git a/examples/fungible/tests/cross_chain.rs b/examples/fungible/tests/cross_chain.rs index 9d1e61ebca6d..f6c02ec434b0 100644 --- a/examples/fungible/tests/cross_chain.rs +++ b/examples/fungible/tests/cross_chain.rs @@ -95,8 +95,6 @@ async fn test_bouncing_tokens() { let receiver_chain = validator.new_chain().await; let receiver_account = AccountOwner::from(receiver_chain.public_key()); - receiver_chain.register_application(application_id).await; - let certificate = sender_chain .add_block(|block| { block.with_operation( diff --git a/examples/matching-engine/tests/transaction.rs b/examples/matching-engine/tests/transaction.rs index 7c94c98778c0..2921997e0d88 100644 --- a/examples/matching-engine/tests/transaction.rs +++ b/examples/matching-engine/tests/transaction.rs @@ -106,9 +106,6 @@ async fn single_transaction() { ) .await; - user_chain_a.register_application(token_id_b).await; - user_chain_b.register_application(token_id_a).await; - // Check the initial starting amounts for chain a and chain b for (owner, amount) in [ (admin_account, None), @@ -138,9 +135,6 @@ async fn single_transaction() { vec![token_id_a.forget_abi(), token_id_b.forget_abi()], ) .await; - // Doing the registrations - user_chain_a.register_application(matching_id).await; - user_chain_b.register_application(matching_id).await; // Creating the bid orders let mut bid_certificates = Vec::new(); diff --git a/linera-base/src/data_types.rs b/linera-base/src/data_types.rs index 906e8c1a066a..39644bfd81c9 100644 --- a/linera-base/src/data_types.rs +++ b/linera-base/src/data_types.rs @@ -33,7 +33,7 @@ use crate::{ doc_scalar, hex_debug, http, identifiers::{ ApplicationId, BlobId, BlobType, BytecodeId, ChainId, Destination, EventId, - GenericApplicationId, MessageId, StreamId, UserApplicationId, + GenericApplicationId, StreamId, UserApplicationId, }, limited_writer::{LimitedWriter, LimitedWriterError}, time::{Duration, SystemTime}, @@ -782,13 +782,17 @@ pub enum OracleResponse { impl<'de> BcsHashable<'de> for OracleResponse {} -/// Description of the necessary information to run a user application. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)] +/// Description of the necessary information to run a user application used within blobs. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize, WitType, WitStore)] pub struct UserApplicationDescription { /// The unique ID of the bytecode to use for the application. pub bytecode_id: BytecodeId, - /// The unique ID of the application's creation. - pub creation: MessageId, + /// The chain ID that created the application. + pub creator_chain_id: ChainId, + /// Height of the block that created this application. + pub block_height: BlockHeight, + /// The index of the application among those created in the same block. + pub application_index: u32, /// The parameters of the application. #[serde(with = "serde_bytes")] #[debug(with = "hex_debug")] @@ -799,10 +803,19 @@ pub struct UserApplicationDescription { impl From<&UserApplicationDescription> for UserApplicationId { fn from(description: &UserApplicationDescription) -> Self { - UserApplicationId { - bytecode_id: description.bytecode_id, - creation: description.creation, - } + UserApplicationId::new( + CryptoHash::new(&BlobContent::new_application_description(description)), + description.bytecode_id, + ) + } +} + +impl BcsHashable<'_> for UserApplicationDescription {} + +impl UserApplicationDescription { + /// Gets the serialized bytes for this `BlobUserApplicationDescription`. + pub fn to_bytes(&self) -> Vec { + bcs::to_bytes(self).expect("Serializing blob bytes should not fail!") } } @@ -939,8 +952,7 @@ impl CompressedBytecode { impl<'a> BcsHashable<'a> for BlobContent {} /// A blob of binary data. -#[derive(Hash, Clone, Debug, Serialize, Deserialize)] -#[cfg_attr(with_testing, derive(Eq, PartialEq))] +#[derive(Hash, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BlobContent { /// The type of data represented by the bytes. blob_type: BlobType, @@ -978,6 +990,14 @@ impl BlobContent { ) } + /// Creates a new application description [`BlobContent`] from a [`UserApplicationDescription`]. + pub fn new_application_description( + application_description: &UserApplicationDescription, + ) -> Self { + let bytes = application_description.to_bytes(); + BlobContent::new(BlobType::ApplicationDescription, bytes) + } + /// Gets a reference to the blob's bytes. pub fn bytes(&self) -> &[u8] { &self.bytes @@ -1001,8 +1021,7 @@ impl From for BlobContent { } /// A blob of binary data, with its hash. -#[derive(Debug, Hash, Clone)] -#[cfg_attr(with_testing, derive(Eq, PartialEq))] +#[derive(Debug, Hash, PartialEq, Eq, Clone)] pub struct Blob { /// ID of the blob. hash: CryptoHash, @@ -1043,6 +1062,16 @@ impl Blob { Blob::new(BlobContent::new_service_bytecode(compressed_bytecode)) } + /// Creates a new application description [`BlobContent`] from the provided + /// description. + pub fn new_application_description( + application_description: &UserApplicationDescription, + ) -> Self { + Blob::new(BlobContent::new_application_description( + application_description, + )) + } + /// A content-addressed blob ID i.e. the hash of the `Blob`. pub fn id(&self) -> BlobId { BlobId { diff --git a/linera-base/src/identifiers.rs b/linera-base/src/identifiers.rs index cc64398d6cfd..4f3a7d16a319 100644 --- a/linera-base/src/identifiers.rs +++ b/linera-base/src/identifiers.rs @@ -163,6 +163,8 @@ pub enum BlobType { ContractBytecode, /// A blob containing compressed service bytecode. ServiceBytecode, + /// A blob containing an application description. + ApplicationDescription, } impl Display for BlobType { @@ -288,14 +290,14 @@ pub struct MessageId { pub index: u32, } -/// A unique identifier for a user application. -#[derive(Debug, WitLoad, WitStore, WitType)] +/// A unique identifier for a user application from a blob. +#[derive(WitLoad, WitStore, WitType)] #[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))] pub struct ApplicationId { + /// The hash of the `UserApplicationDescription` this refers to. + pub application_description_hash: CryptoHash, /// The bytecode to use for the application. pub bytecode_id: BytecodeId, - /// The unique ID of the application's creation. - pub creation: MessageId, } /// Alias for `ApplicationId`. Use this alias in the core @@ -725,11 +727,8 @@ impl Copy for ApplicationId {} impl PartialEq for ApplicationId { fn eq(&self, other: &Self) -> bool { - let ApplicationId { - bytecode_id, - creation, - } = other; - self.bytecode_id == *bytecode_id && self.creation == *creation + self.application_description_hash == other.application_description_hash + && self.bytecode_id == other.bytecode_id } } @@ -737,46 +736,42 @@ impl Eq for ApplicationId {} impl PartialOrd for ApplicationId { fn partial_cmp(&self, other: &Self) -> Option { - let ApplicationId { - bytecode_id, - creation, - } = other; - match self.bytecode_id.partial_cmp(bytecode_id) { - Some(std::cmp::Ordering::Equal) => self.creation.partial_cmp(creation), - result => result, - } + (self.application_description_hash, self.bytecode_id) + .partial_cmp(&(other.application_description_hash, other.bytecode_id)) } } impl Ord for ApplicationId { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - let ApplicationId { - bytecode_id, - creation, - } = other; - match self.bytecode_id.cmp(bytecode_id) { - std::cmp::Ordering::Equal => self.creation.cmp(creation), - result => result, - } + (self.application_description_hash, self.bytecode_id) + .cmp(&(other.application_description_hash, other.bytecode_id)) } } impl Hash for ApplicationId { fn hash(&self, state: &mut H) { - let ApplicationId { - bytecode_id, - creation, - } = self; - bytecode_id.hash(state); - creation.hash(state); + self.application_description_hash.hash(state); + self.bytecode_id.hash(state); + } +} + +impl fmt::Debug for ApplicationId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ApplicationId") + .field( + "application_description_hash", + &self.application_description_hash, + ) + .field("bytecode_id", &self.bytecode_id) + .finish() } } #[derive(Serialize, Deserialize)] #[serde(rename = "ApplicationId")] struct SerializableApplicationId { + pub application_description_hash: CryptoHash, pub bytecode_id: BytecodeId, - pub creation: MessageId, } impl Serialize for ApplicationId { @@ -786,16 +781,16 @@ impl Serialize for ApplicationId { { if serializer.is_human_readable() { let bytes = bcs::to_bytes(&SerializableApplicationId { + application_description_hash: self.application_description_hash, bytecode_id: self.bytecode_id.forget_abi(), - creation: self.creation, }) .map_err(serde::ser::Error::custom)?; serializer.serialize_str(&hex::encode(bytes)) } else { SerializableApplicationId::serialize( &SerializableApplicationId { + application_description_hash: self.application_description_hash, bytecode_id: self.bytecode_id.forget_abi(), - creation: self.creation, }, serializer, ) @@ -814,25 +809,33 @@ impl<'de, A> Deserialize<'de> for ApplicationId { let application_id: SerializableApplicationId = bcs::from_bytes(&application_id_bytes).map_err(serde::de::Error::custom)?; Ok(ApplicationId { + application_description_hash: application_id.application_description_hash, bytecode_id: application_id.bytecode_id.with_abi(), - creation: application_id.creation, }) } else { let value = SerializableApplicationId::deserialize(deserializer)?; Ok(ApplicationId { + application_description_hash: value.application_description_hash, bytecode_id: value.bytecode_id.with_abi(), - creation: value.creation, }) } } } impl ApplicationId { + /// Creates an application ID from the application description hash. + pub fn new(application_description_hash: CryptoHash, bytecode_id: BytecodeId) -> Self { + ApplicationId { + application_description_hash, + bytecode_id, + } + } + /// Specializes an application ID for a given ABI. pub fn with_abi(self) -> ApplicationId { ApplicationId { + application_description_hash: self.application_description_hash, bytecode_id: self.bytecode_id.with_abi(), - creation: self.creation, } } } @@ -841,8 +844,8 @@ impl ApplicationId { /// Forgets the ABI of a bytecode ID (if any). pub fn forget_abi(self) -> ApplicationId { ApplicationId { + application_description_hash: self.application_description_hash, bytecode_id: self.bytecode_id.forget_abi(), - creation: self.creation, } } } diff --git a/linera-base/src/unit_tests.rs b/linera-base/src/unit_tests.rs index b49052274b40..075947aa97db 100644 --- a/linera-base/src/unit_tests.rs +++ b/linera-base/src/unit_tests.rs @@ -104,11 +104,7 @@ fn application_id_test_case() -> ApplicationId { CryptoHash::test_hash("contract bytecode"), CryptoHash::test_hash("service bytecode"), ), - creation: MessageId { - chain_id: ChainId::root(0), - height: BlockHeight(0), - index: 0, - }, + application_description_hash: CryptoHash::test_hash("application description"), } } diff --git a/linera-chain/src/block.rs b/linera-chain/src/block.rs index 948ad1698001..18d2bbe3c6ce 100644 --- a/linera-chain/src/block.rs +++ b/linera-chain/src/block.rs @@ -2,12 +2,15 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::{collections::BTreeSet, fmt::Debug}; +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt::Debug, +}; use async_graphql::SimpleObject; use linera_base::{ crypto::{BcsHashable, CryptoHash}, - data_types::{BlockHeight, Event, OracleResponse, Timestamp}, + data_types::{Blob, BlockHeight, Event, OracleResponse, Timestamp}, hashed::Hashed, identifiers::{BlobId, BlobType, ChainId, MessageId, Owner}, }; @@ -360,6 +363,8 @@ pub struct BlockBody { pub oracle_responses: Vec>, /// The list of events produced by each transaction. pub events: Vec>, + /// The list of blobs produced by each transaction. + pub blobs: Vec>, } impl Block { @@ -391,6 +396,7 @@ impl Block { messages: outcome.messages, oracle_responses: outcome.oracle_responses, events: outcome.events, + blobs: outcome.blobs, }; Self { header, body } @@ -492,12 +498,15 @@ impl Block { pub fn required_blob_ids(&self) -> BTreeSet { let mut blob_ids = self.oracle_blob_ids(); blob_ids.extend(self.published_blob_ids()); + blob_ids.extend(self.created_blob_ids()); blob_ids } /// Returns whether this block requires the blob with the specified ID. pub fn requires_blob(&self, blob_id: &BlobId) -> bool { - self.oracle_blob_ids().contains(blob_id) || self.published_blob_ids().contains(blob_id) + self.oracle_blob_ids().contains(blob_id) + || self.published_blob_ids().contains(blob_id) + || self.created_blob_ids().contains(blob_id) } /// Returns all the published blob IDs in this block's operations. @@ -518,6 +527,26 @@ impl Block { blob_ids } + /// Returns all the blob IDs created by the block's operations. + fn created_blob_ids(&self) -> BTreeSet { + self.body + .blobs + .iter() + .flatten() + .map(|blob| blob.id()) + .collect() + } + + /// Returns all the blobs created by the block's operations. + pub fn created_blobs(&self) -> BTreeMap { + self.body + .blobs + .iter() + .flatten() + .map(|blob| (blob.id(), blob.clone())) + .collect() + } + /// Returns set of blob IDs that were a result of an oracle call. pub fn oracle_blob_ids(&self) -> BTreeSet { let mut required_blob_ids = BTreeSet::new(); @@ -563,6 +592,7 @@ impl From for ExecutedBlock { messages, oracle_responses, events, + blobs, }, } = block; @@ -582,6 +612,7 @@ impl From for ExecutedBlock { messages, oracle_responses, events, + blobs, }; ExecutedBlock { block, outcome } diff --git a/linera-chain/src/chain.rs b/linera-chain/src/chain.rs index 05a3a608d998..fab90b26b4be 100644 --- a/linera-chain/src/chain.rs +++ b/linera-chain/src/chain.rs @@ -152,7 +152,7 @@ static STATE_HASH_COMPUTATION_LATENCY: LazyLock = LazyLock::new(|| }); /// The BCS-serialized size of an empty [`Block`]. -const EMPTY_BLOCK_SIZE: usize = 91; +const EMPTY_BLOCK_SIZE: usize = 92; /// An origin, cursor and timestamp of a unskippable bundle in our inbox. #[derive(Debug, Clone, Serialize, Deserialize, async_graphql::SimpleObject)] @@ -355,7 +355,6 @@ where ) -> Result { self.execution_state .system - .registry .describe_application(application_id) .await .with_execution_context(ChainExecutionContext::DescribeApplication) @@ -741,8 +740,10 @@ where // Collect messages, events and oracle responses, each as one list per transaction. let mut replaying_oracle_responses = replaying_oracle_responses.map(Vec::into_iter); let mut next_message_index = 0; + let mut next_application_index = 0; let mut oracle_responses = Vec::new(); let mut events = Vec::new(); + let mut blobs = Vec::new(); let mut messages = Vec::new(); for (txn_index, transaction) in block.transactions() { let chain_execution_context = match transaction { @@ -754,7 +755,11 @@ where Some(None) => return Err(ChainError::MissingOracleResponseList), None => None, }; - let mut txn_tracker = TransactionTracker::new(next_message_index, maybe_responses); + let mut txn_tracker = TransactionTracker::new( + next_message_index, + next_application_index, + maybe_responses, + ); match transaction { Transaction::ReceiveMessages(incoming_bundle) => { resource_controller @@ -806,14 +811,11 @@ where } } - self.execution_state - .update_execution_outcomes_with_app_registrations(&mut txn_tracker) - .await - .with_execution_context(chain_execution_context)?; let txn_outcome = txn_tracker .into_outcome() .with_execution_context(chain_execution_context)?; next_message_index = txn_outcome.next_message_index; + next_application_index = txn_outcome.next_application_index; // Update the channels. self.process_unsubscribes(txn_outcome.unsubscribe).await?; @@ -857,6 +859,7 @@ where oracle_responses.push(txn_outcome.oracle_responses); messages.push(txn_messages); events.push(txn_outcome.events); + blobs.push(txn_outcome.blobs); } // Finally, charge for the block fee, except if the chain is closed. Closed chains should @@ -886,6 +889,7 @@ where state_hash, oracle_responses, events, + blobs, }; Ok(outcome) } diff --git a/linera-chain/src/data_types.rs b/linera-chain/src/data_types.rs index c1a4fa096a34..92c761df3226 100644 --- a/linera-chain/src/data_types.rs +++ b/linera-chain/src/data_types.rs @@ -15,7 +15,7 @@ use linera_base::{ AccountPublicKey, AccountSecretKey, AccountSignature, BcsHashable, BcsSignable, CryptoError, CryptoHash, ValidatorPublicKey, ValidatorSecretKey, ValidatorSignature, }, - data_types::{Amount, BlockHeight, Event, OracleResponse, Round, Timestamp}, + data_types::{Amount, Blob, BlockHeight, Event, OracleResponse, Round, Timestamp}, doc_scalar, ensure, hashed::Hashed, identifiers::{ @@ -395,6 +395,8 @@ pub struct BlockExecutionOutcome { pub oracle_responses: Vec>, /// The list of events produced by each transaction. pub events: Vec>, + /// The list of blobs created by each transaction. + pub blobs: Vec>, } /// The hash and chain ID of a `CertificateValue`. @@ -686,12 +688,14 @@ impl ExecutedBlock { pub fn required_blob_ids(&self) -> HashSet { let mut blob_ids = self.outcome.oracle_blob_ids(); blob_ids.extend(self.block.published_blob_ids()); + blob_ids.extend(self.outcome.iter_created_blobs_ids()); blob_ids } pub fn requires_blob(&self, blob_id: &BlobId) -> bool { self.outcome.oracle_blob_ids().contains(blob_id) || self.block.published_blob_ids().contains(blob_id) + || self.outcome.created_blobs_ids().contains(blob_id) } } @@ -721,6 +725,21 @@ impl BlockExecutionOutcome { .iter() .any(|responses| !responses.is_empty()) } + + pub fn iter_created_blobs(&self) -> impl Iterator + '_ { + self.blobs + .iter() + .flatten() + .map(|blob| (blob.id(), blob.clone())) + } + + pub fn iter_created_blobs_ids(&self) -> impl Iterator + '_ { + self.blobs.iter().flatten().map(|blob| blob.id()) + } + + pub fn created_blobs_ids(&self) -> HashSet { + self.iter_created_blobs_ids().collect() + } } /// The data a block proposer signs. @@ -786,6 +805,17 @@ impl BlockProposal { ) } + pub fn expected_blob_ids(&self) -> impl Iterator + '_ { + self.content.block.published_blob_ids().into_iter().chain( + self.content.outcome.iter().flat_map(|outcome| { + outcome + .oracle_blob_ids() + .into_iter() + .chain(outcome.iter_created_blobs_ids()) + }), + ) + } + /// Checks that the public key matches the owner and that the optional certificate matches /// the outcome. pub fn check_invariants(&self) -> Result<(), &'static str> { diff --git a/linera-chain/src/unit_tests/chain_tests.rs b/linera-chain/src/unit_tests/chain_tests.rs index 2a9b68c075cb..dafda5e0a8a0 100644 --- a/linera-chain/src/unit_tests/chain_tests.rs +++ b/linera-chain/src/unit_tests/chain_tests.rs @@ -71,7 +71,9 @@ fn make_app_description() -> (UserApplicationDescription, Blob, Blob) { ( UserApplicationDescription { bytecode_id, - creation: make_admin_message_id(BlockHeight(2)), + creator_chain_id: admin_id(), + block_height: BlockHeight(2), + application_index: 0, required_application_ids: vec![], parameters: vec![], }, @@ -112,7 +114,7 @@ async fn test_block_size_limit() { let mut chain = ChainStateView::new(chain_id).await; // The size of the executed valid block below. - let maximum_executed_block_size = 685; + let maximum_executed_block_size = 687; // Initialize the chain. let mut config = make_open_chain_config(); @@ -198,7 +200,13 @@ async fn test_application_permissions() -> anyhow::Result<()> { extra .user_contracts() .insert(application_id, application.clone().into()); - extra.add_blobs([contract_blob, service_blob]).await?; + extra + .add_blobs([ + contract_blob, + service_blob, + Blob::new_application_description(&app_description), + ]) + .await?; // Initialize the chain, with a chain application. let config = OpenChainConfig { @@ -210,10 +218,6 @@ async fn test_application_permissions() -> anyhow::Result<()> { .await?; let open_chain_message = Message::System(SystemMessage::OpenChain(config)); - let register_app_message = SystemMessage::RegisterApplications { - applications: vec![app_description], - }; - // The OpenChain message must be included in the first block. Also register the app. let bundle = IncomingBundle { origin: Origin::chain(admin_id()), @@ -222,10 +226,7 @@ async fn test_application_permissions() -> anyhow::Result<()> { height: BlockHeight(1), transaction_index: 0, timestamp: Timestamp::from(0), - messages: vec![ - open_chain_message.to_posted(0, MessageKind::Protected), - register_app_message.to_posted(1, MessageKind::Simple), - ], + messages: vec![open_chain_message.to_posted(0, MessageKind::Protected)], }, action: MessageAction::Accept, }; diff --git a/linera-chain/src/unit_tests/data_types_tests.rs b/linera-chain/src/unit_tests/data_types_tests.rs index a99bec38d8cd..b4ee7652bb84 100644 --- a/linera-chain/src/unit_tests/data_types_tests.rs +++ b/linera-chain/src/unit_tests/data_types_tests.rs @@ -23,6 +23,7 @@ fn test_signed_values() { state_hash: CryptoHash::test_hash("state"), oracle_responses: vec![Vec::new()], events: vec![Vec::new()], + blobs: vec![Vec::new()], } .with(block); let confirmed_value = Hashed::new(ConfirmedBlock::new(executed_block.clone())); @@ -71,6 +72,7 @@ fn test_hashes() { state_hash: CryptoHash::test_hash("state"), oracle_responses: vec![Vec::new()], events: vec![Vec::new()], + blobs: vec![Vec::new()], } .with(block); let confirmed_hashed = Hashed::new(ConfirmedBlock::new(executed_block.clone())); @@ -94,6 +96,7 @@ fn test_certificates() { state_hash: CryptoHash::test_hash("state"), oracle_responses: vec![Vec::new()], events: vec![Vec::new()], + blobs: vec![Vec::new()], } .with(block); let value = Hashed::new(ConfirmedBlock::new(executed_block)); diff --git a/linera-client/src/client_context.rs b/linera-client/src/client_context.rs index 78dda4b5b0b1..7e23b6262971 100644 --- a/linera-client/src/client_context.rs +++ b/linera-client/src/client_context.rs @@ -906,7 +906,6 @@ where &mut self, key_pairs: &HashMap, application_id: ApplicationId, - chain_clients: &HashMap>, ) -> Result<(), Error> { let default_chain_id = self .wallet @@ -942,49 +941,6 @@ where .expect("should execute block with Transfer operations"); } self.update_wallet_from_client(&chain_client).await?; - // Make sure all chains have registered the application now. - let mut join_set = task::JoinSet::new(); - for (chain_id, chain_client) in chain_clients.iter() { - let chain_id = *chain_id; - let chain_client = chain_client.clone(); - join_set.spawn(async move { - let mut delay_ms = 0; - let mut total_delay_ms = 0; - loop { - linera_base::time::timer::sleep(Duration::from_millis(delay_ms)).await; - chain_client.process_inbox().await?; - let chain_state = chain_client.chain_state_view().await?; - if chain_state - .execution_state - .system - .registry - .known_applications - .contains_key(&application_id) - .await? - { - return Ok::<_, Error>(()); - } - - total_delay_ms += delay_ms; - // If we've been waiting already for more than 10 seconds, give up. - if total_delay_ms > 10_000 { - break; - } - - if delay_ms == 0 { - delay_ms = 100; - } else { - delay_ms *= 2; - } - } - panic!("Could not instantiate application on chain {chain_id:?}"); - }); - } - join_set - .join_all() - .await - .into_iter() - .collect::, _>>()?; Ok(()) } diff --git a/linera-client/src/client_options.rs b/linera-client/src/client_options.rs index 7bf5f65c8c7b..bce2cfa910d4 100644 --- a/linera-client/src/client_options.rs +++ b/linera-client/src/client_options.rs @@ -860,21 +860,6 @@ pub enum ClientCommand { required_application_ids: Option>, }, - /// Request an application from another chain, so it can be used on this one. - RequestApplication { - /// The ID of the application to request. - application_id: UserApplicationId, - - /// The target chain on which the application is already registered. - /// If not specified, the chain on which the application was created is used. - #[arg(long)] - target_chain_id: Option, - - /// The owned chain on which the application is missing. - #[arg(long)] - requester_chain_id: Option, - }, - /// Create an unassigned key pair. Keygen, diff --git a/linera-core/src/chain_worker/state/attempted_changes.rs b/linera-core/src/chain_worker/state/attempted_changes.rs index 4a5e4b3e8e37..27eaaf7bbe3f 100644 --- a/linera-core/src/chain_worker/state/attempted_changes.rs +++ b/linera-core/src/chain_worker/state/attempted_changes.rs @@ -160,7 +160,7 @@ where } let maybe_blobs = self .state - .maybe_get_required_blobs(proposal.required_blob_ids()) + .maybe_get_required_blobs(proposal.required_blob_ids(), None) .await?; let missing_blob_ids = super::missing_blob_ids(&maybe_blobs); if !missing_blob_ids.is_empty() { @@ -190,9 +190,10 @@ where ) -> Result<(), WorkerError> { // Create the vote and store it in the chain state. let executed_block = outcome.with(proposal.content.block.clone()); + let created_blobs: BTreeMap<_, _> = executed_block.outcome.iter_created_blobs().collect(); let blobs = self .state - .get_required_blobs(proposal.required_blob_ids()) + .get_required_blobs(proposal.expected_blob_ids(), &created_blobs) .await?; let key_pair = self.state.config.key_pair(); let manager = &mut self.state.chain.manager; @@ -258,7 +259,7 @@ where let required_blob_ids = block.required_blob_ids(); let maybe_blobs = self .state - .maybe_get_required_blobs(required_blob_ids) + .maybe_get_required_blobs(required_blob_ids, Some(&block.created_blobs())) .await?; let missing_blob_ids = super::missing_blob_ids(&maybe_blobs); if !missing_blob_ids.is_empty() { @@ -337,9 +338,10 @@ where ); let required_blob_ids = executed_block.required_blob_ids(); + let created_blobs: BTreeMap<_, _> = executed_block.outcome.iter_created_blobs().collect(); let blobs_result = self .state - .get_required_blobs(executed_block.required_blob_ids()) + .get_required_blobs(executed_block.required_blob_ids(), &created_blobs) .await .map(|blobs| blobs.into_values().collect::>()); diff --git a/linera-core/src/chain_worker/state/mod.rs b/linera-core/src/chain_worker/state/mod.rs index e3c0716a044a..6a06d7063da1 100644 --- a/linera-core/src/chain_worker/state/mod.rs +++ b/linera-core/src/chain_worker/state/mod.rs @@ -339,8 +339,11 @@ where async fn get_required_blobs( &self, required_blob_ids: impl IntoIterator, + created_blobs: &BTreeMap, ) -> Result, WorkerError> { - let maybe_blobs = self.maybe_get_required_blobs(required_blob_ids).await?; + let maybe_blobs = self + .maybe_get_required_blobs(required_blob_ids, Some(created_blobs)) + .await?; let not_found_blob_ids = missing_blob_ids(&maybe_blobs); ensure!( not_found_blob_ids.is_empty(), @@ -356,11 +359,14 @@ where async fn maybe_get_required_blobs( &self, blob_ids: impl IntoIterator, + created_blobs: Option<&BTreeMap>, ) -> Result>, WorkerError> { let mut maybe_blobs = BTreeMap::from_iter(blob_ids.into_iter().zip(iter::repeat(None))); for (blob_id, maybe_blob) in &mut maybe_blobs { - if let Some(blob) = self.chain.manager.pending_blob(blob_id).await? { + if let Some(blob) = created_blobs.and_then(|blob_map| blob_map.get(blob_id)) { + *maybe_blob = Some(blob.clone()); + } else if let Some(blob) = self.chain.manager.pending_blob(blob_id).await? { *maybe_blob = Some(blob); } else if let Some(blob) = self.chain.pending_validated_blobs.get(blob_id).await? { *maybe_blob = Some(blob); diff --git a/linera-core/src/client/mod.rs b/linera-core/src/client/mod.rs index 00fbe23e3c29..dd60d9532264 100644 --- a/linera-core/src/client/mod.rs +++ b/linera-core/src/client/mod.rs @@ -56,7 +56,7 @@ use linera_execution::{ committee::{Committee, Epoch}, system::{ AdminOperation, OpenChainConfig, Recipient, SystemChannel, SystemOperation, - CREATE_APPLICATION_MESSAGE_INDEX, OPEN_CHAIN_MESSAGE_INDEX, + OPEN_CHAIN_MESSAGE_INDEX, }, ExecutionError, Operation, Query, QueryOutcome, QueryResponse, SystemExecutionError, SystemQuery, SystemResponse, @@ -2389,22 +2389,6 @@ where )) } - /// Requests a `RegisterApplications` message from another chain so the application can be used - /// on this one. - #[instrument(level = "trace")] - pub async fn request_application( - &self, - application_id: UserApplicationId, - chain_id: Option, - ) -> Result, ChainClientError> { - let chain_id = chain_id.unwrap_or(application_id.creation.chain_id); - self.execute_operation(Operation::System(SystemOperation::RequestApplication { - application_id, - chain_id, - })) - .await - } - /// Sends tokens to a chain. #[instrument(level = "trace")] pub async fn transfer_to_account( @@ -2958,12 +2942,22 @@ where .await? .try_map(|certificate| { // The first message of the only operation created the application. - let creation = certificate + let mut creation: Vec<_> = certificate .block() - .message_id_for_operation(0, CREATE_APPLICATION_MESSAGE_INDEX) - .ok_or_else(|| ChainClientError::InternalError("Failed to create application"))?; + .required_blob_ids() + .into_iter() + .filter(|blob_id| blob_id.blob_type == BlobType::ApplicationDescription) + .collect(); + if creation.len() > 1 { + return Err(ChainClientError::InternalError( + "Unexpected number of application descriptions published", + )); + } + let blob_id = creation.pop().ok_or(ChainClientError::InternalError( + "ApplicationDescription blob not found.", + ))?; let id = ApplicationId { - creation, + application_description_hash: blob_id.hash, bytecode_id, }; Ok((id, certificate)) diff --git a/linera-core/src/unit_tests/wasm_worker_tests.rs b/linera-core/src/unit_tests/wasm_worker_tests.rs index 5ad39aafaacb..c8f4e00bc223 100644 --- a/linera-core/src/unit_tests/wasm_worker_tests.rs +++ b/linera-core/src/unit_tests/wasm_worker_tests.rs @@ -19,22 +19,17 @@ use linera_base::{ Amount, Blob, BlockHeight, Bytecode, OracleResponse, Timestamp, UserApplicationDescription, }, hashed::Hashed, - identifiers::{ - BytecodeId, ChainDescription, ChainId, Destination, MessageId, UserApplicationId, - }, + identifiers::{BytecodeId, ChainDescription, ChainId}, ownership::ChainOwnership, }; use linera_chain::{ - data_types::{BlockExecutionOutcome, OutgoingMessage}, + data_types::BlockExecutionOutcome, test::{make_child_block, make_first_block, BlockTestExt}, types::ConfirmedBlock, }; use linera_execution::{ - committee::Epoch, - system::{SystemMessage, SystemOperation}, - test_utils::SystemExecutionState, - Message, MessageKind, Operation, OperationContext, ResourceController, TransactionTracker, - WasmContractModule, WasmRuntime, + committee::Epoch, system::SystemOperation, test_utils::SystemExecutionState, Operation, + OperationContext, ResourceController, TransactionTracker, WasmContractModule, WasmRuntime, }; use linera_storage::{DbStorage, Storage}; #[cfg(feature = "dynamodb")] @@ -148,6 +143,7 @@ where BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: publisher_state_hash, oracle_responses: vec![vec![]], } @@ -193,34 +189,25 @@ where instantiation_argument: initial_value_bytes.clone(), required_application_ids: vec![], }; - let application_id = UserApplicationId { - bytecode_id, - creation: MessageId { - chain_id: creator_chain.into(), - height: BlockHeight::from(0), - index: 0, - }, - }; let application_description = UserApplicationDescription { bytecode_id, - creation: application_id.creation, + creator_chain_id: creator_chain.into(), + block_height: BlockHeight::from(0), + application_index: 0, required_application_ids: vec![], parameters: parameters_bytes, }; + let application_id = From::from(&application_description); let create_block = make_first_block(creator_chain.into()) .with_timestamp(2) .with_operation(create_operation); - creator_system_state - .registry - .known_applications - .insert(application_id, application_description.clone()); creator_system_state.timestamp = Timestamp::from(2); let mut creator_state = creator_system_state.into_view().await; creator_state .simulate_instantiation( contract.into(), Timestamp::from(2), - application_description, + application_description.clone(), initial_value_bytes.clone(), contract_blob, service_blob, @@ -228,20 +215,16 @@ where .await?; let create_block_proposal = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { - messages: vec![vec![OutgoingMessage { - destination: Destination::Recipient(creator_chain.into()), - authenticated_signer: None, - grant: Amount::ZERO, - refund_grant_to: None, - kind: MessageKind::Protected, - message: Message::System(SystemMessage::ApplicationCreated), - }]], + messages: vec![vec![]], events: vec![Vec::new()], state_hash: creator_state.crypto_hash().await?, oracle_responses: vec![vec![ OracleResponse::Blob(contract_blob_id), OracleResponse::Blob(service_blob_id), ]], + blobs: vec![vec![Blob::new_application_description( + &application_description, + )]], } .with(create_block), )); @@ -285,7 +268,7 @@ where application_id, bytes: user_operation, }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await?; @@ -294,6 +277,7 @@ where BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: creator_state.crypto_hash().await?, oracle_responses: vec![Vec::new()], } diff --git a/linera-core/src/unit_tests/worker_tests.rs b/linera-core/src/unit_tests/worker_tests.rs index eeab1f0c2340..88e3693be723 100644 --- a/linera-core/src/unit_tests/worker_tests.rs +++ b/linera-core/src/unit_tests/worker_tests.rs @@ -327,11 +327,13 @@ where let tx_count = block.operations.len() + block.incoming_bundles.len(); let oracle_responses = iter::repeat_with(Vec::new).take(tx_count).collect(); let events = iter::repeat_with(Vec::new).take(tx_count).collect(); + let blobs = iter::repeat_with(Vec::new).take(tx_count).collect(); let state_hash = system_state.into_hash().await; let value = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages, events, + blobs, state_hash, oracle_responses, } @@ -800,6 +802,7 @@ where )], ], events: vec![Vec::new(); 2], + blobs: vec![Vec::new(); 2], state_hash: SystemExecutionState { committees: [(epoch, committee.clone())].into_iter().collect(), ownership: ChainOwnership::single(sender_key_pair.public().into()), @@ -829,6 +832,7 @@ where Amount::from_tokens(3), )]], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: SystemExecutionState { committees: [(epoch, committee.clone())].into_iter().collect(), ownership: ChainOwnership::single(sender_key_pair.public().into()), @@ -1065,6 +1069,7 @@ where vec![direct_credit_message(ChainId::root(3), Amount::ONE)], ], events: vec![Vec::new(); 2], + blobs: vec![Vec::new(); 2], state_hash: SystemExecutionState { committees: [(epoch, committee.clone())].into_iter().collect(), ownership: ChainOwnership::single(recipient_key_pair.public().into()), @@ -1359,6 +1364,7 @@ where BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: state.into_hash().await, oracle_responses: vec![Vec::new()], } @@ -2393,6 +2399,7 @@ where ), ]], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: SystemExecutionState { committees: committees.clone(), ownership: ChainOwnership::single(key_pair.public().into()), @@ -2459,6 +2466,7 @@ where vec![direct_credit_message(user_id, Amount::from_tokens(2))], ], events: vec![Vec::new(); 2], + blobs: vec![Vec::new(); 2], state_hash: SystemExecutionState { // The root chain knows both committees at the end. committees: committees2.clone(), @@ -2561,6 +2569,7 @@ where BlockExecutionOutcome { messages: vec![Vec::new(); 3], events: vec![Vec::new(); 3], + blobs: vec![Vec::new(); 3], state_hash: SystemExecutionState { subscriptions: [ChannelSubscription { chain_id: admin_id, @@ -2713,6 +2722,7 @@ where BlockExecutionOutcome { messages: vec![vec![direct_credit_message(admin_id, Amount::ONE)]], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: SystemExecutionState { committees: committees.clone(), ownership: ChainOwnership::single(owner1), @@ -2747,6 +2757,7 @@ where }, )]], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: SystemExecutionState { committees: committees2.clone(), ownership: ChainOwnership::single(owner0), @@ -2844,6 +2855,7 @@ where BlockExecutionOutcome { messages: vec![vec![direct_credit_message(admin_id, Amount::ONE)]], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: SystemExecutionState { committees: committees.clone(), ownership: ChainOwnership::single(owner1), @@ -2878,6 +2890,7 @@ where })], ], events: vec![Vec::new(); 2], + blobs: vec![Vec::new(); 2], state_hash: SystemExecutionState { committees: committees3.clone(), ownership: ChainOwnership::single(owner0), @@ -2939,6 +2952,7 @@ where BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], + blobs: vec![Vec::new()], state_hash: SystemExecutionState { committees: committees3.clone(), ownership: ChainOwnership::single(owner0), @@ -3792,7 +3806,7 @@ where let (application_id, application); { let mut chain = storage.load_chain(chain_id).await?; - (application_id, application) = chain.execution_state.register_mock_application().await?; + (application_id, application) = chain.execution_state.register_mock_application(0).await?; chain.save().await?; } @@ -3881,7 +3895,7 @@ where let (application_id, application); { let mut chain = storage.load_chain(chain_id).await?; - (application_id, application) = chain.execution_state.register_mock_application().await?; + (application_id, application) = chain.execution_state.register_mock_application(0).await?; chain.save().await?; } @@ -3968,12 +3982,13 @@ where } .into_view() .await; - let _ = state.register_mock_application().await?; + let _ = state.register_mock_application(0).await?; let value = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![], events: vec![], + blobs: vec![], state_hash: state.crypto_hash_mut().await?, oracle_responses: vec![], } diff --git a/linera-execution/src/applications.rs b/linera-execution/src/applications.rs deleted file mode 100644 index 8da936015d32..000000000000 --- a/linera-execution/src/applications.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) Zefchain Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::HashSet; - -use linera_base::{data_types::UserApplicationDescription, identifiers::UserApplicationId}; -use linera_views::{ - context::Context, - map_view::HashedMapView, - views::{ClonableView, HashableView}, -}; -#[cfg(with_testing)] -use { - linera_views::context::{create_test_memory_context, MemoryContext}, - linera_views::views::View, - std::collections::BTreeMap, -}; - -use crate::SystemExecutionError; - -#[cfg(test)] -#[path = "unit_tests/applications_tests.rs"] -mod applications_tests; - -#[derive(Debug, ClonableView, HashableView)] -pub struct ApplicationRegistryView { - /// The applications that are known by the chain. - pub known_applications: HashedMapView, -} - -#[cfg(with_testing)] -#[derive(Default, Eq, PartialEq, Debug, Clone)] -pub struct ApplicationRegistry { - pub known_applications: BTreeMap, -} - -impl ApplicationRegistryView -where - C: Context + Clone + Send + Sync + 'static, -{ - #[cfg(with_testing)] - pub fn import(&mut self, registry: ApplicationRegistry) -> Result<(), SystemExecutionError> { - for (id, description) in registry.known_applications { - self.known_applications.insert(&id, description)?; - } - Ok(()) - } - - /// Registers an existing application. - /// - /// Keeps track of an existing application that the current chain is seeing for the first time. - pub async fn register_application( - &mut self, - application: UserApplicationDescription, - ) -> Result { - // Make sure that referenced applications IDs have been registered. - for required_id in &application.required_application_ids { - self.describe_application(*required_id).await?; - } - let id = UserApplicationId::from(&application); - self.known_applications.insert(&id, application)?; - Ok(id) - } - - /// Registers a newly created application. - pub async fn register_new_application( - &mut self, - application_id: UserApplicationId, - parameters: Vec, - required_application_ids: Vec, - ) -> Result<(), SystemExecutionError> { - // Make sure that referenced applications IDs have been registered. - for required_id in &required_application_ids { - self.describe_application(*required_id).await?; - } - // Create description and register it. - let UserApplicationId { - bytecode_id, - creation, - } = application_id; - let description = UserApplicationDescription { - bytecode_id, - parameters, - creation, - required_application_ids, - }; - self.known_applications - .insert(&application_id, description)?; - Ok(()) - } - - /// Retrieves an application's description. - pub async fn describe_application( - &self, - id: UserApplicationId, - ) -> Result { - self.known_applications - .get(&id) - .await? - .ok_or_else(|| SystemExecutionError::UnknownApplicationId(Box::new(id))) - } - - /// Retrieves the recursive dependencies of applications and apply a topological sort. - pub async fn find_dependencies( - &self, - mut stack: Vec, - ) -> Result, SystemExecutionError> { - // What we return at the end. - let mut result = Vec::new(); - // The entries already inserted in `result`. - let mut sorted = HashSet::new(); - // The entries for which dependencies have already been pushed once to the stack. - let mut seen = HashSet::new(); - - while let Some(id) = stack.pop() { - if sorted.contains(&id) { - continue; - } - if seen.contains(&id) { - // Second time we see this entry. It was last pushed just before its - // dependencies -- which are now fully sorted. - sorted.insert(id); - result.push(id); - continue; - } - // First time we see this entry: - // 1. Mark it so that its dependencies are no longer pushed to the stack. - seen.insert(id); - // 2. Schedule all the (yet unseen) dependencies, then this entry for a second visit. - stack.push(id); - let app = self.describe_application(id).await?; - for child in app.required_application_ids.iter().rev() { - if !seen.contains(child) { - stack.push(*child); - } - } - } - Ok(result) - } - - /// Retrieves applications' descriptions preceded by their recursive dependencies. - pub async fn describe_applications_with_dependencies( - &self, - ids: Vec, - ) -> Result, SystemExecutionError> { - let ids_with_deps = self.find_dependencies(ids).await?; - let mut result = Vec::new(); - for id in ids_with_deps { - let description = self.describe_application(id).await?; - result.push(description); - } - Ok(result) - } -} - -#[cfg(with_testing)] -impl ApplicationRegistryView> -where - MemoryContext<()>: Context + Clone + Send + Sync + 'static, -{ - pub async fn new() -> Self { - let context = create_test_memory_context(); - Self::load(context) - .await - .expect("Loading from memory should work") - } -} diff --git a/linera-execution/src/execution.rs b/linera-execution/src/execution.rs index 44a83b6d1639..92655ce9c09a 100644 --- a/linera-execution/src/execution.rs +++ b/linera-execution/src/execution.rs @@ -1,15 +1,12 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::{ - collections::{BTreeMap, BTreeSet}, - mem, vec, -}; +use std::{mem, vec}; -use futures::{stream::FuturesOrdered, FutureExt, StreamExt, TryStreamExt}; +use futures::{FutureExt, StreamExt}; use linera_base::{ data_types::{Amount, BlockHeight, Timestamp}, - identifiers::{Account, AccountOwner, ChainId, Destination, Owner}, + identifiers::{Account, AccountOwner, BlobType, ChainId, Destination, Owner}, }; use linera_views::{ context::Context, @@ -31,8 +28,8 @@ use { use super::{runtime::ServiceRuntimeRequest, ExecutionRequest}; use crate::{ resources::ResourceController, system::SystemExecutionStateView, ContractSyncRuntime, - ExecutionError, ExecutionOutcome, ExecutionRuntimeConfig, ExecutionRuntimeContext, Message, - MessageContext, MessageKind, Operation, OperationContext, Query, QueryContext, QueryOutcome, + ExecutionError, ExecutionRuntimeConfig, ExecutionRuntimeContext, Message, MessageContext, + MessageKind, Operation, OperationContext, Query, QueryContext, QueryOutcome, RawExecutionOutcome, RawOutgoingMessage, ServiceSyncRuntime, SystemMessage, TransactionTracker, UserApplicationDescription, UserApplicationId, }; @@ -69,24 +66,20 @@ where contract_blob: Blob, service_blob: Blob, ) -> Result<(), ExecutionError> { - let chain_id = application_description.creation.chain_id; + let chain_id = application_description.creator_chain_id; let context = OperationContext { chain_id, authenticated_signer: None, authenticated_caller_id: None, - height: application_description.creation.height, + height: application_description.block_height, round: None, index: Some(0), }; let action = UserAction::Instantiate(context, instantiation_argument); - let next_message_index = application_description.creation.index + 1; + let next_message_index = application_description.application_index + 1; - let application_id = self - .system - .registry - .register_application(application_description) - .await?; + let application_id = From::from(&application_description); self.system.used_blobs.insert(&contract_blob.id())?; self.system.used_blobs.insert(&service_blob.id())?; @@ -108,7 +101,8 @@ where tracker, account: None, }; - let mut txn_tracker = TransactionTracker::new(next_message_index, None); + let mut txn_tracker = TransactionTracker::new(next_message_index, next_message_index, None); + txn_tracker.add_created_blob(Blob::new_application_description(&application_description)); self.run_user_action( application_id, chain_id, @@ -120,8 +114,7 @@ where &mut resource_controller, ) .await?; - self.update_execution_outcomes_with_app_registrations(&mut txn_tracker) - .await?; + Ok(()) } } @@ -214,8 +207,10 @@ where }; let (execution_state_sender, mut execution_state_receiver) = futures::channel::mpsc::unbounded(); + let (code, description) = self + .load_contract(application_id, txn_tracker.get_blobs_cache()) + .await?; let txn_tracker_moved = mem::take(txn_tracker); - let (code, description) = self.load_contract(application_id).await?; let contract_runtime_task = linera_base::task::Blocking::spawn(move |mut codes| { let runtime = ContractSyncRuntime::new( execution_state_sender, @@ -251,74 +246,6 @@ where Ok(()) } - /// Schedules application registration messages when needed. - /// - /// Ensures that the outgoing messages in `results` are preceded by a system message that - /// registers the application that will handle the messages. - pub async fn update_execution_outcomes_with_app_registrations( - &self, - txn_tracker: &mut TransactionTracker, - ) -> Result<(), ExecutionError> { - let results = txn_tracker.outcomes_mut(); - let user_application_outcomes = results.iter().filter_map(|outcome| match outcome { - ExecutionOutcome::User(application_id, result) => Some((application_id, result)), - _ => None, - }); - - let mut applications_to_register_per_destination = BTreeMap::<_, BTreeSet<_>>::new(); - - for (application_id, result) in user_application_outcomes { - for message in &result.messages { - applications_to_register_per_destination - .entry(&message.destination) - .or_default() - .insert(*application_id); - } - } - - if applications_to_register_per_destination.is_empty() { - return Ok(()); - } - - let messages = applications_to_register_per_destination - .into_iter() - .map(|(destination, applications_to_describe)| async { - let applications = self - .system - .registry - .describe_applications_with_dependencies( - applications_to_describe.into_iter().collect(), - ) - .await?; - - Ok::<_, ExecutionError>(RawOutgoingMessage { - destination: destination.clone(), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { applications }, - }) - }) - .collect::>() - .try_collect::>() - .await?; - - let system_outcome = RawExecutionOutcome { - messages, - ..RawExecutionOutcome::default() - }; - - // Insert the message before the first user outcome. - let index = results - .iter() - .position(|outcome| matches!(outcome, ExecutionOutcome::User(_, _))) - .unwrap_or(results.len()); - // TODO(#2362): This inserts messages in front of existing ones, invalidating their IDs. - results.insert(index, ExecutionOutcome::System(system_outcome)); - - Ok(()) - } - pub async fn execute_operation( &mut self, context: OperationContext, @@ -586,12 +513,13 @@ where &self, ) -> Result, ExecutionError> { let mut applications = vec![]; - for index in self.system.registry.known_applications.indices().await? { - let application_description = - self.system.registry.known_applications.get(&index).await?; - - if let Some(application_description) = application_description { - applications.push((index, application_description)); + for blob_id in self.system.used_blobs.indices().await? { + if blob_id.blob_type == BlobType::ApplicationDescription { + let blob_content = self.system.read_blob_content(blob_id).await?; + let application_description: UserApplicationDescription = + bcs::from_bytes(blob_content.bytes())?; + let app_id = UserApplicationId::from(&application_description); + applications.push((app_id, application_description)); } } Ok(applications) diff --git a/linera-execution/src/execution_state_actor.rs b/linera-execution/src/execution_state_actor.rs index dd511e6cc3f2..6eb20bd18ac9 100644 --- a/linera-execution/src/execution_state_actor.rs +++ b/linera-execution/src/execution_state_actor.rs @@ -3,6 +3,7 @@ //! Handle requests from the synchronous execution thread of user applications. +use std::collections::BTreeMap; #[cfg(with_metrics)] use std::sync::LazyLock; @@ -11,9 +12,9 @@ use futures::channel::mpsc; #[cfg(with_metrics)] use linera_base::prometheus_util::{bucket_latencies, register_histogram_vec, MeasureLatency as _}; use linera_base::{ - data_types::{Amount, ApplicationPermissions, BlobContent, Timestamp}, + data_types::{Amount, ApplicationPermissions, Blob, BlobContent, BlockHeight, Timestamp}, hex_debug, hex_vec_debug, http, - identifiers::{Account, AccountOwner, BlobId, MessageId, Owner}, + identifiers::{Account, AccountOwner, BlobId, BlobType, ChainId, MessageId, Owner}, ownership::ChainOwnership, }; use linera_views::{batch::Batch, context::Context, views::View}; @@ -62,10 +63,21 @@ where pub(crate) async fn load_contract( &mut self, id: UserApplicationId, + blobs_cache: &BTreeMap, ) -> Result<(UserContractCode, UserApplicationDescription), ExecutionError> { #[cfg(with_metrics)] let _latency = LOAD_CONTRACT_LATENCY.measure_latency(); - let description = self.system.registry.describe_application(id).await?; + let blob_id = BlobId::new( + id.application_description_hash, + BlobType::ApplicationDescription, + ); + let description = match blobs_cache.get(&blob_id) { + Some(description) => { + let blob = description.clone(); + bcs::from_bytes(blob.bytes())? + } + None => self.system.describe_application(id).await?, + }; let code = self .context() .extra() @@ -80,7 +92,7 @@ where ) -> Result<(UserServiceCode, UserApplicationDescription), ExecutionError> { #[cfg(with_metrics)] let _latency = LOAD_SERVICE_LATENCY.measure_latency(); - let description = self.system.registry.describe_application(id).await?; + let description = self.system.describe_application(id).await?; let code = self .context() .extra() @@ -97,7 +109,11 @@ where use ExecutionRequest::*; match request { #[cfg(not(web))] - LoadContract { id, callback } => callback.respond(self.load_contract(id).await?), + LoadContract { + id, + blobs_cache, + callback, + } => callback.respond(self.load_contract(id, &blobs_cache).await?), #[cfg(not(web))] LoadService { id, callback } => callback.respond(self.load_service(id).await?), @@ -306,7 +322,9 @@ where } CreateApplication { - next_message_id, + chain_id, + block_height, + application_index, bytecode_id, parameters, required_application_ids, @@ -315,7 +333,9 @@ where let create_application_result = self .system .create_application( - next_message_id, + chain_id, + block_height, + application_index, bytecode_id, parameters, required_application_ids, @@ -367,6 +387,7 @@ pub enum ExecutionRequest { #[cfg(not(web))] LoadContract { id: UserApplicationId, + blobs_cache: BTreeMap, #[debug(skip)] callback: Sender<(UserContractCode, UserApplicationDescription)>, }, @@ -509,7 +530,9 @@ pub enum ExecutionRequest { }, CreateApplication { - next_message_id: MessageId, + chain_id: ChainId, + block_height: BlockHeight, + application_index: u32, bytecode_id: BytecodeId, parameters: Vec, required_application_ids: Vec, diff --git a/linera-execution/src/lib.rs b/linera-execution/src/lib.rs index c688ab1e8988..f1ce970ce451 100644 --- a/linera-execution/src/lib.rs +++ b/linera-execution/src/lib.rs @@ -7,7 +7,6 @@ #![cfg_attr(web, feature(trait_upcasting))] #![deny(clippy::large_futures)] -mod applications; pub mod committee; mod execution; mod execution_state_actor; @@ -54,8 +53,6 @@ use serde::{Deserialize, Serialize}; use system::OpenChainConfig; use thiserror::Error; -#[cfg(with_testing)] -pub use crate::applications::ApplicationRegistry; #[cfg(with_revm)] use crate::revm::EvmExecutionError; use crate::runtime::ContractSyncRuntime; @@ -67,7 +64,6 @@ pub use crate::wasm::{ ViewSystemApi, WasmContractModule, WasmExecutionError, WasmServiceModule, }; pub use crate::{ - applications::ApplicationRegistryView, execution::{ExecutionStateView, ServiceRuntimeEndpoint}, execution_state_actor::ExecutionRequest, policy::ResourceControlPolicy, diff --git a/linera-execution/src/policy.rs b/linera-execution/src/policy.rs index 6c67b4468e81..f6d4f04a7be2 100644 --- a/linera-execution/src/policy.rs +++ b/linera-execution/src/policy.rs @@ -212,7 +212,7 @@ impl ResourceControlPolicy { ExecutionError::BytecodeTooLarge ); } - BlobType::Data => {} + BlobType::Data | BlobType::ApplicationDescription => {} } Ok(()) } diff --git a/linera-execution/src/runtime.rs b/linera-execution/src/runtime.rs index 01e87c751d3f..d2c3cb55cd65 100644 --- a/linera-execution/src/runtime.rs +++ b/linera-execution/src/runtime.rs @@ -12,8 +12,8 @@ use custom_debug_derive::Debug; use linera_base::{ crypto::CryptoHash, data_types::{ - Amount, ApplicationPermissions, ArithmeticError, BlockHeight, OracleResponse, Resources, - SendMessageRequest, Timestamp, + Amount, ApplicationPermissions, ArithmeticError, Blob, BlockHeight, OracleResponse, + Resources, SendMessageRequest, Timestamp, }, ensure, http, identifiers::{ @@ -115,8 +115,8 @@ struct ApplicationStatus { caller_id: Option, /// The application ID. id: UserApplicationId, - /// The parameters from the application description. - parameters: Vec, + /// The application description. + description: UserApplicationDescription, /// The authenticated signer for the execution thread, if any. signer: Option, /// The current execution outcome of the application. @@ -127,7 +127,7 @@ struct ApplicationStatus { #[derive(Debug)] struct LoadedApplication { instance: Arc>, - parameters: Vec, + description: UserApplicationDescription, } impl LoadedApplication { @@ -135,7 +135,7 @@ impl LoadedApplication { fn new(instance: Instance, description: UserApplicationDescription) -> Self { LoadedApplication { instance: Arc::new(Mutex::new(instance)), - parameters: description.parameters, + description, } } } @@ -146,7 +146,7 @@ impl Clone for LoadedApplication { fn clone(&self) -> Self { LoadedApplication { instance: self.instance.clone(), - parameters: self.parameters.clone(), + description: self.description.clone(), } } } @@ -401,9 +401,14 @@ impl SyncRuntimeInternal { } #[cfg(not(web))] hash_map::Entry::Vacant(entry) => { + let blobs_cache = self.transaction_tracker.get_blobs_cache().clone(); let (code, description) = self .execution_state_sender - .send_request(|callback| ExecutionRequest::LoadContract { id, callback })? + .send_request(|callback| ExecutionRequest::LoadContract { + id, + blobs_cache, + callback, + })? .recv_response()?; let instance = code.instantiate(this)?; @@ -457,7 +462,7 @@ impl SyncRuntimeInternal { self.push_application(ApplicationStatus { caller_id: authenticated_caller_id, id: callee_id, - parameters: application.parameters, + description: application.description, // Allow further nested calls to be authenticated if this one is. signer: authenticated_signer, outcome: RawExecutionOutcome::default(), @@ -739,11 +744,11 @@ impl BaseRuntime for SyncRuntimeInternal { } fn application_creator_chain_id(&mut self) -> Result { - Ok(self.current_application().id.creation.chain_id) + Ok(self.current_application().description.creator_chain_id) } fn application_parameters(&mut self) -> Result, ExecutionError> { - Ok(self.current_application().parameters.clone()) + Ok(self.current_application().description.parameters.clone()) } fn read_system_timestamp(&mut self) -> Result { @@ -1154,7 +1159,7 @@ impl ContractSyncRuntimeHandle { let status = ApplicationStatus { caller_id: None, id: application_id, - parameters: application.parameters.clone(), + description: application.description.clone(), signer, outcome: RawExecutionOutcome::default(), }; @@ -1175,7 +1180,7 @@ impl ContractSyncRuntimeHandle { let application_status = runtime.pop_application(); assert_eq!(application_status.caller_id, None); assert_eq!(application_status.id, application_id); - assert_eq!(application_status.parameters, contract.parameters); + assert_eq!(application_status.description, contract.description); assert_eq!(application_status.signer, signer); assert!(runtime.call_stack.is_empty()); @@ -1449,24 +1454,20 @@ impl ContractRuntime for ContractSyncRuntimeHandle { required_application_ids: Vec, ) -> Result { let chain_id = self.inner().chain_id; - let height = self.block_height()?; - let index = self.inner().transaction_tracker.next_message_index(); - - let message_id = MessageId { - chain_id, - height, - index, - }; + let block_height = self.block_height()?; + let application_index = self.inner().transaction_tracker.next_application_index(); let CreateApplicationResult { app_id, - message, blobs_to_register, + application_description, } = self .inner() .execution_state_sender .send_request(|callback| ExecutionRequest::CreateApplication { - next_message_id: message_id, + chain_id, + block_height, + application_index, bytecode_id, parameters, required_application_ids, @@ -1478,10 +1479,10 @@ impl ContractRuntime for ContractSyncRuntimeHandle { .transaction_tracker .replay_oracle_response(OracleResponse::Blob(blob_id))?; } - let outcome = RawExecutionOutcome::default().with_message(message); + self.inner() .transaction_tracker - .add_system_outcome(outcome)?; + .add_created_blob(Blob::new_application_description(&application_description)); let (contract, context) = self.inner().prepare_for_call(self.clone(), true, app_id)?; @@ -1662,7 +1663,7 @@ impl ServiceRuntime for ServiceSyncRuntimeHandle { this.push_application(ApplicationStatus { caller_id: None, id: queried_id, - parameters: application.parameters, + description: application.description, signer: None, outcome: RawExecutionOutcome::default(), }); diff --git a/linera-execution/src/system.rs b/linera-execution/src/system.rs index b9bd65a2761d..0ede8d5447db 100644 --- a/linera-execution/src/system.rs +++ b/linera-execution/src/system.rs @@ -9,7 +9,7 @@ mod tests; #[cfg(with_metrics)] use std::sync::LazyLock; use std::{ - collections::BTreeMap, + collections::{BTreeMap, HashMap, HashSet}, fmt::{self, Display, Formatter}, iter, }; @@ -19,7 +19,8 @@ use custom_debug_derive::Debug; use linera_base::{ crypto::CryptoHash, data_types::{ - Amount, ApplicationPermissions, ArithmeticError, BlobContent, OracleResponse, Timestamp, + Amount, ApplicationPermissions, ArithmeticError, Blob, BlobContent, BlockHeight, + OracleResponse, Timestamp, }, ensure, hex_debug, identifiers::{ @@ -44,10 +45,9 @@ use {linera_base::prometheus_util::register_int_counter_vec, prometheus::IntCoun use crate::test_utils::SystemExecutionState; use crate::{ committee::{Committee, Epoch}, - ApplicationRegistryView, ChannelName, ChannelSubscription, Destination, - ExecutionRuntimeContext, MessageContext, MessageKind, OperationContext, QueryContext, - QueryOutcome, RawExecutionOutcome, RawOutgoingMessage, TransactionTracker, - UserApplicationDescription, UserApplicationId, + ChannelName, ChannelSubscription, Destination, ExecutionRuntimeContext, MessageContext, + MessageKind, OperationContext, QueryContext, QueryOutcome, RawExecutionOutcome, + RawOutgoingMessage, TransactionTracker, UserApplicationDescription, UserApplicationId, }; /// The relative index of the `OpenChain` message created by the `OpenChain` operation. @@ -90,8 +90,6 @@ pub struct SystemExecutionStateView { pub balances: HashedMapView, /// The timestamp of the most recent block. pub timestamp: HashedRegisterView, - /// Track the locations of known bytecodes as well as the descriptions of known applications. - pub registry: ApplicationRegistryView, /// Whether this chain has been closed. pub closed: HashedRegisterView, /// Permissions for applications on this chain. @@ -184,11 +182,6 @@ pub enum SystemOperation { #[debug(skip_if = Vec::is_empty)] required_application_ids: Vec, }, - /// Requests a message from another chain to register a user application on this chain. - RequestApplication { - chain_id: ChainId, - application_id: UserApplicationId, - }, /// Operations that are only allowed on the admin chain. Admin(AdminOperation), } @@ -249,9 +242,6 @@ pub enum SystemMessage { RegisterApplications { applications: Vec, }, - /// Requests a `RegisterApplication` message from the target chain to register the specified - /// application on the sender chain. - RequestApplication(UserApplicationId), } /// A query to the system state. @@ -355,8 +345,8 @@ impl UserData { #[derive(Clone, Debug)] pub struct CreateApplicationResult { pub app_id: UserApplicationId, - pub message: RawOutgoingMessage, pub blobs_to_register: Vec, + pub application_description: UserApplicationDescription, } #[derive(Error, Debug)] @@ -414,6 +404,8 @@ pub enum SystemExecutionError { TicksOutOfOrder, #[error("Application {0:?} is not registered by the chain")] UnknownApplicationId(Box), + #[error("Couldn't deserialize blob content: {0:?}")] + BlobDeserializationError(#[from] bcs::Error), #[error("Chain is not active yet.")] InactiveChain, @@ -423,6 +415,8 @@ pub enum SystemExecutionError { OracleResponseMismatch, #[error("No recorded response for oracle query")] MissingOracleResponse, + #[error("Invalid index for operation: {0:?}")] + InvalidOperationIndex(Option), } impl From for SystemExecutionError { @@ -646,14 +640,16 @@ where instantiation_argument, required_application_ids, } => { - let next_message_id = context.next_message_id(txn_tracker.next_message_index()); + let application_index = txn_tracker.next_application_index(); let CreateApplicationResult { app_id, - message, blobs_to_register, + application_description, } = self .create_application( - next_message_id, + context.chain_id, + context.height, + application_index, bytecode_id, parameters, required_application_ids, @@ -661,22 +657,10 @@ where .await?; self.record_bytecode_blobs(blobs_to_register, txn_tracker) .await?; - outcome.messages.push(message); + txn_tracker + .add_created_blob(Blob::new_application_description(&application_description)); new_application = Some((app_id, instantiation_argument)); } - RequestApplication { - chain_id, - application_id, - } => { - let message = RawOutgoingMessage { - destination: Destination::Recipient(chain_id), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RequestApplication(application_id), - }; - outcome.messages.push(message); - } PublishDataBlob { blob_hash } => { self.blob_published(&BlobId::new(blob_hash, BlobType::Data))?; } @@ -877,23 +861,10 @@ where for application in applications { self.check_and_record_bytecode_blobs(&application.bytecode_id, txn_tracker) .await?; - self.registry.register_application(application).await?; + self.check_required_applications(&application).await?; + txn_tracker.add_created_blob(Blob::new_application_description(&application)); } } - RequestApplication(application_id) => { - let applications = self - .registry - .describe_applications_with_dependencies(vec![application_id]) - .await?; - let message = RawOutgoingMessage { - destination: Destination::Recipient(context.message_id.chain_id), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { applications }, - }; - outcome.messages.push(message); - } // These messages are executed immediately when cross-chain requests are received. Subscribe { .. } | Unsubscribe { .. } | OpenChain(_) => {} // This message is only a placeholder: Its ID is part of the application ID. @@ -1028,19 +999,21 @@ where pub async fn create_application( &mut self, - next_message_id: MessageId, + chain_id: ChainId, + block_height: BlockHeight, + application_index: u32, bytecode_id: BytecodeId, parameters: Vec, required_application_ids: Vec, ) -> Result { - let id = UserApplicationId { - bytecode_id, - creation: next_message_id, - }; let mut blobs_to_register = vec![]; - for application in required_application_ids.iter().chain(iter::once(&id)) { + for application_bytecode in required_application_ids + .iter() + .map(|app_id| &app_id.bytecode_id) + .chain(iter::once(&bytecode_id)) + { let (contract_bytecode_blob_id, service_bytecode_blob_id) = - self.check_bytecode_blobs(&application.bytecode_id).await?; + self.check_bytecode_blobs(application_bytecode).await?; // We only remember to register the blobs that aren't recorded in `used_blobs` // already. if !self.used_blobs.contains(&contract_bytecode_blob_id).await? { @@ -1050,25 +1023,107 @@ where blobs_to_register.push(service_bytecode_blob_id); } } - self.registry - .register_new_application(id, parameters, required_application_ids) - .await?; - // Send a message to ourself to increment the message ID. - let message = RawOutgoingMessage { - destination: Destination::Recipient(next_message_id.chain_id), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Protected, - message: SystemMessage::ApplicationCreated, + + let application_description = UserApplicationDescription { + bytecode_id, + creator_chain_id: chain_id, + block_height, + application_index, + parameters, + required_application_ids, }; + self.check_required_applications(&application_description) + .await?; Ok(CreateApplicationResult { - app_id: id, - message, + app_id: UserApplicationId::from(&application_description), blobs_to_register, + application_description, }) } + async fn check_required_applications( + &mut self, + application_description: &UserApplicationDescription, + ) -> Result<(), SystemExecutionError> { + // Make sure that referenced applications ids have been registered. + for required_id in &application_description.required_application_ids { + self.describe_application(*required_id).await?; + } + Ok(()) + } + + /// Retrieves an application's description. + pub async fn describe_application( + &mut self, + id: UserApplicationId, + ) -> Result { + let blob_content = self + .read_blob_content(BlobId::new( + id.application_description_hash, + BlobType::ApplicationDescription, + )) + .await?; + Ok(bcs::from_bytes(blob_content.bytes())?) + } + + /// Retrieves the recursive dependencies of applications and apply a topological sort. + pub async fn find_dependencies( + &mut self, + mut stack: Vec, + ) -> Result, SystemExecutionError> { + // What we return at the end. + let mut result = Vec::new(); + // The entries already inserted in `result`. + let mut sorted = HashSet::new(); + // The entries for which dependencies have already been pushed once to the stack. + let mut seen = HashSet::new(); + + while let Some(id) = stack.pop() { + if sorted.contains(&id) { + continue; + } + if seen.contains(&id) { + // Second time we see this entry. It was last pushed just before its + // dependencies -- which are now fully sorted. + sorted.insert(id); + result.push(id); + continue; + } + // First time we see this entry: + // 1. Mark it so that its dependencies are no longer pushed to the stack. + seen.insert(id); + // 2. Schedule all the (yet unseen) dependencies, then this entry for a second visit. + stack.push(id); + let app = self.describe_application(id).await?; + for child in app.required_application_ids.iter().rev() { + if !seen.contains(child) { + stack.push(*child); + } + } + } + Ok(result) + } + + /// Retrieves applications' descriptions preceded by their recursive dependencies. + pub async fn describe_applications_with_dependencies( + &mut self, + ids: Vec, + extra_registered_apps: &HashMap, + ) -> Result, SystemExecutionError> { + let ids_with_deps = self.find_dependencies(ids).await?; + let mut result = Vec::new(); + for id in ids_with_deps { + let description = if let Some(description) = extra_registered_apps.get(&id) { + description.clone() + } else { + self.describe_application(id).await? + }; + result.push(description); + } + Ok(result) + } + /// Records a blob that is used in this block. If this is the first use on this chain, creates /// an oracle response for it. pub(crate) async fn blob_used( @@ -1094,7 +1149,7 @@ where } pub async fn read_blob_content( - &mut self, + &self, blob_id: BlobId, ) -> Result { match self.context().extra().get_blob(blob_id).await { diff --git a/linera-execution/src/test_utils/mod.rs b/linera-execution/src/test_utils/mod.rs index 9a48e5a1b936..212f94985955 100644 --- a/linera-execution/src/test_utils/mod.rs +++ b/linera-execution/src/test_utils/mod.rs @@ -29,15 +29,15 @@ pub use self::{ system_execution_state::SystemExecutionState, }; use crate::{ - ApplicationRegistryView, ExecutionRequest, ExecutionRuntimeContext, ExecutionStateView, - MessageContext, OperationContext, QueryContext, ServiceRuntimeEndpoint, ServiceRuntimeRequest, + ExecutionRequest, ExecutionRuntimeContext, ExecutionStateView, MessageContext, + OperationContext, QueryContext, ServiceRuntimeEndpoint, ServiceRuntimeRequest, ServiceSyncRuntime, SystemExecutionStateView, TestExecutionRuntimeContext, UserApplicationDescription, UserApplicationId, }; /// Creates a dummy [`UserApplicationDescription`] for use in tests. pub fn create_dummy_user_application_description( - index: u64, + index: u32, ) -> (UserApplicationDescription, Blob, Blob) { let chain_id = ChainId::root(1); let contract_blob = Blob::new_contract_bytecode(CompressedBytecode { @@ -50,11 +50,9 @@ pub fn create_dummy_user_application_description( ( UserApplicationDescription { bytecode_id: BytecodeId::new(contract_blob.id().hash, service_blob.id().hash), - creation: MessageId { - chain_id, - height: BlockHeight(index), - index: 1, - }, + creator_chain_id: chain_id, + block_height: 0.into(), + application_index: index, required_application_ids: vec![], parameters: vec![], }, @@ -110,19 +108,13 @@ pub trait RegisterMockApplication { /// This is included in the mocked [`ApplicationId`]. fn creator_chain_id(&self) -> ChainId; - /// Returns the amount of known registered applications. - /// - /// Used to avoid duplicate registrations. - async fn registered_application_count(&self) -> anyhow::Result; - /// Registers a new [`MockApplication`] and returns it with the [`UserApplicationId`] that was /// used for it. async fn register_mock_application( &mut self, + index: u32, ) -> anyhow::Result<(UserApplicationId, MockApplication)> { - let (description, contract, service) = create_dummy_user_application_description( - self.registered_application_count().await? as u64, - ); + let (description, contract, service) = create_dummy_user_application_description(index); self.register_mock_application_with(description, contract, service) .await @@ -147,10 +139,6 @@ where self.system.creator_chain_id() } - async fn registered_application_count(&self) -> anyhow::Result { - self.system.registered_application_count().await - } - async fn register_mock_application_with( &mut self, description: UserApplicationDescription, @@ -174,17 +162,13 @@ where ).into() } - async fn registered_application_count(&self) -> anyhow::Result { - Ok(self.registry.known_applications.count().await?) - } - async fn register_mock_application_with( &mut self, description: UserApplicationDescription, contract: Blob, service: Blob, ) -> anyhow::Result<(UserApplicationId, MockApplication)> { - let id = self.registry.register_application(description).await?; + let id = From::from(&description); let extra = self.context().extra(); let mock_application = MockApplication::default(); @@ -194,27 +178,27 @@ where extra .user_services() .insert(id, mock_application.clone().into()); - extra.add_blobs([contract, service]).await?; + extra + .add_blobs([ + contract, + service, + Blob::new_application_description(&description), + ]) + .await?; Ok((id, mock_application)) } } -pub async fn create_dummy_user_application_registrations( - registry: &mut ApplicationRegistryView, - count: u64, -) -> anyhow::Result> -where - C: Context + Clone + Send + Sync + 'static, -{ +pub async fn create_dummy_user_application_registrations( + count: u32, +) -> anyhow::Result> { let mut ids = Vec::with_capacity(count as usize); for index in 0..count { let (description, contract_blob, service_blob) = create_dummy_user_application_description(index); - let id = registry.register_application(description.clone()).await?; - - assert_eq!(registry.describe_application(id).await?, description); + let id = From::from(&description); ids.push((id, description, contract_blob, service_blob)); } diff --git a/linera-execution/src/test_utils/system_execution_state.rs b/linera-execution/src/test_utils/system_execution_state.rs index 48b67d435803..a0bd404fcb04 100644 --- a/linera-execution/src/test_utils/system_execution_state.rs +++ b/linera-execution/src/test_utils/system_execution_state.rs @@ -23,7 +23,6 @@ use linera_views::{ use super::{MockApplication, RegisterMockApplication}; use crate::{ - applications::ApplicationRegistry, committee::{Committee, Epoch}, execution::UserAction, system::SystemChannel, @@ -45,7 +44,6 @@ pub struct SystemExecutionState { #[debug(skip_if = BTreeMap::is_empty)] pub balances: BTreeMap, pub timestamp: Timestamp, - pub registry: ApplicationRegistry, pub used_blobs: BTreeSet, #[debug(skip_if = Not::not)] pub closed: bool, @@ -108,7 +106,6 @@ impl SystemExecutionState { balance, balances, timestamp, - registry, used_blobs, closed, application_permissions, @@ -158,10 +155,6 @@ impl SystemExecutionState { .expect("insertion of balances should not fail"); } view.system.timestamp.set(timestamp); - view.system - .registry - .import(registry) - .expect("serialization of registry components should not fail"); for blob_id in used_blobs { view.system .used_blobs @@ -183,10 +176,6 @@ impl RegisterMockApplication for SystemExecutionState { ).into() } - async fn registered_application_count(&self) -> anyhow::Result { - Ok(self.registry.known_applications.len()) - } - async fn register_mock_application_with( &mut self, description: UserApplicationDescription, @@ -196,8 +185,11 @@ impl RegisterMockApplication for SystemExecutionState { let id = ApplicationId::from(&description); let application = MockApplication::default(); - self.registry.known_applications.insert(id, description); - self.extra_blobs.extend([contract, service]); + self.extra_blobs.extend([ + contract, + service, + Blob::new_application_description(&description), + ]); self.mock_applications.insert(id, application.clone()); Ok((id, application)) diff --git a/linera-execution/src/transaction_tracker.rs b/linera-execution/src/transaction_tracker.rs index 3833e2b39e34..13151e08108a 100644 --- a/linera-execution/src/transaction_tracker.rs +++ b/linera-execution/src/transaction_tracker.rs @@ -1,13 +1,13 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::vec; +use std::{collections::BTreeMap, vec}; use custom_debug_derive::Debug; use linera_base::{ - data_types::{Amount, ArithmeticError, Event, OracleResponse}, + data_types::{Amount, ArithmeticError, Blob, Event, OracleResponse}, ensure, - identifiers::{ApplicationId, ChainId, ChannelFullName, StreamId}, + identifiers::{ApplicationId, BlobId, ChainId, ChannelFullName, StreamId}, }; use crate::{ @@ -25,8 +25,11 @@ pub struct TransactionTracker { #[debug(skip_if = Vec::is_empty)] outcomes: Vec, next_message_index: u32, + next_application_index: u32, /// Events recorded by contracts' `emit` calls. events: Vec, + /// Blobs created by contracts. + blobs: BTreeMap, /// Subscribe chains to channels. subscribe: Vec<(ChannelFullName, ChainId)>, /// Unsubscribe chains from channels. @@ -41,8 +44,11 @@ pub struct TransactionOutcome { #[debug(skip_if = Vec::is_empty)] pub outcomes: Vec, pub next_message_index: u32, + pub next_application_index: u32, /// Events recorded by contracts' `emit` calls. pub events: Vec, + /// Blobs created by contracts. + pub blobs: Vec, /// Subscribe chains to channels. pub subscribe: Vec<(ChannelFullName, ChainId)>, /// Unsubscribe chains from channels. @@ -50,10 +56,15 @@ pub struct TransactionOutcome { } impl TransactionTracker { - pub fn new(next_message_index: u32, oracle_responses: Option>) -> Self { + pub fn new( + next_message_index: u32, + next_application_index: u32, + oracle_responses: Option>, + ) -> Self { TransactionTracker { replaying_oracle_responses: oracle_responses.map(Vec::into_iter), next_message_index, + next_application_index, ..Self::default() } } @@ -62,6 +73,12 @@ impl TransactionTracker { self.next_message_index } + pub fn next_application_index(&mut self) -> u32 { + let index = self.next_application_index; + self.next_application_index += 1; + index + } + pub fn add_system_outcome( &mut self, outcome: RawExecutionOutcome, @@ -106,6 +123,14 @@ impl TransactionTracker { }); } + pub fn add_created_blob(&mut self, blob: Blob) { + self.blobs.insert(blob.id(), blob); + } + + pub fn get_blobs_cache(&self) -> &BTreeMap { + &self.blobs + } + pub fn subscribe(&mut self, name: ChannelFullName, subscriber: ChainId) { self.subscribe.push((name, subscriber)); } @@ -155,7 +180,9 @@ impl TransactionTracker { oracle_responses, outcomes, next_message_index, + next_application_index, events, + blobs, subscribe, unsubscribe, } = self; @@ -169,13 +196,11 @@ impl TransactionTracker { outcomes, oracle_responses, next_message_index, + next_application_index, events, + blobs: blobs.into_values().collect(), subscribe, unsubscribe, }) } - - pub(crate) fn outcomes_mut(&mut self) -> &mut Vec { - &mut self.outcomes - } } diff --git a/linera-execution/src/unit_tests/runtime_tests.rs b/linera-execution/src/unit_tests/runtime_tests.rs index a675e105bfd1..946e4c908296 100644 --- a/linera-execution/src/unit_tests/runtime_tests.rs +++ b/linera-execution/src/unit_tests/runtime_tests.rs @@ -14,7 +14,7 @@ use futures::{channel::mpsc, StreamExt}; use linera_base::{ crypto::CryptoHash, data_types::{BlockHeight, Timestamp}, - identifiers::{ApplicationId, BytecodeId, ChainDescription, MessageId}, + identifiers::{ApplicationId, BytecodeId, ChainDescription}, }; use linera_views::batch::Batch; @@ -22,6 +22,7 @@ use super::{ApplicationStatus, SyncRuntimeHandle, SyncRuntimeInternal}; use crate::{ execution_state_actor::ExecutionRequest, runtime::{LoadedApplication, ResourceController, SyncRuntime}, + test_utils::create_dummy_user_application_description, ContractRuntime, RawExecutionOutcome, TransactionTracker, UserContractInstance, }; @@ -185,7 +186,7 @@ fn create_runtime() -> ( execution_state_sender, None, resource_controller, - TransactionTracker::new(0, Some(Vec::new())), + TransactionTracker::new(0, 0, Some(Vec::new())), ); (runtime, execution_state_receiver) @@ -193,10 +194,12 @@ fn create_runtime() -> ( /// Creates an [`ApplicationStatus`] for a dummy application. fn create_dummy_application() -> ApplicationStatus { + let (description, _, _) = create_dummy_user_application_description(0); + let id = From::from(&description); ApplicationStatus { caller_id: None, - id: create_dummy_application_id(), - parameters: vec![], + id, + description, signer: None, outcome: RawExecutionOutcome::default(), } @@ -204,18 +207,12 @@ fn create_dummy_application() -> ApplicationStatus { /// Creates a dummy [`ApplicationId`]. fn create_dummy_application_id() -> ApplicationId { - let chain_id = ChainDescription::Root(1).into(); - ApplicationId { bytecode_id: BytecodeId::new( CryptoHash::test_hash("contract"), CryptoHash::test_hash("service"), ), - creation: MessageId { - chain_id, - height: BlockHeight(1), - index: 1, - }, + application_description_hash: CryptoHash::test_hash("application description"), } } @@ -224,9 +221,10 @@ fn create_fake_application_with_runtime( runtime: &SyncRuntimeHandle>, ) -> LoadedApplication> { let fake_instance: Arc = runtime.0.clone(); + let (description, _, _) = create_dummy_user_application_description(0); LoadedApplication { instance: Arc::new(Mutex::new(fake_instance)), - parameters: vec![], + description, } } diff --git a/linera-execution/src/unit_tests/system_tests.rs b/linera-execution/src/unit_tests/system_tests.rs index a7c4304e5908..e23509cfcaf5 100644 --- a/linera-execution/src/unit_tests/system_tests.rs +++ b/linera-execution/src/unit_tests/system_tests.rs @@ -1,10 +1,7 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use linera_base::{ - data_types::{Blob, BlockHeight, Bytecode}, - identifiers::ApplicationId, -}; +use linera_base::data_types::{Blob, BlockHeight, Bytecode}; use linera_views::context::MemoryContext; use super::*; @@ -36,6 +33,24 @@ async fn new_view_and_context() -> ( (view, context) } +fn expected_application_id( + context: &OperationContext, + bytecode_id: &BytecodeId, + parameters: Vec, + required_application_ids: Vec, + application_index: u32, +) -> UserApplicationId { + let description = UserApplicationDescription { + bytecode_id: *bytecode_id, + creator_chain_id: context.chain_id, + block_height: context.height, + application_index, + parameters, + required_application_ids, + }; + From::from(&description) +} + #[tokio::test] async fn application_message_index() -> anyhow::Result<()> { let (mut view, context) = new_view_and_context().await; @@ -60,23 +75,10 @@ async fn application_message_index() -> anyhow::Result<()> { .system .execute_operation(context, operation, &mut txn_tracker) .await?; - let [ExecutionOutcome::System(result)] = &txn_tracker.into_outcome().unwrap().outcomes[..] - else { + let [ExecutionOutcome::System(_)] = &txn_tracker.into_outcome().unwrap().outcomes[..] else { panic!("Unexpected outcome"); }; - assert_eq!( - result.messages[CREATE_APPLICATION_MESSAGE_INDEX as usize].message, - SystemMessage::ApplicationCreated - ); - let creation = MessageId { - chain_id: context.chain_id, - height: context.height, - index: CREATE_APPLICATION_MESSAGE_INDEX, - }; - let id = ApplicationId { - bytecode_id, - creation, - }; + let id = expected_application_id(&context, &bytecode_id, vec![], vec![], 0); assert_eq!(new_application, Some((id, vec![]))); Ok(()) diff --git a/linera-execution/tests/contract_runtime_apis.rs b/linera-execution/tests/contract_runtime_apis.rs index 47f206f700c7..c28bba32fb2b 100644 --- a/linera-execution/tests/contract_runtime_apis.rs +++ b/linera-execution/tests/contract_runtime_apis.rs @@ -16,8 +16,7 @@ use linera_base::{ Amount, Blob, BlockHeight, CompressedBytecode, Timestamp, UserApplicationDescription, }, identifiers::{ - Account, AccountOwner, ApplicationId, BytecodeId, ChainDescription, ChainId, MessageId, - Owner, + Account, AccountOwner, ApplicationId, BytecodeId, ChainDescription, ChainId, Owner, }, ownership::ChainOwnership, }; @@ -80,7 +79,7 @@ async fn test_transfer_system_api( application_id, bytes: vec![], }; - let mut tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -111,7 +110,7 @@ async fn test_transfer_system_api( Timestamp::from(0), Message::System(outcome.messages[0].message.clone()), None, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await?; @@ -172,7 +171,7 @@ async fn test_unauthorized_transfer_system_api( context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -253,7 +252,7 @@ async fn test_claim_system_api( application_id, bytes: vec![], }; - let mut tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut tracker = TransactionTracker::new(0, 0, Some(Vec::new())); claimer_view .execute_operation( context, @@ -280,7 +279,7 @@ async fn test_claim_system_api( assert_eq!(outcome.messages.len(), 1); - let mut tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut tracker = TransactionTracker::new(0, 0, Some(Vec::new())); source_view .execute_message( create_dummy_message_context(None), @@ -320,7 +319,7 @@ async fn test_claim_system_api( assert_eq!(outcome.messages.len(), 1); - let mut tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut tracker = TransactionTracker::new(0, 0, Some(Vec::new())); let context = MessageContext { chain_id: claimer_chain_id, ..create_dummy_message_context(None) @@ -403,7 +402,7 @@ async fn test_unauthorized_claims( application_id, bytes: vec![], }; - let mut tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut tracker = TransactionTracker::new(0, 0, Some(Vec::new())); let result = claimer_view .execute_operation( context, @@ -435,7 +434,7 @@ async fn test_read_chain_balance_system_api(chain_balance: Amount) { .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -456,7 +455,7 @@ async fn test_read_chain_balance_system_api(chain_balance: Amount) { context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await @@ -476,7 +475,7 @@ async fn test_read_owner_balance_system_api( .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -499,7 +498,7 @@ async fn test_read_owner_balance_system_api( context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await @@ -516,7 +515,7 @@ async fn test_read_owner_balance_returns_zero_for_missing_accounts(missing_accou .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -540,7 +539,7 @@ async fn test_read_owner_balance_returns_zero_for_missing_accounts(missing_accou context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await @@ -560,7 +559,7 @@ async fn test_read_owner_balances_system_api( .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -584,7 +583,7 @@ async fn test_read_owner_balances_system_api( context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await @@ -604,7 +603,7 @@ async fn test_read_balance_owners_system_api( .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -628,7 +627,7 @@ async fn test_read_balance_owners_system_api( context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await @@ -661,11 +660,9 @@ impl TransferTestEndpoint { UserApplicationDescription { bytecode_id: BytecodeId::new(contract_id, service_id), - creation: MessageId { - chain_id: ChainId::root(1000), - height: BlockHeight(0), - index: 0, - }, + creator_chain_id: ChainId::root(1000), + block_height: BlockHeight(0), + application_index: 0, parameters: vec![], required_application_ids: vec![], } @@ -699,11 +696,9 @@ impl TransferTestEndpoint { CryptoHash::test_hash("recipient contract bytecode"), CryptoHash::test_hash("recipient service bytecode"), ), - creation: MessageId { - chain_id: ChainId::root(2000), - height: BlockHeight(0), - index: 0, - }, + application_description_hash: CryptoHash::test_hash( + "recipient application description", + ), } } diff --git a/linera-execution/tests/fee_consumption.rs b/linera-execution/tests/fee_consumption.rs index fdc4b31b51f7..fab88e7d4467 100644 --- a/linera-execution/tests/fee_consumption.rs +++ b/linera-execution/tests/fee_consumption.rs @@ -121,7 +121,7 @@ async fn test_fee_consumption( description: Some(ChainDescription::Root(0)), ..SystemExecutionState::default() }; - let (application_id, application) = state.register_mock_application().await?; + let (application_id, application) = state.register_mock_application(0).await?; let mut view = state.into_view().await; let signer = Owner::from(AccountPublicKey::test_key(0)); @@ -196,7 +196,7 @@ async fn test_fee_consumption( message_id: MessageId::default(), }; let mut grant = initial_grant.unwrap_or_default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_message( context, Timestamp::from(0), diff --git a/linera-execution/tests/revm.rs b/linera-execution/tests/revm.rs index 727f9d9c00ee..67803044b3a3 100644 --- a/linera-execution/tests/revm.rs +++ b/linera-execution/tests/revm.rs @@ -146,11 +146,7 @@ contract ExampleCounter { .into_view_with(ChainId::root(0), ExecutionRuntimeConfig::default()) .await; let (app_desc, contract_blob, service_blob) = create_dummy_user_application_description(1); - let app_id = view - .system - .registry - .register_application(app_desc.clone()) - .await?; + let app_id = From::from(&app_desc); let contract = EvmContractModule::Revm { module: module.clone(), @@ -210,7 +206,7 @@ contract ExampleCounter { }; for increment in &increments { - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); value += increment; let operation = incrementCall { input: *increment }; let bytes = operation.abi_encode(); diff --git a/linera-execution/tests/service_runtime_apis.rs b/linera-execution/tests/service_runtime_apis.rs index 0df08ffb40b0..7d3b52f7e87c 100644 --- a/linera-execution/tests/service_runtime_apis.rs +++ b/linera-execution/tests/service_runtime_apis.rs @@ -29,7 +29,7 @@ async fn test_read_chain_balance_system_api(chain_balance: Amount) { .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::handle_query( move |runtime, _context, _query| { @@ -61,7 +61,7 @@ async fn test_read_owner_balance_system_api( .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::handle_query( move |runtime, _context, _query| { @@ -92,7 +92,7 @@ async fn test_read_owner_balance_returns_zero_for_missing_accounts(missing_accou .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::handle_query( move |runtime, _context, _query| { @@ -127,7 +127,7 @@ async fn test_read_owner_balances_system_api( .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::handle_query( move |runtime, _context, _query| { @@ -162,7 +162,7 @@ async fn test_read_balance_owners_system_api( .into_view() .await; - let (application_id, application) = view.register_mock_application().await.unwrap(); + let (application_id, application) = view.register_mock_application(0).await.unwrap(); application.expect_call(ExpectedCall::handle_query( move |runtime, _context, _query| { diff --git a/linera-execution/tests/test_execution.rs b/linera-execution/tests/test_execution.rs index 320cab872575..e90794ece16a 100644 --- a/linera-execution/tests/test_execution.rs +++ b/linera-execution/tests/test_execution.rs @@ -7,11 +7,10 @@ use std::{collections::BTreeMap, vec}; use anyhow::Context as _; use assert_matches::assert_matches; -use futures::{stream, StreamExt, TryStreamExt}; use linera_base::{ crypto::{AccountPublicKey, ValidatorPublicKey}, data_types::{ - Amount, ApplicationPermissions, BlockHeight, Resources, SendMessageRequest, Timestamp, + Amount, ApplicationPermissions, Blob, BlockHeight, Resources, SendMessageRequest, Timestamp, }, identifiers::{ Account, AccountOwner, ChainDescription, ChainId, Destination, MessageId, Owner, @@ -27,9 +26,9 @@ use linera_execution::{ SystemExecutionState, }, BaseRuntime, ContractRuntime, ExecutionError, ExecutionOutcome, ExecutionRuntimeContext, - Message, MessageKind, Operation, OperationContext, Query, QueryContext, QueryOutcome, - QueryResponse, RawExecutionOutcome, RawOutgoingMessage, ResourceControlPolicy, - ResourceController, SystemOperation, TransactionTracker, + Message, Operation, OperationContext, Query, QueryContext, QueryOutcome, QueryResponse, + RawExecutionOutcome, RawOutgoingMessage, ResourceControlPolicy, ResourceController, + SystemOperation, TransactionTracker, }; use linera_views::{batch::Batch, context::Context, views::View}; use test_case::test_case; @@ -41,10 +40,14 @@ async fn test_missing_bytecode_for_user_application() -> anyhow::Result<()> { let mut view = state.into_view().await; let (app_id, app_desc, contract_blob, service_blob) = - &create_dummy_user_application_registrations(&mut view.system.registry, 1).await?[0]; + &create_dummy_user_application_registrations(1).await?[0]; view.context() .extra() - .add_blobs([contract_blob.clone(), service_blob.clone()]) + .add_blobs([ + contract_blob.clone(), + service_blob.clone(), + Blob::new_application_description(app_desc), + ]) .await?; let context = create_dummy_operation_context(); @@ -57,7 +60,7 @@ async fn test_missing_bytecode_for_user_application() -> anyhow::Result<()> { application_id: *app_id, bytes: vec![], }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -76,8 +79,8 @@ async fn test_simple_user_operation() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, target_application) = view.register_mock_application(1).await?; let owner = Owner::from(AccountPublicKey::test_key(0)); let state_key = vec![]; @@ -140,7 +143,7 @@ async fn test_simple_user_operation() -> anyhow::Result<()> { ..create_dummy_operation_context() }; let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -264,8 +267,8 @@ async fn test_simulated_session() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, target_application) = view.register_mock_application(1).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -320,7 +323,7 @@ async fn test_simulated_session() -> anyhow::Result<()> { let context = create_dummy_operation_context(); let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -372,8 +375,8 @@ async fn test_simulated_session_leak() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, target_application) = view.register_mock_application(1).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -425,7 +428,7 @@ async fn test_simulated_session_leak() -> anyhow::Result<()> { application_id: caller_id, bytes: vec![], }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -441,7 +444,7 @@ async fn test_rejecting_block_from_finalize() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (id, application) = view.register_mock_application().await?; + let (id, application) = view.register_mock_application(0).await?; application.expect_call(ExpectedCall::execute_operation( move |_runtime, _context, _operation| Ok(vec![]), @@ -463,7 +466,7 @@ async fn test_rejecting_block_from_finalize() -> anyhow::Result<()> { application_id: id, bytes: vec![], }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -479,10 +482,10 @@ async fn test_rejecting_block_from_called_applications_finalize() -> anyhow::Res state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (first_id, first_application) = view.register_mock_application().await?; - let (second_id, second_application) = view.register_mock_application().await?; - let (third_id, third_application) = view.register_mock_application().await?; - let (fourth_id, fourth_application) = view.register_mock_application().await?; + let (first_id, first_application) = view.register_mock_application(0).await?; + let (second_id, second_application) = view.register_mock_application(1).await?; + let (third_id, third_application) = view.register_mock_application(2).await?; + let (fourth_id, fourth_application) = view.register_mock_application(3).await?; first_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -525,7 +528,7 @@ async fn test_rejecting_block_from_called_applications_finalize() -> anyhow::Res application_id: first_id, bytes: vec![], }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -541,10 +544,10 @@ async fn test_sending_message_from_finalize() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (first_id, first_application) = view.register_mock_application().await?; - let (second_id, second_application) = view.register_mock_application().await?; - let (third_id, third_application) = view.register_mock_application().await?; - let (fourth_id, fourth_application) = view.register_mock_application().await?; + let (first_id, first_application) = view.register_mock_application(0).await?; + let (second_id, second_application) = view.register_mock_application(1).await?; + let (third_id, third_application) = view.register_mock_application(2).await?; + let (fourth_id, fourth_application) = view.register_mock_application(3).await?; let destination_chain = ChainId::from(ChainDescription::Root(1)); let first_message = SendMessageRequest { @@ -625,7 +628,7 @@ async fn test_sending_message_from_finalize() -> anyhow::Result<()> { let context = create_dummy_operation_context(); let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -637,20 +640,7 @@ async fn test_sending_message_from_finalize() -> anyhow::Result<()> { &mut controller, ) .await?; - view.update_execution_outcomes_with_app_registrations(&mut txn_tracker) - .await?; - let applications = stream::iter([third_id, first_id]) - .then(|id| view.system.registry.describe_application(id)) - .try_collect() - .await?; - let registration_message = RawOutgoingMessage { - destination: Destination::from(destination_chain), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { applications }, - }; let account = Account { chain_id: ChainId::root(0), owner: None, @@ -660,9 +650,6 @@ async fn test_sending_message_from_finalize() -> anyhow::Result<()> { assert_eq!( txn_outcome.outcomes, vec![ - ExecutionOutcome::System( - RawExecutionOutcome::default().with_message(registration_message) - ), ExecutionOutcome::User( fourth_id, RawExecutionOutcome::default().with_refund_grant_to(Some(account)) @@ -714,8 +701,8 @@ async fn test_cross_application_call_from_finalize() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, _target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, _target_application) = view.register_mock_application(1).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |_runtime, _context, _operation| Ok(vec![]), @@ -738,7 +725,7 @@ async fn test_cross_application_call_from_finalize() -> anyhow::Result<()> { application_id: caller_id, bytes: vec![], }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -762,8 +749,8 @@ async fn test_cross_application_call_from_finalize_of_called_application() -> an state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, target_application) = view.register_mock_application(1).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -793,7 +780,7 @@ async fn test_cross_application_call_from_finalize_of_called_application() -> an application_id: caller_id, bytes: vec![], }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -816,8 +803,8 @@ async fn test_calling_application_again_from_finalize() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, target_application) = view.register_mock_application(1).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -847,7 +834,7 @@ async fn test_calling_application_again_from_finalize() -> anyhow::Result<()> { application_id: caller_id, bytes: vec![], }, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await; @@ -873,8 +860,8 @@ async fn test_cross_application_error() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, target_application) = view.register_mock_application(1).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -900,7 +887,7 @@ async fn test_cross_application_error() -> anyhow::Result<()> { bytes: vec![], }, &mut TransactionTracker::new( - 0, + 0, 0, Some(Vec::new())), &mut controller, ) @@ -919,7 +906,7 @@ async fn test_simple_message() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (application_id, application) = view.register_mock_application().await?; + let (application_id, application) = view.register_mock_application(0).await?; let destination_chain = ChainId::from(ChainDescription::Root(1)); let dummy_message = SendMessageRequest { @@ -944,7 +931,7 @@ async fn test_simple_message() -> anyhow::Result<()> { let context = create_dummy_operation_context(); let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -956,23 +943,7 @@ async fn test_simple_message() -> anyhow::Result<()> { &mut controller, ) .await?; - view.update_execution_outcomes_with_app_registrations(&mut txn_tracker) - .await?; - let application_description = view - .system - .registry - .describe_application(application_id) - .await?; - let registration_message = RawOutgoingMessage { - destination: Destination::from(destination_chain), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { - applications: vec![application_description], - }, - }; let account = Account { chain_id: ChainId::root(0), owner: None, @@ -982,9 +953,6 @@ async fn test_simple_message() -> anyhow::Result<()> { assert_eq!( txn_outcome.outcomes, &[ - ExecutionOutcome::System( - RawExecutionOutcome::default().with_message(registration_message) - ), ExecutionOutcome::User( application_id, RawExecutionOutcome::default() @@ -1009,8 +977,8 @@ async fn test_message_from_cross_application_call() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (target_id, target_application) = view.register_mock_application(1).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -1044,7 +1012,7 @@ async fn test_message_from_cross_application_call() -> anyhow::Result<()> { let context = create_dummy_operation_context(); let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -1056,19 +1024,7 @@ async fn test_message_from_cross_application_call() -> anyhow::Result<()> { &mut controller, ) .await?; - view.update_execution_outcomes_with_app_registrations(&mut txn_tracker) - .await?; - let target_description = view.system.registry.describe_application(target_id).await?; - let registration_message = RawOutgoingMessage { - destination: Destination::from(destination_chain), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { - applications: vec![target_description], - }, - }; let account = Account { chain_id: ChainId::root(0), owner: None, @@ -1078,9 +1034,6 @@ async fn test_message_from_cross_application_call() -> anyhow::Result<()> { assert_eq!( txn_outcome.outcomes, &[ - ExecutionOutcome::System( - RawExecutionOutcome::default().with_message(registration_message) - ), ExecutionOutcome::User( target_id, RawExecutionOutcome::default() @@ -1112,9 +1065,9 @@ async fn test_message_from_deeper_call() -> anyhow::Result<()> { state.description = Some(ChainDescription::Root(0)); let mut view = state.into_view().await; - let (caller_id, caller_application) = view.register_mock_application().await?; - let (middle_id, middle_application) = view.register_mock_application().await?; - let (target_id, target_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; + let (middle_id, middle_application) = view.register_mock_application(1).await?; + let (target_id, target_application) = view.register_mock_application(2).await?; caller_application.expect_call(ExpectedCall::execute_operation( move |runtime, _context, _operation| { @@ -1156,7 +1109,7 @@ async fn test_message_from_deeper_call() -> anyhow::Result<()> { let context = create_dummy_operation_context(); let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -1169,18 +1122,6 @@ async fn test_message_from_deeper_call() -> anyhow::Result<()> { ) .await?; - let target_description = view.system.registry.describe_application(target_id).await?; - let registration_message = RawOutgoingMessage { - destination: Destination::from(destination_chain), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { - applications: vec![target_description], - }, - }; - view.update_execution_outcomes_with_app_registrations(&mut txn_tracker) - .await?; let account = Account { chain_id: ChainId::root(0), owner: None, @@ -1189,9 +1130,6 @@ async fn test_message_from_deeper_call() -> anyhow::Result<()> { assert_eq!( txn_outcome.outcomes, &[ - ExecutionOutcome::System( - RawExecutionOutcome::default().with_message(registration_message) - ), ExecutionOutcome::User( target_id, RawExecutionOutcome::default() @@ -1236,11 +1174,11 @@ async fn test_multiple_messages_from_different_applications() -> anyhow::Result< let mut view = state.into_view().await; // The entrypoint application, which sends a message and calls other applications - let (caller_id, caller_application) = view.register_mock_application().await?; + let (caller_id, caller_application) = view.register_mock_application(0).await?; // An application that does not send any messages - let (silent_target_id, silent_target_application) = view.register_mock_application().await?; + let (silent_target_id, silent_target_application) = view.register_mock_application(1).await?; // An application that sends a message when handling a cross-application call - let (sending_target_id, sending_target_application) = view.register_mock_application().await?; + let (sending_target_id, sending_target_application) = view.register_mock_application(2).await?; // The first destination chain receives messages from the caller and the sending applications let first_destination_chain = ChainId::from(ChainDescription::Root(1)); @@ -1313,7 +1251,7 @@ async fn test_multiple_messages_from_different_applications() -> anyhow::Result< // Execute the operation, starting the test scenario let context = create_dummy_operation_context(); let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -1325,39 +1263,6 @@ async fn test_multiple_messages_from_different_applications() -> anyhow::Result< &mut controller, ) .await?; - view.update_execution_outcomes_with_app_registrations(&mut txn_tracker) - .await?; - - // Describe the two applications that sent messages, and will therefore handle them in the - // other chains - let caller_description = view.system.registry.describe_application(caller_id).await?; - let sending_target_description = view - .system - .registry - .describe_application(sending_target_id) - .await?; - - // The registration message for the first destination chain - let first_registration_message = RawOutgoingMessage { - destination: Destination::from(first_destination_chain), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { - applications: vec![sending_target_description.clone(), caller_description], - }, - }; - - // The registration message for the second destination chain - let second_registration_message = RawOutgoingMessage { - destination: Destination::from(second_destination_chain), - authenticated: false, - grant: Amount::ZERO, - kind: MessageKind::Simple, - message: SystemMessage::RegisterApplications { - applications: vec![sending_target_description], - }, - }; let account = Account { chain_id: ChainId::root(0), @@ -1369,11 +1274,6 @@ async fn test_multiple_messages_from_different_applications() -> anyhow::Result< assert_eq!( txn_outcome.outcomes, &[ - ExecutionOutcome::System( - RawExecutionOutcome::default() - .with_message(second_registration_message) - .with_message(first_registration_message) - ), ExecutionOutcome::User( silent_target_id, RawExecutionOutcome::default().with_refund_grant_to(Some(account)), @@ -1424,7 +1324,7 @@ async fn test_open_chain() -> anyhow::Result<()> { ..SystemExecutionState::new(Epoch::ZERO, ChainDescription::Root(0), ChainId::root(0)) }; let mut view = state.into_view().await; - let (application_id, application) = view.register_mock_application().await?; + let (application_id, application) = view.register_mock_application(0).await?; let context = OperationContext { height: BlockHeight(1), @@ -1462,7 +1362,7 @@ async fn test_open_chain() -> anyhow::Result<()> { application_id, bytes: vec![], }; - let mut txn_tracker = TransactionTracker::new(first_message_index, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(first_message_index, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -1527,7 +1427,7 @@ async fn test_close_chain() -> anyhow::Result<()> { ..SystemExecutionState::new(Epoch::ZERO, ChainDescription::Root(0), ChainId::root(0)) }; let mut view = state.into_view().await; - let (application_id, application) = view.register_mock_application().await?; + let (application_id, application) = view.register_mock_application(0).await?; // The application is not authorized to close the chain. let context = create_dummy_operation_context(); @@ -1551,7 +1451,7 @@ async fn test_close_chain() -> anyhow::Result<()> { context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await?; @@ -1564,7 +1464,7 @@ async fn test_close_chain() -> anyhow::Result<()> { context, Timestamp::from(0), operation.into(), - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await?; @@ -1585,7 +1485,7 @@ async fn test_close_chain() -> anyhow::Result<()> { context, Timestamp::from(0), operation, - &mut TransactionTracker::new(0, Some(Vec::new())), + &mut TransactionTracker::new(0, 0, Some(Vec::new())), &mut controller, ) .await?; @@ -1649,7 +1549,7 @@ async fn test_message_receipt_spending_chain_balance( .into_view() .await; - let (application_id, application) = view.register_mock_application().await?; + let (application_id, application) = view.register_mock_application(0).await?; let receiver_chain_account = None; let sender_chain_id = ChainId::root(2); @@ -1668,7 +1568,7 @@ async fn test_message_receipt_spending_chain_balance( let context = create_dummy_message_context(authenticated_signer); let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); let execution_result = view .execute_message( diff --git a/linera-execution/tests/test_system_execution.rs b/linera-execution/tests/test_system_execution.rs index e7801f152591..22e706cc1c18 100644 --- a/linera-execution/tests/test_system_execution.rs +++ b/linera-execution/tests/test_system_execution.rs @@ -44,7 +44,7 @@ async fn test_simple_system_operation() -> anyhow::Result<()> { authenticated_caller_id: None, }; let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), @@ -96,7 +96,7 @@ async fn test_simple_system_message() -> anyhow::Result<()> { refund_grant_to: None, }; let mut controller = ResourceController::default(); - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_message( context, Timestamp::from(0), diff --git a/linera-execution/tests/wasm.rs b/linera-execution/tests/wasm.rs index b85b8a8383c7..048beca0c836 100644 --- a/linera-execution/tests/wasm.rs +++ b/linera-execution/tests/wasm.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use linera_base::{ - data_types::{Amount, BlockHeight, Timestamp}, + data_types::{Amount, Blob, BlockHeight, Timestamp}, identifiers::{Account, ChainDescription, ChainId}, }; use linera_execution::{ @@ -41,11 +41,7 @@ async fn test_fuel_for_counter_wasm_application( .into_view_with(ChainId::root(0), ExecutionRuntimeConfig::default()) .await; let (app_desc, contract_blob, service_blob) = create_dummy_user_application_description(1); - let app_id = view - .system - .registry - .register_application(app_desc.clone()) - .await?; + let app_id = From::from(&app_desc); let contract = WasmContractModule::from_file("tests/fixtures/counter_contract.wasm", wasm_runtime).await?; @@ -63,7 +59,11 @@ async fn test_fuel_for_counter_wasm_application( view.context() .extra() - .add_blobs([contract_blob, service_blob]) + .add_blobs([ + contract_blob, + service_blob, + Blob::new_application_description(&app_desc), + ]) .await?; let context = OperationContext { @@ -92,7 +92,7 @@ async fn test_fuel_for_counter_wasm_application( chain_id: ChainId::root(0), owner: None, }; - let mut txn_tracker = TransactionTracker::new(0, Some(Vec::new())); + let mut txn_tracker = TransactionTracker::new(0, 0, Some(Vec::new())); view.execute_operation( context, Timestamp::from(0), diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index aa51e6677c4e..88ded9eeb591 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -37,10 +37,10 @@ Amount: NEWTYPESTRUCT: U128 ApplicationId: STRUCT: + - application_description_hash: + TYPENAME: CryptoHash - bytecode_id: TYPENAME: BytecodeId - - creation: - TYPENAME: MessageId ApplicationPermissions: STRUCT: - execute_operations: @@ -75,6 +75,8 @@ BlobType: ContractBytecode: UNIT 2: ServiceBytecode: UNIT + 3: + ApplicationDescription: UNIT Block: STRUCT: - header: @@ -101,6 +103,10 @@ BlockBody: SEQ: SEQ: TYPENAME: Event + - blobs: + SEQ: + SEQ: + TYPENAME: BlobContent BlockExecutionOutcome: STRUCT: - messages: @@ -117,6 +123,10 @@ BlockExecutionOutcome: SEQ: SEQ: TYPENAME: Event + - blobs: + SEQ: + SEQ: + TYPENAME: BlobContent BlockHeader: STRUCT: - chain_id: @@ -1024,10 +1034,6 @@ SystemMessage: - applications: SEQ: TYPENAME: UserApplicationDescription - 9: - RequestApplication: - NEWTYPE: - TYPENAME: ApplicationId SystemOperation: ENUM: 0: @@ -1116,13 +1122,6 @@ SystemOperation: SEQ: TYPENAME: ApplicationId 12: - RequestApplication: - STRUCT: - - chain_id: - TYPENAME: ChainId - - application_id: - TYPENAME: ApplicationId - 13: Admin: NEWTYPE: TYPENAME: AdminOperation @@ -1164,8 +1163,11 @@ UserApplicationDescription: STRUCT: - bytecode_id: TYPENAME: BytecodeId - - creation: - TYPENAME: MessageId + - creator_chain_id: + TYPENAME: ChainId + - block_height: + TYPENAME: BlockHeight + - application_index: U32 - parameters: BYTES - required_application_ids: SEQ: diff --git a/linera-sdk/src/contract/conversions_from_wit.rs b/linera-sdk/src/contract/conversions_from_wit.rs index 86013f146440..50d5dfe78456 100644 --- a/linera-sdk/src/contract/conversions_from_wit.rs +++ b/linera-sdk/src/contract/conversions_from_wit.rs @@ -34,8 +34,8 @@ impl From for MessageId { impl From for ApplicationId { fn from(application_id: wit_system_api::ApplicationId) -> Self { ApplicationId { + application_description_hash: application_id.application_description_hash.into(), bytecode_id: application_id.bytecode_id.into(), - creation: application_id.creation.into(), } } } diff --git a/linera-sdk/src/contract/conversions_to_wit.rs b/linera-sdk/src/contract/conversions_to_wit.rs index e65c376c09f2..18f35cc7eb28 100644 --- a/linera-sdk/src/contract/conversions_to_wit.rs +++ b/linera-sdk/src/contract/conversions_to_wit.rs @@ -93,8 +93,8 @@ impl From for wit_system_api::ChainId { impl From for wit_system_api::ApplicationId { fn from(application_id: ApplicationId) -> Self { wit_system_api::ApplicationId { + application_description_hash: application_id.application_description_hash.into(), bytecode_id: application_id.bytecode_id.into(), - creation: application_id.creation.into(), } } } diff --git a/linera-sdk/src/service/conversions_from_wit.rs b/linera-sdk/src/service/conversions_from_wit.rs index 889dad655ce8..05ca2bb438e1 100644 --- a/linera-sdk/src/service/conversions_from_wit.rs +++ b/linera-sdk/src/service/conversions_from_wit.rs @@ -7,7 +7,7 @@ use linera_base::{ crypto::CryptoHash, data_types::{Amount, BlockHeight, Timestamp}, http, - identifiers::{AccountOwner, ApplicationId, BytecodeId, ChainId, MessageId, Owner}, + identifiers::{AccountOwner, ApplicationId, BytecodeId, ChainId, Owner}, }; use super::wit::service_system_api as wit_system_api; @@ -66,21 +66,11 @@ impl From for CryptoHash { } } -impl From for MessageId { - fn from(message_id: wit_system_api::MessageId) -> Self { - MessageId { - chain_id: message_id.chain_id.into(), - height: message_id.height.into(), - index: message_id.index, - } - } -} - impl From for ApplicationId { fn from(application_id: wit_system_api::ApplicationId) -> Self { ApplicationId { + application_description_hash: application_id.application_description_hash.into(), bytecode_id: application_id.bytecode_id.into(), - creation: application_id.creation.into(), } } } diff --git a/linera-sdk/src/service/conversions_to_wit.rs b/linera-sdk/src/service/conversions_to_wit.rs index 8bd32686f626..7b52c9c79cbf 100644 --- a/linera-sdk/src/service/conversions_to_wit.rs +++ b/linera-sdk/src/service/conversions_to_wit.rs @@ -7,7 +7,7 @@ use linera_base::{ crypto::CryptoHash, data_types::BlockHeight, http, - identifiers::{AccountOwner, ApplicationId, BytecodeId, ChainId, MessageId, Owner}, + identifiers::{AccountOwner, ApplicationId, BytecodeId, ChainId, Owner}, }; use super::wit::service_system_api as wit_system_api; @@ -75,8 +75,8 @@ impl From for wit_system_api::ChainId { impl From for wit_system_api::ApplicationId { fn from(application_id: ApplicationId) -> Self { wit_system_api::ApplicationId { + application_description_hash: application_id.application_description_hash.into(), bytecode_id: application_id.bytecode_id.into(), - creation: application_id.creation.into(), } } } @@ -90,16 +90,6 @@ impl From for wit_system_api::BytecodeId { } } -impl From for wit_system_api::MessageId { - fn from(message_id: MessageId) -> Self { - wit_system_api::MessageId { - chain_id: message_id.chain_id.into(), - height: message_id.height.into(), - index: message_id.index, - } - } -} - impl From for wit_system_api::HttpRequest { fn from(request: http::Request) -> Self { wit_system_api::HttpRequest { diff --git a/linera-sdk/src/test/block.rs b/linera-sdk/src/test/block.rs index a932a4ddac0d..541742e3595e 100644 --- a/linera-sdk/src/test/block.rs +++ b/linera-sdk/src/test/block.rs @@ -103,17 +103,6 @@ impl BlockBuilder { self } - /// Adds a request to register an application on this chain. - pub fn with_request_for_application( - &mut self, - application: ApplicationId, - ) -> &mut Self { - self.with_system_operation(SystemOperation::RequestApplication { - chain_id: application.creation.chain_id, - application_id: application.forget_abi(), - }) - } - /// Adds an operation to change this chain's ownership. pub fn with_owner_change( &mut self, diff --git a/linera-sdk/src/test/chain.rs b/linera-sdk/src/test/chain.rs index 7a81ac03f085..f676b44d408b 100644 --- a/linera-sdk/src/test/chain.rs +++ b/linera-sdk/src/test/chain.rs @@ -14,15 +14,12 @@ use std::{ use cargo_toml::Manifest; use linera_base::{ crypto::{AccountPublicKey, AccountSecretKey}, - data_types::{Blob, BlockHeight, Bytecode, CompressedBytecode}, - identifiers::{ApplicationId, BytecodeId, ChainDescription, ChainId, MessageId}, + data_types::{Blob, BlockHeight, Bytecode, CompressedBytecode, UserApplicationDescription}, + identifiers::{ApplicationId, BytecodeId, ChainDescription, ChainId}, }; -use linera_chain::{types::ConfirmedBlockCertificate, ChainError, ChainExecutionContext}; +use linera_chain::types::ConfirmedBlockCertificate; use linera_core::{data_types::ChainInfoQuery, worker::WorkerError}; -use linera_execution::{ - system::{SystemExecutionError, SystemOperation, CREATE_APPLICATION_MESSAGE_INDEX}, - ExecutionError, Query, QueryOutcome, QueryResponse, -}; +use linera_execution::{system::SystemOperation, Query, QueryOutcome, QueryResponse}; use linera_storage::Storage as _; use serde::Serialize; use tokio::{fs, sync::Mutex}; @@ -365,33 +362,30 @@ impl ActiveChain { let parameters = serde_json::to_vec(¶meters).unwrap(); let instantiation_argument = serde_json::to_vec(&instantiation_argument).unwrap(); - for &dependency in &required_application_ids { - self.register_application(dependency).await; - } - let creation_certificate = self .add_block(|block| { block.with_system_operation(SystemOperation::CreateApplication { bytecode_id: bytecode_id.forget_abi(), - parameters, + parameters: parameters.clone(), instantiation_argument, - required_application_ids, + required_application_ids: required_application_ids.clone(), }); }) .await; let block = creation_certificate.inner().block(); assert_eq!(block.messages().len(), 1); - let creation = MessageId { - chain_id: block.header.chain_id, - height: block.header.height, - index: CREATE_APPLICATION_MESSAGE_INDEX, + + let description = UserApplicationDescription { + bytecode_id: bytecode_id.forget_abi(), + creator_chain_id: block.header.chain_id, + block_height: block.header.height, + application_index: 0, + parameters, + required_application_ids, }; - ApplicationId { - bytecode_id: bytecode_id.just_abi(), - creation, - } + ApplicationId::<()>::from(&description).with_abi() } /// Returns whether this chain has been closed. @@ -404,61 +398,6 @@ impl ActiveChain { .is_closed() } - /// Registers on this chain an application created on another chain. - pub async fn register_application(&self, application_id: ApplicationId) { - if self.needs_application_description(application_id).await { - let source_chain = self.validator.get_chain(&application_id.creation.chain_id); - - let request_certificate = self - .add_block(|block| { - block.with_request_for_application(application_id); - }) - .await; - - let register_certificate = source_chain - .add_block(|block| { - block.with_messages_from(&request_certificate); - }) - .await; - - let final_certificate = self - .add_block(|block| { - block.with_messages_from(®ister_certificate); - }) - .await; - - assert_eq!(final_certificate.outgoing_message_count(), 0); - } - } - - /// Checks if the `application_id` is missing from this microchain. - async fn needs_application_description(&self, application_id: ApplicationId) -> bool { - let description_result = self - .validator - .worker() - .describe_application(self.id(), application_id.forget_abi()) - .await; - - match description_result { - Ok(_) => false, - Err(WorkerError::ChainError(boxed_chain_error)) - if matches!( - &*boxed_chain_error, - ChainError::ExecutionError( - execution_error, - ChainExecutionContext::DescribeApplication, - ) if matches!( - **execution_error, - ExecutionError::SystemError(SystemExecutionError::UnknownApplicationId(_)) - ) - ) => - { - true - } - Err(_) => panic!("Failed to check known bytecode locations"), - } - } - /// Executes a `query` on an `application`'s state on this microchain. /// /// Returns the deserialized response from the `application`. diff --git a/linera-sdk/wit/contract-system-api.wit b/linera-sdk/wit/contract-system-api.wit index 6d7c89ec6c89..bd195006c4e1 100644 --- a/linera-sdk/wit/contract-system-api.wit +++ b/linera-sdk/wit/contract-system-api.wit @@ -49,8 +49,8 @@ interface contract-system-api { } record application-id { + application-description-hash: crypto-hash, bytecode-id: bytecode-id, - creation: message-id, } record application-permissions { diff --git a/linera-sdk/wit/service-system-api.wit b/linera-sdk/wit/service-system-api.wit index 18bee9ea510b..ba3d310a6600 100644 --- a/linera-sdk/wit/service-system-api.wit +++ b/linera-sdk/wit/service-system-api.wit @@ -30,8 +30,8 @@ interface service-system-api { } record application-id { + application-description-hash: crypto-hash, bytecode-id: bytecode-id, - creation: message-id, } record block-height { @@ -92,12 +92,6 @@ interface service-system-api { trace, } - record message-id { - chain-id: chain-id, - height: block-height, - index: u32, - } - record owner { inner0: crypto-hash, } diff --git a/linera-service-graphql-client/gql/service_requests.graphql b/linera-service-graphql-client/gql/service_requests.graphql index 59de764e831b..cb81984396f3 100644 --- a/linera-service-graphql-client/gql/service_requests.graphql +++ b/linera-service-graphql-client/gql/service_requests.graphql @@ -277,6 +277,7 @@ query Block($hash: CryptoHash, $chainId: ChainId!) { key value } + blobs } } } @@ -340,6 +341,7 @@ query Blocks($from: CryptoHash, $chainId: ChainId!, $limit: Int) { key value } + blobs } } } diff --git a/linera-service-graphql-client/gql/service_schema.graphql b/linera-service-graphql-client/gql/service_schema.graphql index 56e1dd35cf9d..b232dfceba37 100644 --- a/linera-service-graphql-client/gql/service_schema.graphql +++ b/linera-service-graphql-client/gql/service_schema.graphql @@ -103,6 +103,10 @@ type BlockBody { The list of events produced by each transaction. """ events: [[Event!]!]! + """ + The list of blobs produced by each transaction. + """ + blobs: [[Blob!]!]! } """ @@ -831,11 +835,6 @@ type MutationRoot { Creates a new application. """ createApplication(chainId: ChainId!, bytecodeId: BytecodeId!, parameters: String!, instantiationArgument: String!, requiredApplicationIds: [ApplicationId!]!): ApplicationId! - """ - Requests a `RegisterApplications` message from another chain so the application can be used - on this one. - """ - requestApplication(chainId: ChainId!, applicationId: ApplicationId!, targetChainId: ChainId): CryptoHash! } """ diff --git a/linera-service-graphql-client/src/service.rs b/linera-service-graphql-client/src/service.rs index 2bfd4b3454f9..b8b0931625b2 100644 --- a/linera-service-graphql-client/src/service.rs +++ b/linera-service-graphql-client/src/service.rs @@ -4,7 +4,7 @@ use graphql_client::GraphQLQuery; use linera_base::{ crypto::CryptoHash, - data_types::{Amount, BlockHeight, OracleResponse, Round, Timestamp}, + data_types::{Amount, Blob, BlockHeight, OracleResponse, Round, Timestamp}, identifiers::{ Account, BlobId, ChainDescription, ChainId, ChannelName, Destination, GenericApplicationId, Owner, StreamName, @@ -244,6 +244,7 @@ mod from { operations, oracle_responses, events, + blobs, } = body; let block_header = BlockHeader { @@ -275,6 +276,10 @@ mod from { .into_iter() .map(|events| events.into_iter().map(Into::into).collect()) .collect(), + blobs: blobs + .into_iter() + .map(|blobs| blobs.into_iter().map(Into::into).collect()) + .collect(), }; Block { diff --git a/linera-service/src/cli_wrappers/wallet.rs b/linera-service/src/cli_wrappers/wallet.rs index d309661c4a39..c88314bc7918 100644 --- a/linera-service/src/cli_wrappers/wallet.rs +++ b/linera-service/src/cli_wrappers/wallet.rs @@ -444,25 +444,6 @@ impl ClientWrapper { Ok(stdout.trim().parse::()?.with_abi()) } - /// Runs `linera request-application` - pub async fn request_application( - &self, - application_id: ApplicationId, - requester_chain_id: ChainId, - target_chain_id: Option, - ) -> Result { - let mut command = self.command().await?; - command - .arg("request-application") - .arg(application_id.to_string()) - .args(["--requester-chain-id", &requester_chain_id.to_string()]); - if let Some(target_chain_id) = target_chain_id { - command.args(["--target-chain-id", &target_chain_id.to_string()]); - } - let stdout = command.spawn_and_wait_for_stdout().await?; - Ok(stdout.trim().parse()?) - } - /// Runs `linera service`. pub async fn run_node_service( &self, @@ -1125,6 +1106,7 @@ impl NodeService { &self, chain_id: &ChainId, ) -> Result> { + // uh oh let query = format!("query {{ applications(chainId: \"{chain_id}\") {{ id link }}}}"); let data = self.query_node(query).await?; data["applications"] @@ -1278,23 +1260,6 @@ impl NodeService { .with_abi()) } - pub async fn request_application( - &self, - chain_id: &ChainId, - application_id: &ApplicationId, - ) -> Result { - let application_id = application_id.forget_abi(); - let query = format!( - "mutation {{ requestApplication(\ - chainId: \"{chain_id}\", \ - applicationId: \"{application_id}\") \ - }}" - ); - let data = self.query_node(query).await?; - serde_json::from_value(data["requestApplication"].clone()) - .context("missing requestApplication field in response") - } - pub async fn subscribe( &self, subscriber_chain_id: ChainId, diff --git a/linera-service/src/linera/main.rs b/linera-service/src/linera/main.rs index 1184d04f6500..e3416d9c468a 100644 --- a/linera-service/src/linera/main.rs +++ b/linera-service/src/linera/main.rs @@ -773,9 +773,7 @@ impl Runnable for Job { if let Some(id) = fungible_application_id { let start = Instant::now(); - context - .supply_fungible_tokens(&key_pairs, id, &chain_clients) - .await?; + context.supply_fungible_tokens(&key_pairs, id).await?; info!( "Supplied fungible tokens in {} ms", start.elapsed().as_millis() @@ -1027,34 +1025,6 @@ impl Runnable for Job { println!("{}", application_id); } - RequestApplication { - application_id, - target_chain_id, - requester_chain_id, - } => { - let start_time = Instant::now(); - let requester_chain_id = - requester_chain_id.unwrap_or_else(|| context.default_chain()); - info!("Requesting application for chain {}", requester_chain_id); - let chain_client = context.make_chain_client(requester_chain_id)?; - let certificate = context - .apply_client_command(&chain_client, |chain_client| { - let chain_client = chain_client.clone(); - async move { - chain_client - .request_application(application_id, target_chain_id) - .await - } - }) - .await - .context("Failed to request application")?; - info!( - "Application requested in {} ms", - start_time.elapsed().as_millis() - ); - debug!("{:?}", certificate); - } - Assign { owner, message_id } => { let start_time = Instant::now(); let chain_id = ChainId::child(message_id); @@ -1411,7 +1381,6 @@ fn log_file_name_for(command: &ClientCommand) -> Cow<'static, str> { | ClientCommand::ReadDataBlob { .. } | ClientCommand::CreateApplication { .. } | ClientCommand::PublishAndCreate { .. } - | ClientCommand::RequestApplication { .. } | ClientCommand::Keygen { .. } | ClientCommand::Assign { .. } | ClientCommand::Wallet { .. } diff --git a/linera-service/src/node_service.rs b/linera-service/src/node_service.rs index 107c5a38ec37..e66fd20fd037 100644 --- a/linera-service/src/node_service.rs +++ b/linera-service/src/node_service.rs @@ -625,30 +625,6 @@ where }) .await } - - /// Requests a `RegisterApplications` message from another chain so the application can be used - /// on this one. - async fn request_application( - &self, - chain_id: ChainId, - application_id: UserApplicationId, - target_chain_id: Option, - ) -> Result { - loop { - let client = self.context.lock().await.make_chain_client(chain_id)?; - let result = client - .request_application(application_id, target_chain_id) - .await; - self.context.lock().await.update_wallet(&client).await?; - let timeout = match result? { - ClientOutcome::Committed(certificate) => return Ok(certificate.hash()), - ClientOutcome::WaitForTimeout(timeout) => timeout, - }; - let mut stream = client.subscribe().await?; - drop(client); - util::wait_for_next_round(&mut stream, timeout).await; - } - } } #[async_graphql::Object(cache_control(no_cache))] diff --git a/linera-service/tests/linera_net_tests.rs b/linera-service/tests/linera_net_tests.rs index d7288e6beb7d..a97ca843ba7f 100644 --- a/linera-service/tests/linera_net_tests.rs +++ b/linera-service/tests/linera_net_tests.rs @@ -614,16 +614,6 @@ async fn test_wasm_end_to_end_social_user_pub_sub(config: impl LineraNetConfig) .run_node_service(port2, ProcessInbox::Automatic) .await?; - // Request the application so chain 2 has it, too. - node_service2 - .request_application(&chain2, &application_id) - .await?; - - // First chain1 receives the request for application and then - // chain2 receives the requested application - node_service1.process_inbox(&chain1).await?; - node_service2.process_inbox(&chain2).await?; - let app2 = node_service2 .make_application(&chain2, &application_id) .await?; @@ -1344,16 +1334,6 @@ async fn test_wasm_end_to_end_crowd_funding(config: impl LineraNetConfig) -> Res ) .await; - // Register the campaign on chain2. - node_service2 - .request_application(&chain2, &application_id_crowd) - .await?; - - // Chain2 requests the application from chain1, so chain1 has - // to receive the request and then chain2 receive the answer. - assert_eq!(node_service1.process_inbox(&chain1).await?.len(), 1); - assert_eq!(node_service2.process_inbox(&chain2).await?.len(), 1); - let app_crowd2 = node_service2 .make_application(&chain2, &application_id_crowd) .await?; @@ -1466,27 +1446,6 @@ async fn test_wasm_end_to_end_matching_engine(config: impl LineraNetConfig) -> R let mut node_service_a = client_a.run_node_service(port2, ProcessInbox::Skip).await?; let mut node_service_b = client_b.run_node_service(port3, ProcessInbox::Skip).await?; - node_service_a - .request_application(&chain_a, &token1) - .await?; - node_service_b - .request_application(&chain_b, &token0) - .await?; - node_service_admin - .request_application(&chain_admin, &token0) - .await?; - node_service_admin - .request_application(&chain_admin, &token1) - .await?; - - // In an operation node_service_a.request_application(&chain_a, app_b) - // chain_b needs to process the request first and then chain_a - // the answer. - node_service_a.process_inbox(&chain_a).await?; - node_service_b.process_inbox(&chain_b).await?; - node_service_a.process_inbox(&chain_a).await?; - node_service_admin.process_inbox(&chain_admin).await?; - let app_fungible0_a = FungibleApp(node_service_a.make_application(&chain_a, &token0).await?); let app_fungible1_a = FungibleApp(node_service_a.make_application(&chain_a, &token1).await?); let app_fungible0_b = FungibleApp(node_service_b.make_application(&chain_b, &token0).await?); @@ -1555,21 +1514,6 @@ async fn test_wasm_end_to_end_matching_engine(config: impl LineraNetConfig) -> R .make_application(&chain_admin, &application_id_matching) .await?, ); - node_service_a - .request_application(&chain_a, &application_id_matching) - .await?; - node_service_b - .request_application(&chain_b, &application_id_matching) - .await?; - - // First chain_admin needs to process the two requests and - // then chain_a / chain_b the answers. - assert_eq!( - node_service_admin.process_inbox(&chain_admin).await?.len(), - 1 - ); - assert_eq!(node_service_a.process_inbox(&chain_a).await?.len(), 1); - assert_eq!(node_service_b.process_inbox(&chain_b).await?.len(), 1); let app_matching_a = MatchingEngineApp( node_service_a @@ -1900,18 +1844,6 @@ async fn test_wasm_end_to_end_amm(config: impl LineraNetConfig) -> Result<()> { .make_application(&chain_amm, &application_id_amm) .await?, ); - node_service0 - .request_application(&chain0, &application_id_amm) - .await?; - node_service1 - .request_application(&chain1, &application_id_amm) - .await?; - - // The chain_amm must first requests those two requests - // and then chain0 / chain1 must handle the answers. - assert_eq!(node_service_amm.process_inbox(&chain_amm).await?.len(), 1); - assert_eq!(node_service0.process_inbox(&chain0).await?.len(), 1); - assert_eq!(node_service1.process_inbox(&chain1).await?.len(), 1); let app_amm0 = AmmApp( node_service0