Skip to content

Commit a7c3228

Browse files
authored
Blob use is only an oracle the first time per chain. (#2972)
* Blob use is only an oracle the first time per chain. * Publishing is not an oracle call. * Update handle_certificates_to_create_application test. * blob_used and blob_published
1 parent 4cac5fb commit a7c3228

File tree

10 files changed

+99
-49
lines changed

10 files changed

+99
-49
lines changed

linera-chain/src/certificate/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl CertificateValueT for ValidatedBlock {
175175
}
176176

177177
fn required_blob_ids(&self) -> HashSet<BlobId> {
178-
self.inner().outcome.required_blob_ids().clone()
178+
self.inner().required_blob_ids()
179179
}
180180

181181
#[cfg(with_testing)]
@@ -198,7 +198,7 @@ impl CertificateValueT for ConfirmedBlock {
198198
}
199199

200200
fn required_blob_ids(&self) -> HashSet<BlobId> {
201-
self.executed_block().outcome.required_blob_ids().clone()
201+
self.executed_block().required_blob_ids()
202202
}
203203
}
204204

linera-chain/src/data_types.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -697,11 +697,14 @@ impl ExecutedBlock {
697697
}
698698

699699
pub fn required_blob_ids(&self) -> HashSet<BlobId> {
700-
self.outcome.required_blob_ids()
700+
let mut blob_ids = self.outcome.oracle_blob_ids();
701+
blob_ids.extend(self.block.published_blob_ids());
702+
blob_ids
701703
}
702704

703705
pub fn requires_blob(&self, blob_id: &BlobId) -> bool {
704-
self.required_blob_ids().contains(blob_id)
706+
self.outcome.oracle_blob_ids().contains(blob_id)
707+
|| self.block.published_blob_ids().contains(blob_id)
705708
}
706709
}
707710

@@ -713,7 +716,7 @@ impl BlockExecutionOutcome {
713716
}
714717
}
715718

