-
Notifications
You must be signed in to change notification settings - Fork 167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create ApplicationDescription blob type #2612
Open
ndr-ds
wants to merge
2
commits into
graphite-base/2612
Choose a base branch
from
10-12-create_applicationdescription_blob_type
base: graphite-base/2612
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,11 +31,11 @@ use thiserror::Error; | |
#[cfg(with_metrics)] | ||
use crate::prometheus_util::{self, MeasureLatency}; | ||
use crate::{ | ||
crypto::BcsHashable, | ||
crypto::{BcsHashable, CryptoHash}, | ||
doc_scalar, hex_debug, | ||
identifiers::{ | ||
ApplicationId, BlobId, BlobType, BytecodeId, Destination, GenericApplicationId, MessageId, | ||
UserApplicationId, | ||
ApplicationId, BlobId, BlobType, BlobUserApplicationId, BytecodeId, ChainId, Destination, | ||
GenericApplicationId, MessageId, UserApplicationId, | ||
}, | ||
limited_writer::{LimitedWriter, LimitedWriterError}, | ||
time::{Duration, SystemTime}, | ||
|
@@ -805,6 +805,25 @@ pub struct UserApplicationDescription { | |
pub required_application_ids: Vec<UserApplicationId>, | ||
} | ||
|
||
/// 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 BlobUserApplicationDescription { | ||
/// The unique ID of the bytecode to use for the application. | ||
pub bytecode_id: BytecodeId, | ||
/// The chain ID that created the application. | ||
pub creator_chain_id: ChainId, | ||
/// Height of the block that created this application. | ||
pub block_height: BlockHeight, | ||
/// At what efffect index of the block this application was created. | ||
pub block_effect_index: u32, | ||
/// The parameters of the application. | ||
#[serde(with = "serde_bytes")] | ||
#[debug(with = "hex_debug")] | ||
pub parameters: Vec<u8>, | ||
/// Required dependencies. | ||
pub required_application_ids: Vec<BlobUserApplicationId>, | ||
} | ||
|
||
impl From<&UserApplicationDescription> for UserApplicationId { | ||
fn from(description: &UserApplicationDescription) -> Self { | ||
UserApplicationId { | ||
|
@@ -814,6 +833,29 @@ impl From<&UserApplicationDescription> for UserApplicationId { | |
} | ||
} | ||
|
||
impl From<&BlobUserApplicationDescription> for BlobUserApplicationId { | ||
fn from(description: &BlobUserApplicationDescription) -> Self { | ||
BlobUserApplicationId::new( | ||
CryptoHash::new(&description.blob_bytes()), | ||
description.bytecode_id, | ||
) | ||
} | ||
} | ||
|
||
impl BcsHashable for BlobUserApplicationDescription {} | ||
|
||
impl BlobUserApplicationDescription { | ||
/// Gets the `BlobBytes` for this `BlobUserApplicationDescription`. | ||
pub fn blob_bytes(&self) -> BlobBytes { | ||
BlobBytes(self.to_bytes()) | ||
} | ||
|
||
/// Gets the serialized bytes for this `BlobUserApplicationDescription`. | ||
pub fn to_bytes(&self) -> Vec<u8> { | ||
bcs::to_bytes(self).expect("Serializing blob bytes should not fail!") | ||
} | ||
} | ||
|
||
/// A WebAssembly module's bytecode. | ||
#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize)] | ||
pub struct Bytecode { | ||
|
@@ -973,6 +1015,8 @@ pub enum BlobContent { | |
ContractBytecode(CompressedBytecode), | ||
/// A blob containing service bytecode. | ||
ServiceBytecode(CompressedBytecode), | ||
/// A blob containing an application description. | ||
ApplicationDescription(BlobUserApplicationDescription), | ||
} | ||
|
||
impl fmt::Debug for BlobContent { | ||
|
@@ -981,6 +1025,13 @@ impl fmt::Debug for BlobContent { | |
BlobContent::Data(_) => write!(f, "BlobContent::Data"), | ||
BlobContent::ContractBytecode(_) => write!(f, "BlobContent::ContractBytecode"), | ||
BlobContent::ServiceBytecode(_) => write!(f, "BlobContent::ServiceBytecode"), | ||
BlobContent::ApplicationDescription(description) => f | ||
.debug_struct("BlobContent::ApplicationDescription") | ||
.field("bytecode_id", &description.bytecode_id) | ||
.field("creator_chain_id", &description.creator_chain_id) | ||
.field("block_height", &description.block_height) | ||
.field("block_effect_index", &description.block_effect_index) | ||
.finish_non_exhaustive(), | ||
} | ||
} | ||
} | ||
|
@@ -996,6 +1047,9 @@ impl BlobContent { | |
BlobType::ServiceBytecode => BlobContent::ServiceBytecode(CompressedBytecode { | ||
compressed_bytes: bytes, | ||
}), | ||
BlobType::ApplicationDescription => BlobContent::ApplicationDescription( | ||
bcs::from_bytes(&bytes).expect("Deserializing blob bytes should not fail!"), | ||
), | ||
} | ||
} | ||
|
||
|
@@ -1014,6 +1068,13 @@ impl BlobContent { | |
BlobContent::ServiceBytecode(compressed_bytecode) | ||
} | ||
|
||
/// Creates a new application description [`BlobContent`] from a [`BlobUserApplicationDescription`]. | ||
pub fn new_application_description( | ||
application_description: BlobUserApplicationDescription, | ||
) -> Self { | ||
BlobContent::ApplicationDescription(application_description) | ||
} | ||
|
||
/// Creates a `Blob` without checking that this is the correct `BlobId`. | ||
pub fn with_blob_id_unchecked(self, blob_id: BlobId) -> Blob { | ||
Blob { | ||
|
@@ -1032,6 +1093,11 @@ impl BlobContent { | |
BlobType::ServiceBytecode if matches!(&self, BlobContent::ServiceBytecode(_)) => { | ||
Some(()) | ||
} | ||
BlobType::ApplicationDescription | ||
if matches!(&self, BlobContent::ApplicationDescription(_)) => | ||
{ | ||
Some(()) | ||
} | ||
_ => None, | ||
}?; | ||
|
||
|
@@ -1047,13 +1113,20 @@ impl BlobContent { | |
/// Gets the inner blob's bytes. | ||
pub fn inner_bytes(&self) -> Vec<u8> { | ||
match self { | ||
BlobContent::Data(bytes) => bytes, | ||
BlobContent::ContractBytecode(compressed_bytecode) => { | ||
&compressed_bytecode.compressed_bytes | ||
} | ||
BlobContent::ServiceBytecode(compressed_bytecode) => { | ||
&compressed_bytecode.compressed_bytes | ||
BlobContent::Data(bytes) => bytes.clone(), | ||
BlobContent::ContractBytecode(compressed_bytecode) | ||
| BlobContent::ServiceBytecode(compressed_bytecode) => { | ||
compressed_bytecode.compressed_bytes.clone() | ||
} | ||
BlobContent::ApplicationDescription(description) => description.to_bytes(), | ||
} | ||
} | ||
|
||
/// Moves ownership of the blob's application description. If the `BlobContent` is of the wrong type, returns `None`. | ||
pub fn into_inner_application_description(self) -> Option<BlobUserApplicationDescription> { | ||
match self { | ||
BlobContent::ApplicationDescription(description) => Some(description), | ||
_ => None, | ||
} | ||
.clone() | ||
} | ||
|
@@ -1071,6 +1144,7 @@ impl BlobContent { | |
| BlobContent::ServiceBytecode(compressed_bytecode) => { | ||
compressed_bytecode.compressed_bytes.len() | ||
} | ||
BlobContent::ApplicationDescription(description) => description.to_bytes().len(), | ||
} | ||
} | ||
} | ||
|
@@ -1082,7 +1156,7 @@ impl From<Blob> for BlobContent { | |
} | ||
|
||
impl From<BlobContent> for Blob { | ||
fn from(content: BlobContent) -> Blob { | ||
fn from(content: BlobContent) -> Self { | ||
Self { | ||
id: BlobId::from_content(&content), | ||
content, | ||
|
@@ -1121,6 +1195,13 @@ impl Blob { | |
BlobContent::new_service_bytecode(compressed_bytecode).into() | ||
} | ||
|
||
/// Creates a new application description [`Blob`] from a [`BlobUserApplicationDescription`]. | ||
pub fn new_application_description( | ||
application_description: BlobUserApplicationDescription, | ||
) -> Self { | ||
BlobContent::new_application_description(application_description).into() | ||
} | ||
|
||
/// A content-addressed blob ID i.e. the hash of the `Blob`. | ||
pub fn id(&self) -> BlobId { | ||
self.id | ||
|
@@ -1141,8 +1222,13 @@ impl Blob { | |
self.content.inner_bytes() | ||
} | ||
|
||
/// Moves ownership of the blob's application description. If the `Blob` is of the wrong type, returns `None`. | ||
pub fn into_inner_application_description(self) -> Option<BlobUserApplicationDescription> { | ||
self.content.into_inner_application_description() | ||
} | ||
|
||
/// Loads data blob content from a file. | ||
pub async fn load_data_blob_from_file(path: impl AsRef<Path>) -> io::Result<Self> { | ||
pub async fn load_data_blob_from_file(path: impl AsRef<Path>) -> Result<Self, io::Error> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Why?) |
||
Ok(Self::new_data(fs::read(path)?)) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,6 +157,8 @@ pub enum BlobType { | |
ContractBytecode, | ||
/// A blob containing service bytecode. | ||
ServiceBytecode, | ||
/// A blob containing an application description. | ||
ApplicationDescription, | ||
} | ||
|
||
impl Display for BlobType { | ||
|
@@ -182,6 +184,7 @@ impl From<&BlobContent> for BlobType { | |
BlobContent::Data(_) => BlobType::Data, | ||
BlobContent::ContractBytecode(_) => BlobType::ContractBytecode, | ||
BlobContent::ServiceBytecode(_) => BlobType::ServiceBytecode, | ||
BlobContent::ApplicationDescription(_) => BlobType::ApplicationDescription, | ||
} | ||
} | ||
} | ||
|
@@ -310,10 +313,24 @@ pub struct ApplicationId<A = ()> { | |
pub creation: MessageId, | ||
} | ||
|
||
/// A unique identifier for a user application from a blob. | ||
#[derive(WitLoad, WitStore, WitType)] | ||
#[cfg_attr(with_testing, derive(Default))] | ||
pub struct BlobApplicationId<A = ()> { | ||
/// The hash of the `UserApplicationDescription` this refers to. | ||
pub application_description_hash: CryptoHash, | ||
/// The bytecode to use for the application. | ||
pub bytecode_id: BytecodeId<A>, | ||
} | ||
|
||
/// Alias for `ApplicationId`. Use this alias in the core | ||
/// protocol where the distinction with the more general enum `GenericApplicationId` matters. | ||
pub type UserApplicationId<A = ()> = ApplicationId<A>; | ||
|
||
/// Alias for `BlobApplicationId`. Use this alias in the core | ||
/// protocol where the distinction with the more general enum `GenericApplicationId` matters. | ||
pub type BlobUserApplicationId<A = ()> = BlobApplicationId<A>; | ||
|
||
/// A unique identifier for an application. | ||
#[derive( | ||
Eq, | ||
|
@@ -819,6 +836,140 @@ impl<A> ApplicationId<A> { | |
} | ||
} | ||
|
||
// Cannot use #[derive(Clone)] because it requires `A: Clone`. | ||
impl<A> Clone for BlobApplicationId<A> { | ||
fn clone(&self) -> Self { | ||
*self | ||
} | ||
} | ||
|
||
impl<A> Copy for BlobApplicationId<A> {} | ||
|
||
impl<A: PartialEq> PartialEq for BlobApplicationId<A> { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.application_description_hash == other.application_description_hash | ||
&& self.bytecode_id == other.bytecode_id | ||
} | ||
} | ||
|
||
impl<A: Eq> Eq for BlobApplicationId<A> {} | ||
|
||
impl<A: PartialOrd> PartialOrd for BlobApplicationId<A> { | ||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||
(self.application_description_hash, self.bytecode_id) | ||
.partial_cmp(&(other.application_description_hash, other.bytecode_id)) | ||
} | ||
} | ||
|
||
impl<A: Ord> Ord for BlobApplicationId<A> { | ||
fn cmp(&self, other: &Self) -> std::cmp::Ordering { | ||
(self.application_description_hash, self.bytecode_id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we destructure in these methods, the compiler will warn us in case we add any additional fields. |
||
.cmp(&(other.application_description_hash, other.bytecode_id)) | ||
} | ||
} | ||
|
||
impl<A> Hash for BlobApplicationId<A> { | ||
fn hash<H: Hasher>(&self, state: &mut H) { | ||
self.application_description_hash.hash(state); | ||
self.bytecode_id.hash(state); | ||
} | ||
} | ||
|
||
impl<A> Debug for BlobApplicationId<A> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("BlobApplicationId") | ||
.field( | ||
"application_description_hash", | ||
&self.application_description_hash, | ||
) | ||
.field("bytecode_id", &self.bytecode_id) | ||
.finish() | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize)] | ||
#[serde(rename = "BlobApplicationId")] | ||
struct SerializableBlobApplicationId { | ||
pub application_description_hash: CryptoHash, | ||
pub bytecode_id: BytecodeId, | ||
} | ||
|
||
impl<A> Serialize for BlobApplicationId<A> { | ||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: serde::ser::Serializer, | ||
{ | ||
if serializer.is_human_readable() { | ||
let bytes = bcs::to_bytes(&SerializableBlobApplicationId { | ||
application_description_hash: self.application_description_hash, | ||
bytecode_id: self.bytecode_id.forget_abi(), | ||
}) | ||
.map_err(serde::ser::Error::custom)?; | ||
serializer.serialize_str(&hex::encode(bytes)) | ||
} else { | ||
SerializableBlobApplicationId::serialize( | ||
&SerializableBlobApplicationId { | ||
application_description_hash: self.application_description_hash, | ||
bytecode_id: self.bytecode_id.forget_abi(), | ||
}, | ||
serializer, | ||
) | ||
} | ||
} | ||
} | ||
|
||
impl<'de, A> Deserialize<'de> for BlobApplicationId<A> { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: serde::de::Deserializer<'de>, | ||
{ | ||
if deserializer.is_human_readable() { | ||
let s = String::deserialize(deserializer)?; | ||
let application_id_bytes = hex::decode(s).map_err(serde::de::Error::custom)?; | ||
let application_id: SerializableBlobApplicationId = | ||
bcs::from_bytes(&application_id_bytes).map_err(serde::de::Error::custom)?; | ||
Ok(BlobApplicationId { | ||
application_description_hash: application_id.application_description_hash, | ||
bytecode_id: application_id.bytecode_id.with_abi(), | ||
}) | ||
} else { | ||
let value = SerializableBlobApplicationId::deserialize(deserializer)?; | ||
Ok(BlobApplicationId { | ||
application_description_hash: value.application_description_hash, | ||
bytecode_id: value.bytecode_id.with_abi(), | ||
}) | ||
} | ||
} | ||
} | ||
|
||
impl BlobApplicationId { | ||
/// Creates an application ID from the application description hash. | ||
pub fn new(application_description_hash: CryptoHash, bytecode_id: BytecodeId) -> Self { | ||
BlobApplicationId { | ||
application_description_hash, | ||
bytecode_id, | ||
} | ||
} | ||
|
||
/// Specializes an application ID for a given ABI. | ||
pub fn with_abi<A>(self) -> BlobApplicationId<A> { | ||
BlobApplicationId { | ||
application_description_hash: self.application_description_hash, | ||
bytecode_id: self.bytecode_id.with_abi(), | ||
} | ||
} | ||
} | ||
|
||
impl<A> BlobApplicationId<A> { | ||
/// Forgets the ABI of a bytecode ID (if any). | ||
pub fn forget_abi(self) -> BlobApplicationId { | ||
BlobApplicationId { | ||
application_description_hash: self.application_description_hash, | ||
bytecode_id: self.bytecode_id.forget_abi(), | ||
} | ||
} | ||
} | ||
|
||
impl Display for Owner { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { | ||
Display::fmt(&self.0, f) | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.