diff --git a/examples/meta-counter/src/contract.rs b/examples/meta-counter/src/contract.rs index 182320e90473..b930f4bfe9c4 100644 --- a/examples/meta-counter/src/contract.rs +++ b/examples/meta-counter/src/contract.rs @@ -4,7 +4,7 @@ #![cfg_attr(target_arch = "wasm32", no_main)] use linera_sdk::{ - base::{ApplicationId, WithContractAbi}, + base::{ApplicationId, StreamName, WithContractAbi}, Contract, ContractRuntime, Resources, }; use meta_counter::{Message, MetaCounterAbi, Operation}; @@ -40,6 +40,8 @@ impl Contract for MetaCounterContract { // Send a no-op message to ourselves. This is only for testing contracts that send messages // on initialization. Since the value is 0 it does not change the counter value. let this_chain = self.runtime.chain_id(); + self.runtime + .emit(StreamName(b"announcements".to_vec()), b"instantiated"); self.runtime.send_message(this_chain, Message::Increment(0)); } diff --git a/linera-base/src/identifiers.rs b/linera-base/src/identifiers.rs index df5398a41d7d..2cb8c84b0101 100644 --- a/linera-base/src/identifiers.rs +++ b/linera-base/src/identifiers.rs @@ -278,7 +278,7 @@ pub struct ChannelName(#[serde(with = "serde_bytes")] Vec); WitStore, WitType, )] -pub struct StreamName(#[serde(with = "serde_bytes")] Vec); +pub struct StreamName(#[serde(with = "serde_bytes")] pub Vec); /// The destination of a message, relative to a particular application. #[derive( diff --git a/linera-core/src/unit_tests/wasm_client_tests.rs b/linera-core/src/unit_tests/wasm_client_tests.rs index d42f91d748db..97b81f2a8465 100644 --- a/linera-core/src/unit_tests/wasm_client_tests.rs +++ b/linera-core/src/unit_tests/wasm_client_tests.rs @@ -20,10 +20,12 @@ use async_graphql::Request; use counter::CounterAbi; use linera_base::{ data_types::{Amount, HashedBlob, OracleResponse}, - identifiers::{AccountOwner, ApplicationId, ChainDescription, ChainId, Destination, Owner}, + identifiers::{ + AccountOwner, ApplicationId, ChainDescription, ChainId, Destination, Owner, StreamName, + }, ownership::{ChainOwnership, TimeoutConfig}, }; -use linera_chain::data_types::{CertificateValue, MessageAction, OutgoingMessage}; +use linera_chain::data_types::{CertificateValue, EventRecord, MessageAction, OutgoingMessage}; use linera_execution::{ Bytecode, Message, MessageKind, Operation, ResourceControlPolicy, SystemMessage, UserApplicationDescription, WasmRuntime, @@ -367,7 +369,7 @@ where .await .unwrap() .unwrap(); - let (application_id2, _) = creator + let (application_id2, certificate) = creator .create_application( bytecode_id2, &application_id1, @@ -377,6 +379,17 @@ where .await .unwrap() .unwrap(); + assert_eq!( + certificate.value().executed_block().unwrap().outcome.events, + vec![ + Vec::new(), + vec![EventRecord { + application_id: application_id2.forget_abi().into(), + stream_name: StreamName(b"announcements".to_vec()), + payload: b"instantiated".to_vec(), + }] + ] + ); let mut operation = meta_counter::Operation::increment(receiver_id, 5); operation.fuel_grant = 1000000; diff --git a/linera-core/src/unit_tests/wasm_worker_tests.rs b/linera-core/src/unit_tests/wasm_worker_tests.rs index 3f211b42c03b..b03c2f73cf9d 100644 --- a/linera-core/src/unit_tests/wasm_worker_tests.rs +++ b/linera-core/src/unit_tests/wasm_worker_tests.rs @@ -448,7 +448,7 @@ where message: Message::System(SystemMessage::ApplicationCreated), }], ], - events: vec![Vec::new()], + events: vec![Vec::new(); 2], state_hash: creator_state.crypto_hash().await?, oracle_responses: vec![Vec::new(); 2], } diff --git a/linera-core/src/unit_tests/worker_tests.rs b/linera-core/src/unit_tests/worker_tests.rs index 02a8c5ba8953..d439ad33b33b 100644 --- a/linera-core/src/unit_tests/worker_tests.rs +++ b/linera-core/src/unit_tests/worker_tests.rs @@ -709,7 +709,7 @@ where Amount::from_tokens(2), )], ], - events: vec![Vec::new(); 3], + events: vec![Vec::new(); 2], state_hash: SystemExecutionState { committees: [(epoch, committee.clone())].into_iter().collect(), ownership: ChainOwnership::single(sender_key_pair.public()), @@ -2331,7 +2331,7 @@ where }, ), ]], - events: vec![Vec::new(); 2], + events: vec![Vec::new()], state_hash: SystemExecutionState { committees: committees.clone(), ownership: ChainOwnership::single(key_pair.public()), diff --git a/linera-explorer/src/components/Block.test.ts b/linera-explorer/src/components/Block.test.ts index 098eda01d45e..2e1c19eea5ca 100644 --- a/linera-explorer/src/components/Block.test.ts +++ b/linera-explorer/src/components/Block.test.ts @@ -56,6 +56,7 @@ test('Block mounting', () => { } } }]], + events: [[]], stateHash: "5bcd40995283e74798c60e8dc7a93e8c61059440534070673dfb973b2b66f61a", oracleResponses: [] } diff --git a/linera-explorer/src/components/Blocks.test.ts b/linera-explorer/src/components/Blocks.test.ts index fec2c571b4d7..ec7253fea482 100644 --- a/linera-explorer/src/components/Blocks.test.ts +++ b/linera-explorer/src/components/Blocks.test.ts @@ -58,6 +58,7 @@ test('Blocks mounting', () => { } } }]], + events: [[]], stateHash: "5bcd40995283e74798c60e8dc7a93e8c61059440534070673dfb973b2b66f61a", oracleResponses: [] } diff --git a/linera-sdk/src/contract/test_runtime.rs b/linera-sdk/src/contract/test_runtime.rs index 1c0915323c3d..191eb9f96f09 100644 --- a/linera-sdk/src/contract/test_runtime.rs +++ b/linera-sdk/src/contract/test_runtime.rs @@ -13,6 +13,7 @@ use linera_base::{ data_types::{Amount, BlockHeight, HashedBlob, Resources, SendMessageRequest, Timestamp}, identifiers::{ Account, ApplicationId, BlobId, ChainId, ChannelName, Destination, MessageId, Owner, + StreamName, }, ownership::{ChainOwnership, CloseChainError}, }; @@ -43,6 +44,7 @@ where subscribe_requests: Vec<(ChainId, ChannelName)>, unsubscribe_requests: Vec<(ChainId, ChannelName)>, outgoing_transfers: HashMap, + events: Vec<(StreamName, Vec)>, claim_requests: Vec, expected_service_queries: VecDeque<(ApplicationId, String, String)>, expected_post_requests: VecDeque<(String, Vec, Vec)>, @@ -84,6 +86,7 @@ where subscribe_requests: Vec::new(), unsubscribe_requests: Vec::new(), outgoing_transfers: HashMap::new(), + events: Vec::new(), claim_requests: Vec::new(), expected_service_queries: VecDeque::new(), expected_post_requests: VecDeque::new(), @@ -586,6 +589,11 @@ where .expect("Failed to deserialize `Response` type from cross-application call") } + /// Adds a new item to an event stream. + pub fn emit(&mut self, name: StreamName, payload: &[u8]) { + self.events.push((name, payload.to_vec())); + } + /// Adds an expected `query_service` call`, and the response it should return in the test. pub fn add_expected_service_query( &mut self, diff --git a/linera-service-graphql-client/gql/service_requests.graphql b/linera-service-graphql-client/gql/service_requests.graphql index 7c88fde4a503..e5aba3bd61bc 100644 --- a/linera-service-graphql-client/gql/service_requests.graphql +++ b/linera-service-graphql-client/gql/service_requests.graphql @@ -182,6 +182,11 @@ query Block($hash: CryptoHash, $chainId: ChainId!) { } stateHash oracleResponses + events { + applicationId + streamName + payload + } } } } @@ -219,6 +224,11 @@ query Blocks($from: CryptoHash, $chainId: ChainId!, $limit: Int) { } stateHash oracleResponses + events { + applicationId + streamName + payload + } } } } diff --git a/linera-service-graphql-client/src/service.rs b/linera-service-graphql-client/src/service.rs index 6ade5792389b..10dd66ac4b1a 100644 --- a/linera-service-graphql-client/src/service.rs +++ b/linera-service-graphql-client/src/service.rs @@ -5,7 +5,10 @@ use graphql_client::GraphQLQuery; use linera_base::{ crypto::CryptoHash, data_types::{Amount, BlockHeight, OracleResponse, Timestamp}, - identifiers::{Account, ChainDescription, ChainId, ChannelName, Destination, Owner}, + identifiers::{ + Account, ChainDescription, ChainId, ChannelName, Destination, GenericApplicationId, Owner, + StreamName, + }, }; pub type JSONObject = serde_json::Value; @@ -130,7 +133,7 @@ pub struct Transfer; #[cfg(not(target_arch = "wasm32"))] mod from { use linera_chain::data_types::{ - BlockExecutionOutcome, ExecutedBlock, HashedCertificateValue, IncomingMessage, + BlockExecutionOutcome, EventRecord, ExecutedBlock, HashedCertificateValue, IncomingMessage, OutgoingMessage, }; @@ -210,6 +213,7 @@ mod from { messages, state_hash, oracle_responses, + events, }, } = val; let messages = messages @@ -222,12 +226,25 @@ mod from { messages, state_hash, oracle_responses: oracle_responses.into_iter().map(Into::into).collect(), - events: vec![], // events.into_iter().map(Into::into).collect(), + events: events + .into_iter() + .map(|events| events.into_iter().map(Into::into).collect()) + .collect(), }, } } } + impl From for EventRecord { + fn from(event: block::BlockBlockValueExecutedBlockOutcomeEvents) -> Self { + EventRecord { + application_id: event.application_id, + stream_name: event.stream_name, + payload: event.payload.into_iter().map(|byte| byte as u8).collect(), + } + } + } + impl TryFrom for HashedCertificateValue { type Error = String; fn try_from(val: block::BlockBlock) -> Result {