716-
pub fn required_blob_ids(&self) -> HashSet<BlobId> {
719+
pub fn oracle_blob_ids(&self) -> HashSet<BlobId> {
717720
let mut required_blob_ids = HashSet::new();
718721
for responses in &self.oracle_responses {
719722
for response in responses {

linera-core/src/unit_tests/client_tests.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,7 +1435,8 @@ where
14351435
.await;
14361436
assert_matches!(
14371437
result,
1438-
Err(ChainClientError::RemoteNodeError(NodeError::BlobsNotFound(not_found_blob_ids))) if not_found_blob_ids == [blob0_id]
1438+
Err(ChainClientError::RemoteNodeError(NodeError::BlobsNotFound(not_found_blob_ids)))
1439+
if not_found_blob_ids == [blob0_id]
14391440
);
14401441

14411442
// Take one validator down
@@ -1456,16 +1457,17 @@ where
14561457
// But another one goes down
14571458
builder.set_fault_type([3], FaultType::Offline).await;
14581459

1459-
// Try to read the blob. This is a different client but on the same chain, so when we synchronize this with the validators
1460-
// before executing the block, we'll actually download and cache locally the blobs that were published by `client_a`.
1461-
// So this will succeed.
1460+
// Try to read the blob. This is a different client but on the same chain, so when we
1461+
// synchronize this with the validators before executing the block, we'll actually download
1462+
// and cache locally the blobs that were published by `client_a`. So this will succeed.
14621463
client1_b.prepare_chain().await?;
14631464
let certificate = client1_b
14641465
.execute_operation(SystemOperation::ReadBlob { blob_id: blob0_id }.into())
14651466
.await?
14661467
.unwrap();
14671468
assert_eq!(certificate.round, Round::MultiLeader(0));
1468-
assert!(certificate.executed_block().requires_blob(&blob0_id));
1469+
// The blob is not new on this chain, so it is not required.
1470+
assert!(!certificate.executed_block().requires_blob(&blob0_id));
14691471

14701472
builder
14711473
.set_fault_type([0, 1, 2], FaultType::DontSendConfirmVote)

linera-core/src/unit_tests/wasm_worker_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#![allow(clippy::large_futures)]
1111
#![cfg(any(feature = "wasmer", feature = "wasmtime"))]
1212

13+
use std::collections::BTreeSet;
14+
1315
use linera_base::{
1416
crypto::KeyPair,
1517
data_types::{
@@ -135,6 +137,7 @@ where
135137
committees: [(Epoch::ZERO, committee.clone())].into_iter().collect(),
136138
ownership: ChainOwnership::single(publisher_key_pair.public()),
137139
timestamp: Timestamp::from(1),
140+
used_blobs: BTreeSet::from([contract_blob_id, service_blob_id]),
138141
..SystemExecutionState::new(Epoch::ZERO, publisher_chain, admin_id)
139142
};
140143
let publisher_state_hash = publisher_system_state.clone().into_hash().await;
@@ -143,10 +146,7 @@ where
143146
messages: vec![Vec::new()],
144147
events: vec![Vec::new()],
145148
state_hash: publisher_state_hash,
146-
oracle_responses: vec![vec![
147-
OracleResponse::Blob(contract_blob_id),
148-
OracleResponse::Blob(service_blob_id),
149-
]],
149+
oracle_responses: vec![vec![]],
150150
}
151151
.with(publish_block),
152152
);

linera-core/src/worker.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,6 @@ pub enum WorkerError {
199199
MissingCertificateValue,
200200
#[error("The hash certificate doesn't match its value.")]
201201
InvalidLiteCertificate,
202-
#[error("An additional value was provided that is not required: {value_hash}.")]
203-
UnneededValue { value_hash: CryptoHash },
204202
#[error("An additional blob was provided that is not required: {blob_id}.")]
205203
UnneededBlob { blob_id: BlobId },
206204
#[error("The blobs provided in the proposal were not the published ones, in order.")]

linera-execution/src/execution.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ where
8787
.register_application(application_description)
8888
.await?;
8989

90+
self.system.used_blobs.insert(&contract_blob.id())?;
91+
self.system.used_blobs.insert(&service_blob.id())?;
92+
9093
self.context()
9194
.extra()
9295
.user_contracts()

linera-execution/src/execution_state_actor.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,12 +311,13 @@ where
311311

312312
ReadBlobContent { blob_id, callback } => {
313313
let blob = self.system.read_blob_content(blob_id).await?;
314-
callback.respond(blob);
314+
let is_new = self.system.blob_used(None, blob_id).await?;
315+
callback.respond((blob, is_new))
315316
}
316317

317318
AssertBlobExists { blob_id, callback } => {
318319
self.system.assert_blob_exists(blob_id).await?;
319-
callback.respond(())
320+
callback.respond(self.system.blob_used(None, blob_id).await?)
320321
}
321322
}
322323

@@ -480,12 +481,12 @@ pub enum ExecutionRequest {
480481
ReadBlobContent {
481482
blob_id: BlobId,
482483
#[debug(skip)]
483-
callback: Sender<BlobContent>,
484+
callback: Sender<(BlobContent, bool)>,
484485
},
485486

486487
AssertBlobExists {
487488
blob_id: BlobId,
488489
#[debug(skip)]
489-
callback: Sender<()>,
490+
callback: Sender<bool>,
490491
},
491492
}

linera-execution/src/runtime.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,22 +1024,27 @@ impl<UserInstance> BaseRuntime for SyncRuntimeInternal<UserInstance> {
10241024

10251025
fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError> {
10261026
let blob_id = BlobId::new(*hash, BlobType::Data);
1027-
self.transaction_tracker
1028-
.replay_oracle_response(OracleResponse::Blob(blob_id))?;
1029-
let blob_content = self
1027+
let (blob_content, is_new) = self
10301028
.execution_state_sender
10311029
.send_request(|callback| ExecutionRequest::ReadBlobContent { blob_id, callback })?
10321030
.recv_response()?;
1031+
if is_new {
1032+
self.transaction_tracker
1033+
.replay_oracle_response(OracleResponse::Blob(blob_id))?;
1034+
}
10331035
Ok(blob_content.inner_bytes())
10341036
}
10351037

10361038
fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError> {
10371039
let blob_id = BlobId::new(*hash, BlobType::Data);
1038-
self.transaction_tracker
1039-
.replay_oracle_response(OracleResponse::Blob(blob_id))?;
1040-
self.execution_state_sender
1040+
let is_new = self
1041+
.execution_state_sender
10411042
.send_request(|callback| ExecutionRequest::AssertBlobExists { blob_id, callback })?
10421043
.recv_response()?;
1044+
if is_new {
1045+
self.transaction_tracker
1046+
.replay_oracle_response(OracleResponse::Blob(blob_id))?;
1047+
}
10431048
Ok(())
10441049
}
10451050
}

linera-execution/src/system.rs

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ pub struct SystemExecutionStateView<C> {
9595
pub closed: HashedRegisterView<C, bool>,
9696
/// Permissions for applications on this chain.
9797
pub application_permissions: HashedRegisterView<C, ApplicationPermissions>,
98+
/// Blobs that have been used or published on this chain.
99+
pub used_blobs: HashedSetView<C, BlobId>,
98100
}
99101

100102
/// The configuration for a new chain.
@@ -609,14 +611,14 @@ where
609611
outcome.messages.push(message);
610612
}
611613
PublishBytecode { bytecode_id } => {
612-
txn_tracker.replay_oracle_response(OracleResponse::Blob(BlobId::new(
614+
self.blob_published(&BlobId::new(
613615
bytecode_id.contract_blob_hash,
614616
BlobType::ContractBytecode,
615-
)))?;
616-
txn_tracker.replay_oracle_response(OracleResponse::Blob(BlobId::new(
617+
))?;
618+
self.blob_published(&BlobId::new(
617619
bytecode_id.service_blob_hash,
618620
BlobType::ServiceBytecode,
619-
)))?;
621+
))?;
620622
}
621623
CreateApplication {
622624
bytecode_id,
@@ -664,14 +666,11 @@ where
664666
outcome.messages.push(message);
665667
}
666668
PublishDataBlob { blob_hash } => {
667-
txn_tracker.replay_oracle_response(OracleResponse::Blob(BlobId::new(
668-
blob_hash,
669-
BlobType::Data,
670-
)))?;
669+
self.blob_published(&BlobId::new(blob_hash, BlobType::Data))?;
671670
}
672671
ReadBlob { blob_id } => {
673-
txn_tracker.replay_oracle_response(OracleResponse::Blob(blob_id))?;
674672
self.read_blob_content(blob_id).await?;
673+
self.blob_used(Some(txn_tracker), blob_id).await?;
675674
}
676675
}
677676

@@ -1006,12 +1005,41 @@ where
10061005
Ok(messages)
10071006
}
10081007

