Skip to content

Commit a3e6eb9

Browse files
Allow contract to create blobs (#4247)
## Motivation In order for the EVM to be able to create contracts, it needs to be able to create the corresponding data blobs. However, for publishing application, we need blobs that are not data blobs and so the `publish_module` is also added. Fixes #3707 ## Proposal The following was done: * Add the `create_data_blob` functionality to the runtime, thereby addressing the issue. * Add the `publish_bytecode` functionality to the runtime so that smart contracts can create modules. * The use of `fn compress` on the web forced the insertion of a relevant function using `ruzstd` and the corresponding version bump. ## Test Plan The CI, and 4 tests that have been introduced. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links None
1 parent 0331e73 commit a3e6eb9

File tree

12 files changed

+321
-27
lines changed

12 files changed

+321
-27
lines changed

Cargo.lock

Lines changed: 10 additions & 4 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ revm-state = { version = "4.0.1", default-features = false, features = [
202202
"serde",
203203
] }
204204
rocksdb = "0.21.0"
205-
ruzstd = "0.7.1"
205+
ruzstd = "0.8.1"
206206
scylla = "1.1.0"
207207
semver = "1.0.22"
208208
serde = { version = "1.0.197", features = ["derive"] }

examples/Cargo.lock

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

linera-base/src/data_types.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ impl ApplicationDescription {
10881088
}
10891089

10901090
/// A WebAssembly module's bytecode.
1091-
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
1091+
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitType, WitLoad, WitStore)]
10921092
pub struct Bytecode {
10931093
/// Bytes of the bytecode.
10941094
#[serde(with = "serde_bytes")]
@@ -1118,6 +1118,23 @@ impl Bytecode {
11181118

11191119
CompressedBytecode { compressed_bytes }
11201120
}
1121+
1122+
/// Compresses the [`Bytecode`] into a [`CompressedBytecode`].
1123+
#[cfg(target_arch = "wasm32")]
1124+
pub fn compress(&self) -> CompressedBytecode {
1125+
use ruzstd::encoding::{CompressionLevel, FrameCompressor};
1126+
1127+
#[cfg(with_metrics)]
1128+
let _compression_latency = metrics::BYTECODE_COMPRESSION_LATENCY.measure_latency();
1129+
1130+
let mut compressed_bytes = Vec::new();
1131+
let mut compressor = FrameCompressor::new(CompressionLevel::Fastest);
1132+
compressor.set_source(&*self.bytes);
1133+
compressor.set_drain(&mut compressed_bytes);
1134+
compressor.compress();
1135+
1136+
CompressedBytecode { compressed_bytes }
1137+
}
11211138
}
11221139

