Skip to content

Commit

Permalink
Ensure only data blobs are present in SDK/System API calls
Browse files Browse the repository at this point in the history
  • Loading branch information
andresilva91 committed Aug 12, 2024
1 parent 560cbc3 commit 6786837
Show file tree
Hide file tree
Showing 22 changed files with 179 additions and 276 deletions.
5 changes: 1 addition & 4 deletions examples/non-fungible/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ linera service --port $PORT &
mint(
minter: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f",
name: "nft1",
blobId: {
hash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326",
blob_type: "Data"
}
blobHash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326",
)
}
```
Expand Down
14 changes: 7 additions & 7 deletions examples/non-fungible/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::collections::BTreeSet;

use fungible::Account;
use linera_sdk::{
base::{AccountOwner, BlobId, WithContractAbi},
base::{AccountOwner, CryptoHash, WithContractAbi},
views::{RootView, View, ViewStorageContext},
Contract, ContractRuntime,
};
Expand Down Expand Up @@ -51,10 +51,10 @@ impl Contract for NonFungibleTokenContract {
Operation::Mint {
minter,
name,
blob_id,
blob_hash,
} => {
self.check_account_authentication(minter);
self.mint(minter, name, blob_id).await;
self.mint(minter, name, blob_hash).await;
}

Operation::Transfer {
Expand Down Expand Up @@ -176,14 +176,14 @@ impl NonFungibleTokenContract {
.expect("NFT {token_id} not found")
}

async fn mint(&mut self, owner: AccountOwner, name: String, blob_id: BlobId) {
self.runtime.assert_blob_exists(blob_id);
async fn mint(&mut self, owner: AccountOwner, name: String, blob_hash: CryptoHash) {
self.runtime.assert_data_blob_exists(blob_hash);
let token_id = Nft::create_token_id(
&self.runtime.chain_id(),
&self.runtime.application_id().forget_abi(),
&name,
&owner,
&blob_id,
&blob_hash,
*self.state.num_minted_nfts.get(),
)
.expect("Failed to serialize NFT metadata");
Expand All @@ -193,7 +193,7 @@ impl NonFungibleTokenContract {
owner,
name,
minter: owner,
blob_id,
blob_hash,
})
.await;

Expand Down
15 changes: 6 additions & 9 deletions examples/non-fungible/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,7 @@ linera service --port $PORT &
mint(
minter: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f",
name: "nft1",
blobId: {
hash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326",
blob_type: "Data"
}
blobHash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326",
)
}
```
Expand Down Expand Up @@ -182,7 +179,7 @@ use std::fmt::{Display, Formatter};
use async_graphql::{InputObject, Request, Response, SimpleObject};
use fungible::Account;
use linera_sdk::{
base::{AccountOwner, ApplicationId, BlobId, ChainId, ContractAbi, ServiceAbi},
base::{AccountOwner, ApplicationId, ChainId, ContractAbi, CryptoHash, ServiceAbi},
graphql::GraphQLMutationRoot,
ToBcsBytes,
};
Expand Down Expand Up @@ -215,7 +212,7 @@ pub enum Operation {
Mint {
minter: AccountOwner,
name: String,
blob_id: BlobId,
blob_hash: CryptoHash,
},
/// Transfers a token from a (locally owned) account to a (possibly remote) account.
Transfer {
Expand Down Expand Up @@ -255,7 +252,7 @@ pub struct Nft {
pub owner: AccountOwner,
pub name: String,
pub minter: AccountOwner,
pub blob_id: BlobId,
pub blob_hash: CryptoHash,
}

#[derive(Debug, Serialize, Deserialize, Clone, SimpleObject, PartialEq, Eq)]
Expand Down Expand Up @@ -304,7 +301,7 @@ impl Nft {
application_id: &ApplicationId,
name: &String,
minter: &AccountOwner,
blob_id: &BlobId,
blob_hash: &CryptoHash,
num_minted_nfts: u64,
) -> Result<TokenId, bcs::Error> {
use sha3::Digest as _;
Expand All @@ -315,7 +312,7 @@ impl Nft {
hasher.update(name);
hasher.update(name.len().to_bcs_bytes()?);
hasher.update(minter.to_bcs_bytes()?);
hasher.update(blob_id.to_bcs_bytes()?);
hasher.update(blob_hash.to_bcs_bytes()?);
hasher.update(num_minted_nfts.to_bcs_bytes()?);

Ok(TokenId {
Expand Down
12 changes: 6 additions & 6 deletions examples/non-fungible/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use async_graphql::{EmptySubscription, Object, Request, Response, Schema};
use base64::engine::{general_purpose::STANDARD_NO_PAD, Engine as _};
use fungible::Account;
use linera_sdk::{
base::{AccountOwner, BlobId, WithServiceAbi},
base::{AccountOwner, CryptoHash, WithServiceAbi},
views::{View, ViewStorageContext},
Service, ServiceRuntime,
};
Expand Down Expand Up @@ -82,7 +82,7 @@ impl QueryRoot {
.runtime
.try_lock()
.expect("Services only run in a single thread");
runtime.read_blob(nft.blob_id).into_inner().bytes
runtime.read_data_blob(nft.blob_hash)
};
let nft_output = NftOutput::new_with_token_id(token_id, nft, payload);
Some(nft_output)
Expand All @@ -101,7 +101,7 @@ impl QueryRoot {
.runtime
.try_lock()
.expect("Services only run in a single thread");
runtime.read_blob(nft.blob_id).into_inner().bytes
runtime.read_data_blob(nft.blob_hash)
};
let nft_output = NftOutput::new(nft, payload);
nfts.insert(nft_output.token_id.clone(), nft_output);
Expand Down Expand Up @@ -166,7 +166,7 @@ impl QueryRoot {
.runtime
.try_lock()
.expect("Services only run in a single thread");
runtime.read_blob(nft.blob_id).into_inner().bytes
runtime.read_data_blob(nft.blob_hash)
};
let nft_output = NftOutput::new(nft, payload);
result.insert(nft_output.token_id.clone(), nft_output);
Expand All @@ -180,11 +180,11 @@ struct MutationRoot;

#[Object]
impl MutationRoot {
async fn mint(&self, minter: AccountOwner, name: String, blob_id: BlobId) -> Vec<u8> {
async fn mint(&self, minter: AccountOwner, name: String, blob_hash: CryptoHash) -> Vec<u8> {
bcs::to_bytes(&Operation::Mint {
minter,
name,
blob_id,
blob_hash,
})
.unwrap()
}
Expand Down
26 changes: 18 additions & 8 deletions examples/non-fungible/web-frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ const GET_OWNED_NFTS = gql`
`;

const MINT_NFT = gql`
mutation Mint($minter: AccountOwner!, $name: String!, $blobId: BlobId!) {
mint(minter: $minter, name: $name, blobId: $blobId)
mutation Mint(
$minter: AccountOwner!
$name: String!
$blobHash: CryptoHash!
) {
mint(minter: $minter, name: $name, blobHash: $blobHash)
}
`;

Expand Down Expand Up @@ -183,24 +187,28 @@ function App({ chainId, owner }) {
variables: {
chainId: chainId,
blobContent: {
bytes: Array.from(byteArrayFile)
bytes: Array.from(byteArrayFile),
},
},
}).then((r) => {
if ('errors' in r) {
console.log('Got error while publishing Data Blob: ' + JSON.stringify(r, null, 2));
console.log(
'Got error while publishing Data Blob: ' + JSON.stringify(r, null, 2)
);
} else {
console.log('Data Blob published: ' + JSON.stringify(r, null, 2));
const blobId = r['data']['publishDataBlob'];
const blobHash = r['data']['publishDataBlob']['hash'];
mintNft({
variables: {
minter: `User:${owner}`,
name: name,
blobId: blobId,
blobHash: blobHash,
},
}).then((r) => {
if ('errors' in r) {
console.log('Got error while minting NFT: ' + JSON.stringify(r, null, 2));
console.log(
'Got error while minting NFT: ' + JSON.stringify(r, null, 2)
);
} else {
console.log('NFT minted: ' + JSON.stringify(r, null, 2));
}
Expand Down Expand Up @@ -229,7 +237,9 @@ function App({ chainId, owner }) {
},
}).then((r) => {
if ('errors' in r) {
console.log('Error while transferring NFT: ' + JSON.stringify(r, null, 2));
console.log(
'Error while transferring NFT: ' + JSON.stringify(r, null, 2)
);
} else {
console.log('NFT transferred: ' + JSON.stringify(r, null, 2));
}
Expand Down
7 changes: 6 additions & 1 deletion linera-base/src/identifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,13 @@ pub struct BlobId {
impl BlobId {
/// Creates a new `BlobId` from a `BlobContent`
pub fn new_data(blob_content: &BlobContent) -> Self {
Self::new_data_from_hash(CryptoHash::new(blob_content))
}

/// Creates a new `BlobId` from a hash
pub fn new_data_from_hash(hash: CryptoHash) -> Self {
BlobId {
hash: CryptoHash::new(blob_content),
hash,
blob_type: BlobType::Data,
}
}
Expand Down
14 changes: 7 additions & 7 deletions linera-execution/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use linera_base::{
abi::Abi,
crypto::CryptoHash,
data_types::{
Amount, ApplicationPermissions, ArithmeticError, Blob, BlobContent, BlockHeight, Resources,
Amount, ApplicationPermissions, ArithmeticError, Blob, BlockHeight, Resources,
SendMessageRequest, Timestamp,
},
doc_scalar, hex_debug,
Expand Down Expand Up @@ -257,7 +257,7 @@ pub trait ExecutionRuntimeContext {
description: &UserApplicationDescription,
) -> Result<UserServiceCode, ExecutionError>;

async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError>;
async fn get_data_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError>;

async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
}
Expand Down Expand Up @@ -492,11 +492,11 @@ pub trait BaseRuntime {
/// owner, not a super owner.
fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;

/// Reads a blob content specified by a given `BlobId`.
fn read_blob_content(&mut self, blob_id: &BlobId) -> Result<BlobContent, ExecutionError>;
/// Reads a data blob content specified by a given hash.
fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError>;

/// Asserts the existence of a blob with the given `BlobId`.
fn assert_blob_exists(&mut self, blob_id: &BlobId) -> Result<(), ExecutionError>;
/// Asserts the existence of a data blob with the given hash.
fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError>;
}

pub trait ServiceRuntime: BaseRuntime {
Expand Down Expand Up @@ -918,7 +918,7 @@ impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
.clone())
}

async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError> {
async fn get_data_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError> {
Ok(self
.blobs
.get(&blob_id)
Expand Down
39 changes: 18 additions & 21 deletions linera-execution/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use std::{

use custom_debug_derive::Debug;
use linera_base::{
crypto::CryptoHash,
data_types::{
Amount, ApplicationPermissions, ArithmeticError, BlobContent, BlockHeight, OracleResponse,
Resources, SendMessageRequest, Timestamp,
Amount, ApplicationPermissions, ArithmeticError, BlockHeight, OracleResponse, Resources,
SendMessageRequest, Timestamp,
},
ensure,
identifiers::{
Expand Down Expand Up @@ -712,12 +713,12 @@ impl<UserInstance> BaseRuntime for SyncRuntimeHandle<UserInstance> {
self.inner().assert_before(timestamp)
}

fn read_blob_content(&mut self, blob_id: &BlobId) -> Result<BlobContent, ExecutionError> {
self.inner().read_blob_content(blob_id)
fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError> {
self.inner().read_data_blob(hash)
}

fn assert_blob_exists(&mut self, blob_id: &BlobId) -> Result<(), ExecutionError> {
self.inner().assert_blob_exists(blob_id)
fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError> {
self.inner().assert_data_blob_exists(hash)
}
}

Expand Down Expand Up @@ -1017,42 +1018,38 @@ impl<UserInstance> BaseRuntime for SyncRuntimeInternal<UserInstance> {
Ok(())
}

fn read_blob_content(&mut self, blob_id: &BlobId) -> Result<BlobContent, ExecutionError> {
fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError> {
let blob_id = BlobId::new_data_from_hash(*hash);
if let Some(responses) = &mut self.replaying_oracle_responses {
match responses.next() {
Some(OracleResponse::Blob(oracle_blob_id)) if oracle_blob_id == *blob_id => {}
Some(OracleResponse::Blob(oracle_blob_id)) if oracle_blob_id == blob_id => {}
Some(_) => return Err(ExecutionError::OracleResponseMismatch),
None => return Err(ExecutionError::MissingOracleResponse),
}
}
let blob_content = self
.execution_state_sender
.send_request(|callback| ExecutionRequest::ReadBlobContent {
blob_id: *blob_id,
callback,
})?
.send_request(|callback| ExecutionRequest::ReadBlobContent { blob_id, callback })?
.recv_response()?;
self.recorded_oracle_responses
.push(OracleResponse::Blob(*blob_id));
Ok(blob_content)
.push(OracleResponse::Blob(blob_id));
Ok(blob_content.bytes)
}

fn assert_blob_exists(&mut self, blob_id: &BlobId) -> Result<(), ExecutionError> {
fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError> {
let blob_id = BlobId::new_data_from_hash(*hash);
if let Some(responses) = &mut self.replaying_oracle_responses {
match responses.next() {
Some(OracleResponse::Blob(oracle_blob_id)) if oracle_blob_id == *blob_id => {}
Some(OracleResponse::Blob(oracle_blob_id)) if oracle_blob_id == blob_id => {}
Some(_) => return Err(ExecutionError::OracleResponseMismatch),
None => return Err(ExecutionError::MissingOracleResponse),
}
}
self.execution_state_sender
.send_request(|callback| ExecutionRequest::AssertBlobExists {
blob_id: *blob_id,
callback,
})?
.send_request(|callback| ExecutionRequest::AssertBlobExists { blob_id, callback })?
.recv_response()?;
self.recorded_oracle_responses
.push(OracleResponse::Blob(*blob_id));
.push(OracleResponse::Blob(blob_id));
Ok(())
}
}
Expand Down
2 changes: 1 addition & 1 deletion linera-execution/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,7 @@ where
) -> Result<BlobContent, SystemExecutionError> {
self.context()
.extra()
.get_blob(blob_id)
.get_data_blob(blob_id)
.await
.map_err(|_| SystemExecutionError::BlobNotFoundOnRead(blob_id))
.map(Into::into)
Expand Down
Loading

0 comments on commit 6786837

Please sign in to comment.