1009-
pub async fn read_blob_content(&mut self, blob_id: BlobId) -> Result<BlobContent, ViewError> {
1010-
self.context()
1011-
.extra()
1012-
.get_blob(blob_id)
1013-
.await
1014-
.map(Into::into)
1008+
/// Records a blob that is used in this block. If this is the first use on this chain, creates
1009+
/// an oracle response for it.
1010+
pub(crate) async fn blob_used(
1011+
&mut self,
1012+
txn_tracker: Option<&mut TransactionTracker>,
1013+
blob_id: BlobId,
1014+
) -> Result<bool, SystemExecutionError> {
1015+
if self.used_blobs.contains(&blob_id).await? {
1016+
return Ok(false); // Nothing to do.
1017+
}
1018+
self.used_blobs.insert(&blob_id)?;
1019+
if let Some(txn_tracker) = txn_tracker {
1020+
txn_tracker.replay_oracle_response(OracleResponse::Blob(blob_id))?;
1021+
}
1022+
Ok(true)
1023+
}
1024+
1025+
/// Records a blob that is published in this block. This does not create an oracle entry, and
1026+
/// the blob can be used without using an oracle in the future on this chain.
1027+
fn blob_published(&mut self, blob_id: &BlobId) -> Result<(), SystemExecutionError> {
1028+
self.used_blobs.insert(blob_id)?;
1029+
Ok(())
1030+
}
1031+
1032+
pub async fn read_blob_content(
1033+
&mut self,
1034+
blob_id: BlobId,
1035+
) -> Result<BlobContent, SystemExecutionError> {
1036+
match self.context().extra().get_blob(blob_id).await {
1037+
Ok(blob) => Ok(blob.into()),
1038+
Err(ViewError::BlobsNotFound(_)) => {
1039+
Err(SystemExecutionError::BlobsNotFound(vec![blob_id]))
1040+
}
1041+
Err(error) => Err(error.into()),
1042+
}
10151043
}
10161044

10171045
pub async fn assert_blob_exists(
@@ -1054,12 +1082,14 @@ where
10541082
missing_blobs.push(service_bytecode_blob_id);
10551083
}
10561084

1057-
if missing_blobs.is_empty() {
1058-
txn_tracker.replay_oracle_response(OracleResponse::Blob(contract_bytecode_blob_id))?;
1059-
txn_tracker.replay_oracle_response(OracleResponse::Blob(service_bytecode_blob_id))?;
1060-
Ok(())
1061-
} else {
1062-
Err(SystemExecutionError::BlobsNotFound(missing_blobs))
1063-
}
1085+
ensure!(
1086+
missing_blobs.is_empty(),
1087+
SystemExecutionError::BlobsNotFound(missing_blobs)
1088+
);
1089+
self.blob_used(Some(txn_tracker), contract_bytecode_blob_id)
1090+
.await?;
1091+
self.blob_used(Some(txn_tracker), service_bytecode_blob_id)
1092+
.await?;
1093+
Ok(())
10641094
}
10651095
}