11231140
impl AsRef<[u8]> for Bytecode {
@@ -1180,10 +1197,10 @@ impl CompressedBytecode {
11801197
compressed_bytes: &[u8],
11811198
limit: u64,
11821199
) -> Result<bool, DecompressionError> {
1200+
use ruzstd::decoding::StreamingDecoder;
11831201
let limit = usize::try_from(limit).unwrap_or(usize::MAX);
11841202
let mut writer = LimitedWriter::new(io::sink(), limit);
1185-
let mut decoder = ruzstd::streaming_decoder::StreamingDecoder::new(compressed_bytes)
1186-
.map_err(io::Error::other)?;
1203+
let mut decoder = StreamingDecoder::new(compressed_bytes).map_err(io::Error::other)?;
11871204

11881205
// TODO(#2710): Decode multiple frames, if present
11891206
match io::copy(&mut decoder, &mut writer) {
@@ -1197,7 +1214,7 @@ impl CompressedBytecode {
11971214

11981215
/// Decompresses a [`CompressedBytecode`] into a [`Bytecode`].
11991216
pub fn decompress(&self) -> Result<Bytecode, DecompressionError> {
1200-
use ruzstd::{io::Read, streaming_decoder::StreamingDecoder};
1217+
use ruzstd::{decoding::StreamingDecoder, io::Read};
12011218

12021219
#[cfg(with_metrics)]
12031220
let _decompression_latency = BYTECODE_DECOMPRESSION_LATENCY.measure_latency();

linera-execution/src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ use linera_base::{
3636
crypto::{BcsHashable, CryptoHash},
3737
data_types::{
3838
Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
39-
DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate, Timestamp,
39+
Bytecode, DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate,
40+
Timestamp,
4041
},
4142
doc_scalar, hex_debug, http,
4243
identifiers::{
@@ -819,6 +820,17 @@ pub trait ContractRuntime: BaseRuntime {
819820
required_application_ids: Vec<ApplicationId>,
820821
) -> Result<ApplicationId, ExecutionError>;
821822

823+
/// Creates a new data blob and returns its ID.
824+
fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<BlobId, ExecutionError>;
825+
826+
/// Publishes a module with contract and service bytecode and returns the module ID.
827+
fn publish_module(
828+
&mut self,
829+
contract: Bytecode,
830+
service: Bytecode,
831+
vm_runtime: VmRuntime,
832+
) -> Result<ModuleId, ExecutionError>;
833+
822834
/// Returns the round in which this block was validated.
823835
fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
824836

linera-execution/src/runtime.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use custom_debug_derive::Debug;
1313
use linera_base::{
1414
crypto::CryptoHash,
1515
data_types::{
16-
Amount, ApplicationPermissions, ArithmeticError, BlockHeight, OracleResponse,
17-
SendMessageRequest, Timestamp,
16+
Amount, ApplicationPermissions, ArithmeticError, Blob, BlockHeight, Bytecode,
17+
OracleResponse, SendMessageRequest, Timestamp,
1818
},
1919
ensure, http,
2020
identifiers::{
@@ -1590,6 +1590,27 @@ impl ContractRuntime for ContractSyncRuntimeHandle {
15901590
Ok(app_id)
15911591
}
15921592

1593+
fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<BlobId, ExecutionError> {
1594+
let blob = Blob::new_data(bytes);
1595+
let blob_id = blob.id();
1596+
self.inner().transaction_tracker.add_created_blob(blob);
1597+
Ok(blob_id)
1598+
}
1599+
1600+
fn publish_module(
1601+
&mut self,
1602+
contract: Bytecode,
1603+
service: Bytecode,
1604+
vm_runtime: VmRuntime,
1605+
) -> Result<ModuleId, ExecutionError> {
1606+
let (blobs, module_id) =
1607+
crate::runtime::create_bytecode_blobs_sync(contract, service, vm_runtime);
1608+
for blob in blobs {
1609+
self.inner().transaction_tracker.add_created_blob(blob);
1610+
}
1611+
Ok(module_id)
1612+
}
1613+
15931614
fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError> {
15941615
let mut this = self.inner();
15951616
let round =
@@ -1828,3 +1849,32 @@ impl From<&MessageContext> for ExecutingMessage {
18281849
}
18291850
}
18301851
}
1852+
1853+
/// Creates a compressed contract and service bytecode synchronously.
1854+
pub fn create_bytecode_blobs_sync(
1855+
contract: Bytecode,
1856+
service: Bytecode,
1857+
vm_runtime: VmRuntime,
1858+
) -> (Vec<Blob>, ModuleId) {
1859+
match vm_runtime {
1860+
VmRuntime::Wasm => {
1861+
let compressed_contract = contract.compress();
1862+
let compressed_service = service.compress();
1863+
let contract_blob = Blob::new_contract_bytecode(compressed_contract);
1864+
let service_blob = Blob::new_service_bytecode(compressed_service);
1865+
let module_id =
1866+
ModuleId::new(contract_blob.id().hash, service_blob.id().hash, vm_runtime);
1867+
(vec![contract_blob, service_blob], module_id)
1868+
}
1869+
VmRuntime::Evm => {
1870+
let compressed_contract = contract.compress();
1871+
let evm_contract_blob = Blob::new_evm_bytecode(compressed_contract);
1872+
let module_id = ModuleId::new(
1873+
evm_contract_blob.id().hash,
1874+
evm_contract_blob.id().hash,
1875+
vm_runtime,
1876+
);
1877+
(vec![evm_contract_blob], module_id)
1878+
}
1879+
}
1880+
}

linera-execution/src/wasm/runtime_api.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use std::{any::Any, collections::HashMap, marker::PhantomData};
55

66
use linera_base::{
77
crypto::CryptoHash,
8-
data_types::{Amount, ApplicationPermissions, BlockHeight, SendMessageRequest, Timestamp},
8+
data_types::{
9+
Amount, ApplicationPermissions, BlockHeight, Bytecode, SendMessageRequest, Timestamp,
10+
},
911
http,
10-
identifiers::{Account, AccountOwner, ApplicationId, ChainId, MessageId, StreamName},
12+
identifiers::{Account, AccountOwner, ApplicationId, BlobId, ChainId, MessageId, StreamName},
1113
ownership::{ChainOwnership, ChangeApplicationPermissionsError, CloseChainError},
1214
vm::VmRuntime,
1315
};
@@ -531,6 +533,29 @@ where
531533
.map_err(|error| RuntimeError::Custom(error.into()))
532534
}
533535

536+
/// Creates a new data blob and returns its ID.
537+
fn create_data_blob(caller: &mut Caller, bytes: Vec<u8>) -> Result<BlobId, RuntimeError> {
538+
caller
539+
.user_data_mut()
540+
.runtime
541+
.create_data_blob(bytes)
542+
.map_err(|error| RuntimeError::Custom(error.into()))
543+
}
544+
545+
/// Publishes a module with contract and service bytecode and returns the module ID.
546+
fn publish_module(
547+
caller: &mut Caller,
548+
contract: Bytecode,
549+
service: Bytecode,
550+
vm_runtime: VmRuntime,
551+
) -> Result<ModuleId, RuntimeError> {
552+
caller
553+
.user_data_mut()
554+
.runtime
555+
.publish_module(contract, service, vm_runtime)
556+
.map_err(|error| RuntimeError::Custom(error.into()))
557+
}
558+
534559
/// Calls another application.
535560
fn try_call_application(
536561
caller: &mut Caller,

0 commit comments

Comments
 (0)