Skip to content

Commit

Permalink
PublishBlob
Browse files Browse the repository at this point in the history
  • Loading branch information
ndr-ds committed May 21, 2024
1 parent 7694c9e commit a5ab0fe
Show file tree
Hide file tree
Showing 38 changed files with 1,212 additions and 284 deletions.
15 changes: 15 additions & 0 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This document contains the help content for the `linera` command-line program.
* [`linera service`](#linera-service)
* [`linera faucet`](#linera-faucet)
* [`linera publish-bytecode`](#linera-publish-bytecode)
* [`linera publish-blob`](#linera-publish-blob)
* [`linera create-application`](#linera-create-application)
* [`linera publish-and-create`](#linera-publish-and-create)
* [`linera request-application`](#linera-request-application)
Expand Down Expand Up @@ -77,6 +78,7 @@ A Byzantine-fault tolerant sidechain with low-latency finality and high throughp
* `service` — Run a GraphQL service to explore and extend the chains of the wallet
* `faucet` — Run a GraphQL service that exposes a faucet where users can claim tokens. This gives away the chain's tokens, and is mainly intended for testing
* `publish-bytecode` — Publish bytecode
* `publish-blob` — Publish a blob of binary data
* `create-application` — Create an application
* `publish-and-create` — Create an application, and publish the required bytecode
* `request-application` — Request an application from another chain, so it can be used on this one
Expand Down Expand Up @@ -553,6 +555,19 @@ Publish bytecode



## `linera publish-blob`

Publish a blob of binary data

**Usage:** `linera publish-blob <BLOB_PATH> [PUBLISHER]`

###### **Arguments:**

* `<BLOB_PATH>` — Path to blob file to be published
* `<PUBLISHER>` — An optional chain ID to publish the blob. The default chain of the wallet is used otherwise



## `linera create-application`

Create an application
Expand Down
20 changes: 10 additions & 10 deletions examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 79 additions & 2 deletions linera-base/src/data_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ use anyhow::Context as _;
use async_graphql::SimpleObject;
use base64::engine::{general_purpose::STANDARD_NO_PAD, Engine as _};
use linera_witty::{WitLoad, WitStore, WitType};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use thiserror::Error;

use crate::{
crypto::BcsHashable,
doc_scalar,
identifiers::{ApplicationId, Destination, GenericApplicationId},
identifiers::{ApplicationId, BlobId, Destination, GenericApplicationId},
time::{Duration, SystemTime},
};

Expand Down Expand Up @@ -733,6 +734,77 @@ impl std::str::FromStr for OracleResponse {
}
}

/// A blob of binary data.
#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
pub struct Blob {
#[serde(with = "serde_bytes")]
bytes: Vec<u8>,
}

impl Blob {
/// Creates a `HashedBlob` without checking that this is the correct `BlobId`!
pub fn with_hash_unchecked(self, blob_id: BlobId) -> HashedBlob {
HashedBlob {
id: blob_id,
blob: self,
}
}

/// Get the `HashedBlob` from the `Blob`.
pub fn into_hashed(self) -> HashedBlob {
let id = BlobId::new(&self);
HashedBlob { blob: self, id }
}
}

impl BcsHashable for Blob {}

impl From<HashedBlob> for Blob {
fn from(blob: HashedBlob) -> Blob {
blob.blob
}
}

/// A blob of binary data, with its content-addressed blob ID
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub struct HashedBlob {
id: BlobId,
blob: Blob,
}

impl HashedBlob {
/// Loads a hashed blob from a file.
pub async fn load_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
let blob = Blob {
bytes: std::fs::read(path)?,
};
Ok(blob.into_hashed())
}

/// A content-addressed blob ID i.e. the hash of the `Blob`.
pub fn id(&self) -> BlobId {
self.id
}
}

impl Serialize for HashedBlob {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.blob.serialize(serializer)
}
}

impl<'a> Deserialize<'a> for HashedBlob {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
Ok(Blob::deserialize(deserializer)?.into_hashed())
}
}

doc_scalar!(Amount, "A non-negative amount of tokens.");
doc_scalar!(BlockHeight, "A block height to identify blocks in a chain");
doc_scalar!(
Expand All @@ -744,6 +816,11 @@ doc_scalar!(
"A number to identify successive attempts to decide a value in a consensus protocol."
);
doc_scalar!(OracleResponse, "A record of a single oracle response.");
doc_scalar!(Blob, "A blob of binary data.");
doc_scalar!(
HashedBlob,
"A blob of binary data, with its content-addressed blob ID."
);

#[cfg(test)]
mod tests {
Expand Down
31 changes: 19 additions & 12 deletions linera-base/src/identifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use serde::{Deserialize, Serialize};

use crate::{
bcs_scalar,
crypto::{BcsHashable, BcsSignable, CryptoError, CryptoHash, PublicKey},
data_types::BlockHeight,
crypto::{BcsHashable, CryptoError, CryptoHash, PublicKey},
data_types::{Blob, BlockHeight},
doc_scalar,
};

Expand Down Expand Up @@ -78,7 +78,7 @@ impl Account {
}
}

impl std::fmt::Display for Account {
impl Display for Account {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.owner {
Some(owner) => write!(f, "{}:{}", self.chain_id, owner),
Expand Down Expand Up @@ -142,19 +142,23 @@ impl ChainDescription {
#[cfg_attr(with_testing, derive(Default))]
pub struct ChainId(pub CryptoHash);

/// A blob ID.
/// A content-addressed blob ID i.e. the hash of the Blob.
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Debug, Serialize, Deserialize)]
#[cfg_attr(with_testing, derive(Default))]
#[cfg_attr(with_testing, derive(test_strategy::Arbitrary, Default))]
pub struct BlobId(pub CryptoHash);

/// A blob.
#[derive(Serialize, Deserialize)]
pub struct Blob {
#[serde(with = "serde_bytes")]
bytes: Vec<u8>,
impl BlobId {
/// Creates a new `BlobId` from a `Blob`
pub fn new(blob: &Blob) -> Self {
BlobId(CryptoHash::new(blob))
}
}

impl BcsSignable for Blob {}
impl Display for BlobId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}

/// The index of a message in a chain.
#[derive(
Expand Down Expand Up @@ -802,7 +806,10 @@ doc_scalar!(
);
doc_scalar!(AccountOwner, "An owner of an account.");
doc_scalar!(Account, "An account");
doc_scalar!(BlobId, "A blob id");
doc_scalar!(
BlobId,
"A content-addressed blob ID i.e. the hash of the Blob"
);

#[cfg(test)]
mod tests {
Expand Down
21 changes: 18 additions & 3 deletions linera-chain/src/data_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ use std::{
use async_graphql::{Object, SimpleObject};
use linera_base::{
crypto::{BcsHashable, BcsSignable, CryptoError, CryptoHash, KeyPair, PublicKey, Signature},
data_types::{Amount, BlockHeight, OracleRecord, Round, Timestamp},
data_types::{Amount, BlockHeight, HashedBlob, OracleRecord, Round, Timestamp},
doc_scalar, ensure,
identifiers::{
Account, ChainId, ChannelName, Destination, GenericApplicationId, MessageId, Owner,
Account, BlobId, ChainId, ChannelName, Destination, GenericApplicationId, MessageId, Owner,
},
};
use linera_execution::{
committee::{Committee, Epoch, ValidatorName},
BytecodeLocation, Message, MessageKind, Operation,
BytecodeLocation, Message, MessageKind, Operation, SystemOperation,
};
use serde::{de::Deserializer, Deserialize, Serialize};

Expand Down Expand Up @@ -78,6 +78,18 @@ impl Block {
locations
}

/// Returns all the blob IDs referred to in this block's operations.
pub fn blob_ids(&self) -> HashSet<BlobId> {
let mut blob_ids = HashSet::new();
for operation in &self.operations {
if let Operation::System(SystemOperation::PublishBlob { blob_id }) = operation {
blob_ids.insert(blob_id.to_owned());
}
}

blob_ids
}

/// Returns whether the block contains only rejected incoming messages, which
/// makes it admissible even on closed chains.
pub fn has_only_rejected_messages(&self) -> bool {
Expand Down Expand Up @@ -221,6 +233,7 @@ pub struct BlockProposal {
pub owner: Owner,
pub signature: Signature,
pub hashed_certificate_values: Vec<HashedCertificateValue>,
pub hashed_blobs: Vec<HashedBlob>,
pub validated: Option<Certificate>,
}

Expand Down Expand Up @@ -800,6 +813,7 @@ impl BlockProposal {
content: BlockAndRound,
secret: &KeyPair,
hashed_certificate_values: Vec<HashedCertificateValue>,
hashed_blobs: Vec<HashedBlob>,
validated: Option<Certificate>,
) -> Self {
let outcome = validated
Expand All @@ -818,6 +832,7 @@ impl BlockProposal {
owner: secret.public().into(),
signature,
hashed_certificate_values,
hashed_blobs,
validated,
}
}
Expand Down
16 changes: 14 additions & 2 deletions linera-chain/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ use std::collections::BTreeMap;

use linera_base::{
crypto::{KeyPair, PublicKey},
data_types::{ArithmeticError, BlockHeight, Round, Timestamp},
data_types::{ArithmeticError, BlockHeight, HashedBlob, Round, Timestamp},
doc_scalar, ensure,
identifiers::{ChainId, Owner},
identifiers::{BlobId, ChainId, Owner},
ownership::ChainOwnership,
};
use linera_execution::committee::Epoch;
Expand Down Expand Up @@ -134,6 +134,8 @@ pub struct ChainManager {
pub current_round: Round,
/// The owners that take over in fallback mode.
pub fallback_owners: BTreeMap<Owner, (PublicKey, u64)>,
/// The pending blobs
pub pending_blobs: BTreeMap<BlobId, HashedBlob>,
}

doc_scalar!(
Expand Down Expand Up @@ -202,6 +204,7 @@ impl ChainManager {
round_timeout,
current_round,
fallback_owners,
pending_blobs: BTreeMap::new(),
})
}

Expand Down Expand Up @@ -404,6 +407,11 @@ impl ChainManager {
self.locked = Some(validated.clone());
}
}

for hashed_blob in proposal.hashed_blobs {
self.pending_blobs.insert(hashed_blob.id(), hashed_blob);
}

if let Some(key_pair) = key_pair {
let BlockAndRound { block, round } = proposal.content;
let executed_block = outcome.with(block);
Expand Down Expand Up @@ -583,6 +591,8 @@ pub struct ChainManagerInfo {
pub leader: Option<Owner>,
/// The timestamp when the current round times out.
pub round_timeout: Option<Timestamp>,
/// The pending blobs
pub pending_blobs: BTreeMap<BlobId, HashedBlob>,
}

impl From<&ChainManager> for ChainManagerInfo {
Expand All @@ -600,6 +610,7 @@ impl From<&ChainManager> for ChainManagerInfo {
current_round,
leader: manager.round_leader(current_round).cloned(),
round_timeout: manager.round_timeout,
pending_blobs: BTreeMap::new(),
}
}
}
Expand All @@ -613,6 +624,7 @@ impl ChainManagerInfo {
.pending
.as_ref()
.map(|vote| Box::new(vote.value.clone()));
self.pending_blobs = manager.pending_blobs.clone();
}

/// Returns the highest known validated block certificate.
Expand Down
4 changes: 2 additions & 2 deletions linera-chain/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ impl BlockTestExt for Block {

fn into_proposal_with_round(self, key_pair: &KeyPair, round: Round) -> BlockProposal {
let content = BlockAndRound { block: self, round };
BlockProposal::new(content, key_pair, vec![], None)
BlockProposal::new(content, key_pair, vec![], vec![], None)
}

fn into_justified_proposal(
Expand All @@ -132,7 +132,7 @@ impl BlockTestExt for Block {
validated: Certificate,
) -> BlockProposal {
let content = BlockAndRound { block: self, round };
BlockProposal::new(content, key_pair, vec![], Some(validated))
BlockProposal::new(content, key_pair, vec![], vec![], Some(validated))
}
}

Expand Down
Loading

0 comments on commit a5ab0fe

Please sign in to comment.