linera-execution/src/test_utils/system_execution_state.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use custom_debug_derive::Debug;
1111
use linera_base::{
1212
crypto::CryptoHash,
1313
data_types::{Amount, ApplicationPermissions, Blob, Timestamp},
14-
identifiers::{ApplicationId, ChainDescription, ChainId, Owner},
14+
identifiers::{ApplicationId, BlobId, ChainDescription, ChainId, Owner},
1515
ownership::ChainOwnership,
1616
};
1717
use linera_views::{
@@ -46,6 +46,7 @@ pub struct SystemExecutionState {
4646
pub balances: BTreeMap<Owner, Amount>,
4747
pub timestamp: Timestamp,
4848
pub registry: ApplicationRegistry,
49+
pub used_blobs: BTreeSet<BlobId>,
4950
#[debug(skip_if = Not::not)]
5051
pub closed: bool,
5152
pub application_permissions: ApplicationPermissions,
@@ -108,6 +109,7 @@ impl SystemExecutionState {
108109
balances,
109110
timestamp,
110111
registry,
112+
used_blobs,
111113
closed,
112114
application_permissions,
113115
extra_blobs,
@@ -160,6 +162,12 @@ impl SystemExecutionState {
160162
.registry
161163
.import(registry)
162164
.expect("serialization of registry components should not fail");
165+
for blob_id in used_blobs {
166+
view.system
167+
.used_blobs
168+
.insert(&blob_id)
169+
.expect("inserting blob IDs should not fail");
170+
}
163171
view.system.closed.set(closed);
164172
view.system
165173
.application_permissions

0 commit comments

Comments
 (0)