Skip to content

Commit c1d1ec4

Browse files
Resolve the blob persistence problem (#4356)
## Motivation Following PR #4247, PR #4278, and PR #3382, we face the scenario where a contract can create a blob but not have it available right away. This concerns the access to Blob Bytecode, Blob ApplicationDescription, and Blob Data. This PR addresses those problems. Fixes #4287 Fixes #4279 ## Proposal This PR addresses the problem with the data blob. But it also addresses the issues for the ApplicationDescription blob and the contract blobs. There are three scenarios to cover: * The blob is created in one block and read in another block. * The blob is created in one operation and read in the same operation. * The blob is created in two different operations of the same block. The first scenario is already working correctly. The second scenario is addressed with the addition of the test of `created_blobs` in `ReadBlobContent`. This is done in the same way as for access to the ApplicationDescription and the Contract/Service. The third scenario is addressed by the change in how the TransactionTracker is built in `block_tracker`. ## Test Plan The CI and the test in `test_wasm_end_to_end_publish_read_data_blob`, which considers all 3 scenarios. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links None
1 parent e348a95 commit c1d1ec4

File tree

23 files changed

+400
-49
lines changed

23 files changed

+400
-49
lines changed

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ matching-engine = { path = "./examples/matching-engine" }
313313
meta-counter = { path = "./examples/meta-counter" }
314314
native-fungible = { path = "./examples/native-fungible" }
315315
non-fungible = { path = "./examples/non-fungible" }
316+
publish-read-data-blob = { path = "./examples/publish-read-data-blob" }
316317
social = { path = "./examples/social" }
317318

318319
[workspace.dependencies.aws-config]

examples/Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ members = [
1717
"meta-counter",
1818
"native-fungible",
1919
"non-fungible",
20+
"publish-read-data-blob",
2021
"rfq",
2122
"social",
2223
]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "publish-read-data-blob"
3+
version = "0.1.0"
4+
authors = ["Linera <[email protected]>"]
5+
edition = "2021"
6+
7+
[dependencies]
8+
linera-sdk.workspace = true
9+
serde.workspace = true
10+
11+
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
12+
linera-sdk = { workspace = true, features = ["test", "wasmer"] }
13+
tokio = { workspace = true, features = ["rt", "sync"] }
14+
15+
[dev-dependencies]
16+
assert_matches.workspace = true
17+
linera-sdk = { workspace = true, features = ["test"] }
18+
19+
[[bin]]
20+
name = "publish_read_data_blob_contract"
21+
path = "src/contract.rs"
22+
23+
[[bin]]
24+
name = "publish_read_data_blob_service"
25+
path = "src/service.rs"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Publish and read a data blob
2+
3+
This example shows how to create a blob and how to read it.
4+
This example should be read in conjunction with the end-to-end
5+
test `test_wasm_end_to_end_publish_read_data_blob`.
6+
7+
It shows 3 scenarios:
8+
* Publishing and reading blobs with the publishing and reading in different
9+
blocks.
10+
* Publishing and reading blobs in the same transaction.
11+
* Publishing and reqding blobs in the same block but not the same transaction
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![cfg_attr(target_arch = "wasm32", no_main)]
5+
6+
mod state;
7+
8+
use linera_sdk::{
9+
linera_base_types::{DataBlobHash, WithContractAbi},
10+
Contract, ContractRuntime,
11+
};
12+
use publish_read_data_blob::{Operation, PublishReadDataBlobAbi};
13+
14+
pub struct PublishReadDataBlobContract {
15+
runtime: ContractRuntime<Self>,
16+
}
17+
18+
linera_sdk::contract!(PublishReadDataBlobContract);
19+
20+
impl WithContractAbi for PublishReadDataBlobContract {
21+
type Abi = PublishReadDataBlobAbi;
22+
}
23+
24+
impl Contract for PublishReadDataBlobContract {
25+
type Message = ();
26+
type InstantiationArgument = ();
27+
type Parameters = ();
28+
type EventValue = ();
29+
30+
async fn load(runtime: ContractRuntime<Self>) -> Self {
31+
PublishReadDataBlobContract { runtime }
32+
}
33+
34+
async fn instantiate(&mut self, _argument: ()) {
35+
// Validate that the application parameters were configured correctly.
36+
self.runtime.application_parameters();
37+
}
38+
39+
async fn execute_operation(&mut self, operation: Operation) {
40+
match operation {
41+
Operation::CreateDataBlob(data) => {
42+
self.runtime.create_data_blob(data);
43+
}
44+
Operation::ReadDataBlob(hash, expected_data) => {
45+
let data = self.runtime.read_data_blob(hash);
46+
assert_eq!(
47+
data, expected_data,
48+
"Read data does not match expected data"
49+
);
50+
}
51+
Operation::CreateAndReadDataBlob(data) => {
52+
let hash: DataBlobHash = self.runtime.create_data_blob(data.clone());
53+
let data_read = self.runtime.read_data_blob(hash);
54+
assert_eq!(data_read, data);
55+
}
56+
}
57+
}
58+
59+
async fn execute_message(&mut self, _message: ()) {
60+
panic!("Publish-Read Data Blob application doesn't support any cross-chain messages");
61+
}
62+
63+
async fn store(self) {}
64+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/*! ABI of the Publish-Read Data Blob Example Application */
5+
6+
use linera_sdk::linera_base_types::{ContractAbi, DataBlobHash, ServiceAbi};
7+
use serde::{Deserialize, Serialize};
8+
9+
pub struct PublishReadDataBlobAbi;
10+
11+
#[derive(Debug, Deserialize, Serialize)]
12+
pub enum Operation {
13+
CreateDataBlob(Vec<u8>),
14+
ReadDataBlob(DataBlobHash, Vec<u8>),
15+
CreateAndReadDataBlob(Vec<u8>),
16+
}
17+
18+
#[derive(Debug, Deserialize, Serialize)]
19+
pub enum ServiceQuery {
20+
PublishDataBlob(Vec<u8>),
21+
ReadDataBlob(DataBlobHash, Vec<u8>),
22+
PublishAndCreateOneOperation(Vec<u8>),
23+
PublishAndCreateTwoOperations(Vec<u8>),
24+
}
25+
26+
impl ContractAbi for PublishReadDataBlobAbi {
27+
type Operation = Operation;
28+
type Response = ();
29+
}
30+
31+
impl ServiceAbi for PublishReadDataBlobAbi {
32+
type Query = ServiceQuery;
33+
type QueryResponse = Vec<u8>;
34+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![cfg_attr(target_arch = "wasm32", no_main)]
5+
6+
mod state;
7+
8+
use std::sync::Arc;
9+
10+
use linera_sdk::{
11+
linera_base_types::{BlobContent, CryptoHash, DataBlobHash, WithServiceAbi},
12+
Service, ServiceRuntime,
13+
};
14+
use publish_read_data_blob::{Operation, PublishReadDataBlobAbi, ServiceQuery};
15+
16+
pub struct PublishReadDataBlobService {
17+
runtime: Arc<ServiceRuntime<Self>>,
18+
}
19+
20+
linera_sdk::service!(PublishReadDataBlobService);
21+
22+
impl WithServiceAbi for PublishReadDataBlobService {
23+
type Abi = PublishReadDataBlobAbi;
24+
}
25+
26+
impl Service for PublishReadDataBlobService {
27+
type Parameters = ();
28+
29+
async fn new(runtime: ServiceRuntime<Self>) -> Self {
30+
PublishReadDataBlobService {
31+
runtime: Arc::new(runtime),
32+
}
33+
}
34+
35+
async fn handle_query(&self, query: ServiceQuery) -> Vec<u8> {
36+
match query {
37+
ServiceQuery::PublishDataBlob(data) => {
38+
self.runtime
39+
.schedule_operation(&Operation::CreateDataBlob(data));
40+
Vec::new()
41+
}
42+
ServiceQuery::ReadDataBlob(hash, expected_data) => {
43+
self.runtime
44+
.schedule_operation(&Operation::ReadDataBlob(hash, expected_data));
45+
Vec::new()
46+
}
47+
ServiceQuery::PublishAndCreateOneOperation(data) => {
48+
self.runtime
49+
.schedule_operation(&Operation::CreateAndReadDataBlob(data));
50+
Vec::new()
51+
}
52+
ServiceQuery::PublishAndCreateTwoOperations(data) => {
53+
// First operation: create the blob
54+
self.runtime
55+
.schedule_operation(&Operation::CreateDataBlob(data.clone()));
56+
57+
// Compute the blob_id from the data
58+
let content = BlobContent::new_data(data.clone());
59+
let hash = DataBlobHash(CryptoHash::new(&content));
60+
61+
// Second operation: read the blob with verification
62+
self.runtime
63+
.schedule_operation(&Operation::ReadDataBlob(hash, data));
64+
Vec::new()
65+
}
66+
}
67+
}
68+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use linera_sdk::{
5+
linera_base_types::DataBlobHash,
6+
views::{linera_views, RegisterView, RootView, ViewStorageContext},
7+
};
8+
9+
#[derive(RootView)]
10+
#[view(context = ViewStorageContext)]
11+
pub struct PublishReadDataBlobState {
12+
pub hash: RegisterView<Option<DataBlobHash>>,
13+
}

0 commit comments

Comments
 (0)