diff --git a/examples/non-fungible/README.md b/examples/non-fungible/README.md index ed46a05c9f4..22a175b2fd0 100644 --- a/examples/non-fungible/README.md +++ b/examples/non-fungible/README.md @@ -86,6 +86,7 @@ linera service --port $PORT & - Navigate to `http://localhost:8080/`. - To publish a blob, run the mutation: + ```gql,uri=http://localhost:8080/ mutation { publishDataBlob( @@ -99,23 +100,22 @@ linera service --port $PORT & - Navigate to `http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID`. - To mint an NFT, run the mutation: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID mutation { mint( minter: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f", name: "nft1", - blobId: { - hash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326", - blob_type: "Data" - } + blobHash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326", ) } ``` - To check that it's there, run the query: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID query { - nft(tokenId: "o66hIR1qQ8oUoTZTKHK35Cg1ObRgYSpBJnnS2gyvGBQ") { + nft(tokenId: "iQe01ZJeKo8E7HPr+MfwFedBZMdZ2v3UDI0F0kHMY+A") { tokenId, owner, name, @@ -126,6 +126,7 @@ linera service --port $PORT & ``` - To check that it's assigned to the owner, run the query: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID query { ownedNfts(owner: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f") @@ -133,6 +134,7 @@ linera service --port $PORT & ``` - To check everything that it's there, run the query: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID query { nfts @@ -140,11 +142,12 @@ linera service --port $PORT & ``` - To transfer the NFT to user `$OWNER_2`, still on chain `$CHAIN_1`, run the mutation: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID mutation { transfer( sourceOwner: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f", - tokenId: "o66hIR1qQ8oUoTZTKHK35Cg1ObRgYSpBJnnS2gyvGBQ", + tokenId: "iQe01ZJeKo8E7HPr+MfwFedBZMdZ2v3UDI0F0kHMY+A", targetAccount: { chainId: "e476187f6ddfeb9d588c7b45d3df334d5501d6499b3f9ad5595cae86cce16a65", owner: "User:598d18f67709fe76ed6a36b75a7c9889012d30b896800dfd027ee10e1afd49a3" diff --git a/examples/non-fungible/src/contract.rs b/examples/non-fungible/src/contract.rs index af558e0144d..6fc126f5759 100644 --- a/examples/non-fungible/src/contract.rs +++ b/examples/non-fungible/src/contract.rs @@ -9,9 +9,9 @@ use std::collections::BTreeSet; use fungible::Account; use linera_sdk::{ - base::{AccountOwner, BlobId, WithContractAbi}, + base::{AccountOwner, WithContractAbi}, views::{RootView, View, ViewStorageContext}, - Contract, ContractRuntime, + Contract, ContractRuntime, DataBlobHash, }; use non_fungible::{Message, Nft, NonFungibleTokenAbi, Operation, TokenId}; @@ -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 { @@ -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: DataBlobHash) { + 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"); @@ -193,7 +193,7 @@ impl NonFungibleTokenContract { owner, name, minter: owner, - blob_id, + blob_hash, }) .await; diff --git a/examples/non-fungible/src/lib.rs b/examples/non-fungible/src/lib.rs index a59febfed71..ab3428d563a 100644 --- a/examples/non-fungible/src/lib.rs +++ b/examples/non-fungible/src/lib.rs @@ -90,6 +90,7 @@ linera service --port $PORT & - Navigate to `http://localhost:8080/`. - To publish a blob, run the mutation: + ```gql,uri=http://localhost:8080/ mutation { publishDataBlob( @@ -103,23 +104,22 @@ linera service --port $PORT & - Navigate to `http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID`. - To mint an NFT, run the mutation: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID mutation { mint( minter: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f", name: "nft1", - blobId: { - hash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326", - blob_type: "Data" - } + blobHash: "34a20da0fdd7e24ddbff60cb7f952b053b3f0e196e622d47c3a368f690f01326", ) } ``` - To check that it's there, run the query: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID query { - nft(tokenId: "o66hIR1qQ8oUoTZTKHK35Cg1ObRgYSpBJnnS2gyvGBQ") { + nft(tokenId: "iQe01ZJeKo8E7HPr+MfwFedBZMdZ2v3UDI0F0kHMY+A") { tokenId, owner, name, @@ -130,6 +130,7 @@ linera service --port $PORT & ``` - To check that it's assigned to the owner, run the query: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID query { ownedNfts(owner: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f") @@ -137,6 +138,7 @@ linera service --port $PORT & ``` - To check everything that it's there, run the query: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID query { nfts @@ -144,11 +146,12 @@ linera service --port $PORT & ``` - To transfer the NFT to user `$OWNER_2`, still on chain `$CHAIN_1`, run the mutation: + ```gql,uri=http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID mutation { transfer( sourceOwner: "User:7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f", - tokenId: "o66hIR1qQ8oUoTZTKHK35Cg1ObRgYSpBJnnS2gyvGBQ", + tokenId: "iQe01ZJeKo8E7HPr+MfwFedBZMdZ2v3UDI0F0kHMY+A", targetAccount: { chainId: "e476187f6ddfeb9d588c7b45d3df334d5501d6499b3f9ad5595cae86cce16a65", owner: "User:598d18f67709fe76ed6a36b75a7c9889012d30b896800dfd027ee10e1afd49a3" @@ -182,9 +185,9 @@ 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, ServiceAbi}, graphql::GraphQLMutationRoot, - ToBcsBytes, + DataBlobHash, ToBcsBytes, }; use serde::{Deserialize, Serialize}; @@ -215,7 +218,7 @@ pub enum Operation { Mint { minter: AccountOwner, name: String, - blob_id: BlobId, + blob_hash: DataBlobHash, }, /// Transfers a token from a (locally owned) account to a (possibly remote) account. Transfer { @@ -255,7 +258,7 @@ pub struct Nft { pub owner: AccountOwner, pub name: String, pub minter: AccountOwner, - pub blob_id: BlobId, + pub blob_hash: DataBlobHash, } #[derive(Debug, Serialize, Deserialize, Clone, SimpleObject, PartialEq, Eq)] @@ -304,7 +307,7 @@ impl Nft { application_id: &ApplicationId, name: &String, minter: &AccountOwner, - blob_id: &BlobId, + blob_hash: &DataBlobHash, num_minted_nfts: u64, ) -> Result { use sha3::Digest as _; @@ -315,7 +318,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 { diff --git a/examples/non-fungible/src/service.rs b/examples/non-fungible/src/service.rs index 2f16549900e..1038800cc55 100644 --- a/examples/non-fungible/src/service.rs +++ b/examples/non-fungible/src/service.rs @@ -14,9 +14,9 @@ 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, WithServiceAbi}, views::{View, ViewStorageContext}, - Service, ServiceRuntime, + DataBlobHash, Service, ServiceRuntime, }; use non_fungible::{NftOutput, Operation, TokenId}; @@ -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) @@ -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); @@ -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); @@ -180,11 +180,11 @@ struct MutationRoot; #[Object] impl MutationRoot { - async fn mint(&self, minter: AccountOwner, name: String, blob_id: BlobId) -> Vec { + async fn mint(&self, minter: AccountOwner, name: String, blob_hash: DataBlobHash) -> Vec { bcs::to_bytes(&Operation::Mint { minter, name, - blob_id, + blob_hash, }) .unwrap() } diff --git a/examples/non-fungible/web-frontend/src/App.js b/examples/non-fungible/web-frontend/src/App.js index f62d4265365..066ad18f7f6 100644 --- a/examples/non-fungible/web-frontend/src/App.js +++ b/examples/non-fungible/web-frontend/src/App.js @@ -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) } `; @@ -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)); } @@ -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)); } diff --git a/linera-base/src/identifiers.rs b/linera-base/src/identifiers.rs index a45c2441840..d24cd77627b 100644 --- a/linera-base/src/identifiers.rs +++ b/linera-base/src/identifiers.rs @@ -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, } } diff --git a/linera-execution/src/lib.rs b/linera-execution/src/lib.rs index 5e932b91b89..ecdf3f68e5c 100644 --- a/linera-execution/src/lib.rs +++ b/linera-execution/src/lib.rs @@ -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, @@ -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; + /// Reads a data blob content specified by a given hash. + fn read_data_blob(&mut self, hash: &CryptoHash) -> Result, 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 { diff --git a/linera-execution/src/runtime.rs b/linera-execution/src/runtime.rs index 639f538ee2c..72cb716b9c4 100644 --- a/linera-execution/src/runtime.rs +++ b/linera-execution/src/runtime.rs @@ -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::{ @@ -712,12 +713,12 @@ impl BaseRuntime for SyncRuntimeHandle { self.inner().assert_before(timestamp) } - fn read_blob_content(&mut self, blob_id: &BlobId) -> Result { - self.inner().read_blob_content(blob_id) + fn read_data_blob(&mut self, hash: &CryptoHash) -> Result, 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) } } @@ -1017,42 +1018,38 @@ impl BaseRuntime for SyncRuntimeInternal { Ok(()) } - fn read_blob_content(&mut self, blob_id: &BlobId) -> Result { + fn read_data_blob(&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), } } 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(()) } } diff --git a/linera-execution/src/wasm/system_api.rs b/linera-execution/src/wasm/system_api.rs index 4e5742e753a..b9828c07257 100644 --- a/linera-execution/src/wasm/system_api.rs +++ b/linera-execution/src/wasm/system_api.rs @@ -4,12 +4,9 @@ use std::{any::Any, collections::HashMap, marker::PhantomData}; use linera_base::{ - data_types::{ - Amount, ApplicationPermissions, BlobContent, BlockHeight, SendMessageRequest, Timestamp, - }, - identifiers::{ - Account, ApplicationId, BlobId, ChainId, ChannelName, MessageId, Owner, StreamName, - }, + crypto::CryptoHash, + data_types::{Amount, ApplicationPermissions, BlockHeight, SendMessageRequest, Timestamp}, + identifiers::{Account, ApplicationId, ChainId, ChannelName, MessageId, Owner, StreamName}, ownership::{ChainOwnership, CloseChainError}, }; use linera_views::batch::{Batch, WriteOperation}; @@ -356,24 +353,21 @@ where .map_err(|error| RuntimeError::Custom(error.into())) } - /// Reads a blob content from storage. - fn read_blob_content( - caller: &mut Caller, - blob_id: BlobId, - ) -> Result { + /// Reads a data blob from storage. + fn read_data_blob(caller: &mut Caller, hash: CryptoHash) -> Result, RuntimeError> { caller .user_data_mut() .runtime - .read_blob_content(&blob_id) + .read_data_blob(&hash) .map_err(|error| RuntimeError::Custom(error.into())) } - /// Asserts the existence of a blob with the given `BlobId`. - fn assert_blob_exists(caller: &mut Caller, blob_id: BlobId) -> Result<(), RuntimeError> { + /// Asserts the existence of a data blob with the given hash. + fn assert_data_blob_exists(caller: &mut Caller, hash: CryptoHash) -> Result<(), RuntimeError> { caller .user_data_mut() .runtime - .assert_blob_exists(&blob_id) + .assert_data_blob_exists(&hash) .map_err(|error| RuntimeError::Custom(error.into())) } @@ -542,24 +536,21 @@ where .map_err(|error| RuntimeError::Custom(error.into())) } - /// Reads a blob content from storage. - fn read_blob_content( - caller: &mut Caller, - blob_id: BlobId, - ) -> Result { + /// Reads a data blob from storage. + fn read_data_blob(caller: &mut Caller, hash: CryptoHash) -> Result, RuntimeError> { caller .user_data_mut() .runtime - .read_blob_content(&blob_id) + .read_data_blob(&hash) .map_err(|error| RuntimeError::Custom(error.into())) } - /// Asserts the existence of a blob with the given `BlobId`. - fn assert_blob_exists(caller: &mut Caller, blob_id: BlobId) -> Result<(), RuntimeError> { + /// Asserts the existence of a data blob with the given hash. + fn assert_data_blob_exists(caller: &mut Caller, hash: CryptoHash) -> Result<(), RuntimeError> { caller .user_data_mut() .runtime - .assert_blob_exists(&blob_id) + .assert_data_blob_exists(&hash) .map_err(|error| RuntimeError::Custom(error.into())) } diff --git a/linera-sdk/src/contract/conversions_from_wit.rs b/linera-sdk/src/contract/conversions_from_wit.rs index 385dc6d4ad1..57d4f7088b8 100644 --- a/linera-sdk/src/contract/conversions_from_wit.rs +++ b/linera-sdk/src/contract/conversions_from_wit.rs @@ -5,8 +5,8 @@ use linera_base::{ crypto::{CryptoHash, PublicKey}, - data_types::{Amount, BlobContent, BlockHeight, TimeDelta, Timestamp}, - identifiers::{ApplicationId, BlobId, BlobType, BytecodeId, ChainId, MessageId, Owner}, + data_types::{Amount, BlockHeight, TimeDelta, Timestamp}, + identifiers::{ApplicationId, BytecodeId, ChainId, MessageId, Owner}, ownership::{ChainOwnership, CloseChainError, TimeoutConfig}, }; @@ -37,29 +37,6 @@ impl From for ApplicationId { } } -impl From for BlobId { - fn from(blob_id: wit_system_api::BlobId) -> Self { - Self { - hash: blob_id.hash.into(), - blob_type: blob_id.blob_type.into(), - } - } -} - -impl From for BlobType { - fn from(blob_type: wit_system_api::BlobType) -> Self { - match blob_type { - wit_system_api::BlobType::Data => BlobType::Data, - } - } -} - -impl From for BlobContent { - fn from(blob: wit_system_api::BlobContent) -> Self { - BlobContent { bytes: blob.bytes } - } -} - impl From for BytecodeId { fn from(bytecode_id: wit_system_api::BytecodeId) -> Self { BytecodeId::new(bytecode_id.message_id.into()) diff --git a/linera-sdk/src/contract/conversions_to_wit.rs b/linera-sdk/src/contract/conversions_to_wit.rs index 809ecd79ece..5a5b18fcea4 100644 --- a/linera-sdk/src/contract/conversions_to_wit.rs +++ b/linera-sdk/src/contract/conversions_to_wit.rs @@ -10,8 +10,8 @@ use linera_base::{ Timestamp, }, identifiers::{ - Account, ApplicationId, BlobId, BlobType, BytecodeId, ChainId, ChannelName, Destination, - MessageId, Owner, StreamName, + Account, ApplicationId, BytecodeId, ChainId, ChannelName, Destination, MessageId, Owner, + StreamName, }, ownership::{ChainOwnership, TimeoutConfig}, }; @@ -45,23 +45,6 @@ impl From for wit_system_api::Owner { } } -impl From for wit_system_api::BlobId { - fn from(blob_id: BlobId) -> Self { - wit_system_api::BlobId { - hash: blob_id.hash.into(), - blob_type: blob_id.blob_type.into(), - } - } -} - -impl From for wit_system_api::BlobType { - fn from(blob_id: BlobType) -> Self { - match blob_id { - BlobType::Data => wit_system_api::BlobType::Data, - } - } -} - impl From for wit_system_api::Amount { fn from(host: Amount) -> Self { wit_system_api::Amount { diff --git a/linera-sdk/src/contract/runtime.rs b/linera-sdk/src/contract/runtime.rs index e5af9301060..f45706a387f 100644 --- a/linera-sdk/src/contract/runtime.rs +++ b/linera-sdk/src/contract/runtime.rs @@ -6,19 +6,17 @@ use linera_base::{ abi::{ContractAbi, ServiceAbi}, data_types::{ - Amount, ApplicationPermissions, Blob, BlobContent, BlockHeight, Resources, - SendMessageRequest, Timestamp, + Amount, ApplicationPermissions, BlockHeight, Resources, SendMessageRequest, Timestamp, }, identifiers::{ - Account, ApplicationId, BlobId, ChainId, ChannelName, Destination, MessageId, Owner, - StreamName, + Account, ApplicationId, ChainId, ChannelName, Destination, MessageId, Owner, StreamName, }, ownership::{ChainOwnership, CloseChainError}, }; use serde::Serialize; use super::wit::contract_system_api as wit; -use crate::{Contract, KeyValueStore}; +use crate::{Contract, DataBlobHash, KeyValueStore}; /// The common runtime to interface with the host executing the contract. /// @@ -271,14 +269,14 @@ where wit::assert_before(timestamp.into()); } - /// Reads a blob with the given `BlobId` from storage. - pub fn read_blob(&mut self, blob_id: BlobId) -> Blob { - BlobContent::from(wit::read_blob_content(blob_id.into())).with_blob_id_unchecked(blob_id) + /// Reads a data blob with the given hash from storage. + pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec { + wit::read_data_blob(hash.0.into()) } - /// Asserts that a blob with the given `BlobId` exists in storage. - pub fn assert_blob_exists(&mut self, blob_id: BlobId) { - wit::assert_blob_exists(blob_id.into()) + /// Asserts that a data blob with the given hash exists in storage. + pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) { + wit::assert_data_blob_exists(hash.0.into()) } } diff --git a/linera-sdk/src/contract/test_runtime.rs b/linera-sdk/src/contract/test_runtime.rs index 36b5c4dd58a..73e0829b3d3 100644 --- a/linera-sdk/src/contract/test_runtime.rs +++ b/linera-sdk/src/contract/test_runtime.rs @@ -10,16 +10,15 @@ use std::{ use linera_base::{ abi::{ContractAbi, ServiceAbi}, - data_types::{Amount, Blob, BlockHeight, Resources, SendMessageRequest, Timestamp}, + data_types::{Amount, BlockHeight, Resources, SendMessageRequest, Timestamp}, identifiers::{ - Account, ApplicationId, BlobId, ChainId, ChannelName, Destination, MessageId, Owner, - StreamName, + Account, ApplicationId, ChainId, ChannelName, Destination, MessageId, Owner, StreamName, }, ownership::{ChainOwnership, CloseChainError}, }; use serde::Serialize; -use crate::{Contract, KeyValueStore}; +use crate::{Contract, DataBlobHash, KeyValueStore}; /// A mock of the common runtime to interface with the host executing the contract. pub struct MockContractRuntime @@ -48,8 +47,8 @@ where claim_requests: Vec, expected_service_queries: VecDeque<(ApplicationId, String, String)>, expected_post_requests: VecDeque<(String, Vec, Vec)>, - expected_read_blob_requests: VecDeque<(BlobId, Blob)>, - expected_contains_blob_requests: VecDeque<(BlobId, Option<()>)>, + expected_read_data_blob_requests: VecDeque<(DataBlobHash, Vec)>, + expected_assert_data_blob_exists_requests: VecDeque<(DataBlobHash, Option<()>)>, key_value_store: KeyValueStore, } @@ -91,8 +90,8 @@ where claim_requests: Vec::new(), expected_service_queries: VecDeque::new(), expected_post_requests: VecDeque::new(), - expected_read_blob_requests: VecDeque::new(), - expected_contains_blob_requests: VecDeque::new(), + expected_read_data_blob_requests: VecDeque::new(), + expected_assert_data_blob_exists_requests: VecDeque::new(), key_value_store: KeyValueStore::mock().to_mut(), } } @@ -615,16 +614,20 @@ where .push_back((url, payload, response)); } - /// Adds an expected `read_blob` call, and the response it should return in the test. - pub fn add_expected_read_blob_requests(&mut self, blob_id: BlobId, response: Blob) { - self.expected_read_blob_requests - .push_back((blob_id, response)); + /// Adds an expected `read_data_blob` call, and the response it should return in the test. + pub fn add_expected_read_data_blob_requests(&mut self, hash: DataBlobHash, response: Vec) { + self.expected_read_data_blob_requests + .push_back((hash, response)); } - /// Adds an expected `contains_blob` call, and the response it should return in the test. - pub fn add_expected_contains_blob_requests(&mut self, blob_id: BlobId, response: Option<()>) { - self.expected_contains_blob_requests - .push_back((blob_id, response)); + /// Adds an expected `assert_data_blob_exists` call, and the response it should return in the test. + pub fn add_expected_assert_data_blob_exists_requests( + &mut self, + hash: DataBlobHash, + response: Option<()>, + ) { + self.expected_assert_data_blob_exists_requests + .push_back((hash, response)); } /// Queries our application service as an oracle and returns the response. @@ -673,19 +676,20 @@ where assert!(self.timestamp.is_some_and(|t| t < timestamp)) } - /// Reads a blob with the given `BlobId` from storage. - pub fn read_blob(&mut self, blob_id: &BlobId) -> Blob { - let maybe_request = self.expected_read_blob_requests.pop_front(); - let (expected_blob_id, response) = maybe_request.expect("Unexpected read_blob request"); - assert_eq!(*blob_id, expected_blob_id); + /// Reads a data blob with the given hash from storage. + pub fn read_data_blob(&mut self, hash: &DataBlobHash) -> Vec { + let maybe_request = self.expected_read_data_blob_requests.pop_front(); + let (expected_hash, response) = maybe_request.expect("Unexpected read_data_blob request"); + assert_eq!(*hash, expected_hash); response } - /// Asserts that a blob with the given `BlobId` exists in storage. - pub fn assert_blob_exists(&mut self, blob_id: BlobId) { - let maybe_request = self.expected_contains_blob_requests.pop_front(); - let (expected_blob_id, response) = maybe_request.expect("Unexpected contains_blob request"); - assert_eq!(blob_id, expected_blob_id); + /// Asserts that a blob with the given hash exists in storage. + pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) { + let maybe_request = self.expected_assert_data_blob_exists_requests.pop_front(); + let (expected_blob_hash, response) = + maybe_request.expect("Unexpected assert_data_blob_exists request"); + assert_eq!(hash, expected_blob_hash); response.expect("Blob does not exist!"); } } diff --git a/linera-sdk/src/lib.rs b/linera-sdk/src/lib.rs index 668e40f322b..48a9aee82c5 100644 --- a/linera-sdk/src/lib.rs +++ b/linera-sdk/src/lib.rs @@ -45,13 +45,17 @@ pub mod views; use std::fmt::Debug; pub use bcs; -use linera_base::abi::{ContractAbi, ServiceAbi, WithContractAbi, WithServiceAbi}; pub use linera_base::{ abi, data_types::{Resources, SendMessageRequest}, ensure, }; -use serde::{de::DeserializeOwned, Serialize}; +use linera_base::{ + abi::{ContractAbi, ServiceAbi, WithContractAbi, WithServiceAbi}, + crypto::CryptoHash, + doc_scalar, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; pub use serde_json; #[doc(hidden)] @@ -64,6 +68,12 @@ pub use self::{ views::KeyValueStore, }; +/// Hash of a data blob. +#[derive(Eq, Hash, PartialEq, Debug, Serialize, Deserialize, Clone, Copy)] +pub struct DataBlobHash(pub CryptoHash); + +doc_scalar!(DataBlobHash, "Hash of a Data Blob"); + /// The contract interface of a Linera application. /// /// As opposed to the [`Service`] interface of an application, contract entry points diff --git a/linera-sdk/src/service/conversions_from_wit.rs b/linera-sdk/src/service/conversions_from_wit.rs index 8869500c9b8..f4ebc1506d7 100644 --- a/linera-sdk/src/service/conversions_from_wit.rs +++ b/linera-sdk/src/service/conversions_from_wit.rs @@ -5,8 +5,8 @@ use linera_base::{ crypto::CryptoHash, - data_types::{Amount, BlobContent, BlockHeight, Timestamp}, - identifiers::{ApplicationId, BlobId, BlobType, BytecodeId, ChainId, MessageId, Owner}, + data_types::{Amount, BlockHeight, Timestamp}, + identifiers::{ApplicationId, BytecodeId, ChainId, MessageId, Owner}, }; use super::wit::service_system_api as wit_system_api; @@ -23,29 +23,6 @@ impl From for Owner { } } -impl From for BlobId { - fn from(blob_id: wit_system_api::BlobId) -> Self { - Self { - hash: blob_id.hash.into(), - blob_type: blob_id.blob_type.into(), - } - } -} - -impl From for BlobType { - fn from(blob_type: wit_system_api::BlobType) -> Self { - match blob_type { - wit_system_api::BlobType::Data => BlobType::Data, - } - } -} - -impl From for BlobContent { - fn from(blob: wit_system_api::BlobContent) -> Self { - BlobContent { bytes: blob.bytes } - } -} - impl From for Amount { fn from(balance: wit_system_api::Amount) -> Self { let (lower_half, upper_half) = balance.inner0; diff --git a/linera-sdk/src/service/conversions_to_wit.rs b/linera-sdk/src/service/conversions_to_wit.rs index 5c655a95530..7221077c06b 100644 --- a/linera-sdk/src/service/conversions_to_wit.rs +++ b/linera-sdk/src/service/conversions_to_wit.rs @@ -6,7 +6,7 @@ use linera_base::{ crypto::CryptoHash, data_types::BlockHeight, - identifiers::{ApplicationId, BlobId, BlobType, BytecodeId, ChainId, MessageId, Owner}, + identifiers::{ApplicationId, BytecodeId, ChainId, MessageId, Owner}, }; use super::wit::service_system_api as wit_system_api; @@ -44,23 +44,6 @@ impl From for wit_system_api::Owner { } } -impl From for wit_system_api::BlobId { - fn from(blob_id: BlobId) -> Self { - wit_system_api::BlobId { - hash: blob_id.hash.into(), - blob_type: blob_id.blob_type.into(), - } - } -} - -impl From for wit_system_api::BlobType { - fn from(blob_type: BlobType) -> Self { - match blob_type { - BlobType::Data => wit_system_api::BlobType::Data, - } - } -} - impl From for wit_system_api::BlockHeight { fn from(block_height: BlockHeight) -> Self { wit_system_api::BlockHeight { diff --git a/linera-sdk/src/service/runtime.rs b/linera-sdk/src/service/runtime.rs index 16d37234d6e..078c0f27626 100644 --- a/linera-sdk/src/service/runtime.rs +++ b/linera-sdk/src/service/runtime.rs @@ -7,12 +7,12 @@ use std::cell::Cell; use linera_base::{ abi::ServiceAbi, - data_types::{Amount, Blob, BlobContent, BlockHeight, Timestamp}, - identifiers::{ApplicationId, BlobId, ChainId, Owner}, + data_types::{Amount, BlockHeight, Timestamp}, + identifiers::{ApplicationId, ChainId, Owner}, }; use super::wit::service_system_api as wit; -use crate::{KeyValueStore, Service}; +use crate::{DataBlobHash, KeyValueStore, Service}; /// The runtime available during execution of a query. pub struct ServiceRuntime @@ -145,13 +145,13 @@ where value } - /// Reads a blob with the given `BlobId` from storage. - pub fn read_blob(&mut self, blob_id: BlobId) -> Blob { - BlobContent::from(wit::read_blob_content(blob_id.into())).with_blob_id_unchecked(blob_id) + /// Reads a data blob with the given hash from storage. + pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec { + wit::read_data_blob(hash.0.into()) } - /// Asserts that a blob with the given `BlobId` exists in storage. - pub fn assert_blob_exists(&mut self, blob_id: BlobId) { - wit::assert_blob_exists(blob_id.into()) + /// Asserts that a data blob with the given hash exists in storage. + pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) { + wit::assert_data_blob_exists(hash.0.into()) } } diff --git a/linera-sdk/src/service/test_runtime.rs b/linera-sdk/src/service/test_runtime.rs index 1e22575808d..98555a10a25 100644 --- a/linera-sdk/src/service/test_runtime.rs +++ b/linera-sdk/src/service/test_runtime.rs @@ -10,11 +10,11 @@ use std::{ use linera_base::{ abi::ServiceAbi, - data_types::{Amount, Blob, BlockHeight, Timestamp}, - identifiers::{ApplicationId, BlobId, ChainId, Owner}, + data_types::{Amount, BlockHeight, Timestamp}, + identifiers::{ApplicationId, ChainId, Owner}, }; -use crate::{KeyValueStore, Service}; +use crate::{DataBlobHash, KeyValueStore, Service}; /// The runtime available during execution of a query. pub struct MockServiceRuntime @@ -30,7 +30,7 @@ where owner_balances: RefCell>>, query_application_handler: RefCell>, url_blobs: RefCell>>>, - blobs: RefCell>>, + blobs: RefCell>>>, key_value_store: KeyValueStore, } @@ -359,56 +359,56 @@ where }) } - /// Configures the blobs returned when fetching from `BlobId`s during the test. - pub fn with_blobs(self, blobs: impl IntoIterator) -> Self { + /// Configures the blobs returned when fetching from hashes during the test. + pub fn with_blobs(self, blobs: impl IntoIterator)>) -> Self { *self.blobs.borrow_mut() = Some(blobs.into_iter().collect()); self } - /// Configures the blobs returned when fetching from `BlobId`s during the test. - pub fn set_blobs(&self, blobs: impl IntoIterator) -> &Self { + /// Configures the blobs returned when fetching from hashes during the test. + pub fn set_blobs(&self, blobs: impl IntoIterator)>) -> &Self { *self.blobs.borrow_mut() = Some(blobs.into_iter().collect()); self } /// Configures the `Blob` returned when fetching from the `BlobId` during the test. - pub fn with_blob(self, blob_id: impl Into, blob: Blob) -> Self { - self.set_blob(blob_id, blob); + pub fn with_blob(self, hash: impl Into, blob: Vec) -> Self { + self.set_blob(hash, blob); self } - /// Configures the `Blob` returned when fetching from the `BlobId` during the test. - pub fn set_blob(&self, blob_id: impl Into, blob: Blob) -> &Self { + /// Configures the blob returned when fetching from the hash during the test. + pub fn set_blob(&self, hash: impl Into, blob: Vec) -> &Self { self.blobs .borrow_mut() .get_or_insert_with(HashMap::new) - .insert(blob_id.into(), blob); + .insert(hash.into(), blob); self } - /// Fetches a `Blob` from a given `BlobId`. - pub fn read_blob(&mut self, blob_id: BlobId) -> Blob { + /// Fetches a blob from a given hash. + pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec { self.blobs .borrow() .as_ref() - .and_then(|blobs| blobs.get(&blob_id).cloned()) + .and_then(|blobs| blobs.get(&hash).cloned()) .unwrap_or_else(|| { panic!( - "Blob for BlobId {blob_id:?} has not been mocked, \ + "Blob for hash {hash:?} has not been mocked, \ please call `MockServiceRuntime::set_blob` first" ) }) } - /// Asserts that a blob with the given `BlobId` exists in storage. - pub fn assert_blob_exists(&mut self, blob_id: BlobId) { + /// Asserts that a blob with the given hash exists in storage. + pub fn assert_blob_exists(&mut self, hash: DataBlobHash) { self.blobs .borrow() .as_ref() - .map(|blobs| blobs.contains_key(&blob_id)) + .map(|blobs| blobs.contains_key(&hash)) .unwrap_or_else(|| { panic!( - "Blob for BlobId {blob_id:?} has not been mocked, \ + "Blob for hash {hash:?} has not been mocked, \ please call `MockServiceRuntime::set_blob` first" ) }); diff --git a/linera-sdk/wit/contract-system-api.wit b/linera-sdk/wit/contract-system-api.wit index 6dc5cbc0868..5fac034eac3 100644 --- a/linera-sdk/wit/contract-system-api.wit +++ b/linera-sdk/wit/contract-system-api.wit @@ -25,8 +25,8 @@ interface contract-system-api { query-service: func(application-id: application-id, query: list) -> list; http-post: func(query: string, content-type: string, payload: list) -> list; assert-before: func(timestamp: timestamp); - read-blob-content: func(blob-id: blob-id) -> blob-content; - assert-blob-exists: func(blob-id: blob-id); + read-data-blob: func(hash: crypto-hash) -> list; + assert-data-blob-exists: func(hash: crypto-hash); log: func(message: string, level: log-level); consume-fuel: func(fuel: u64); @@ -50,19 +50,6 @@ interface contract-system-api { close-chain: list, } - record blob-content { - bytes: list, - } - - record blob-id { - hash: crypto-hash, - blob-type: blob-type, - } - - enum blob-type { - data, - } - record block-height { inner0: u64, } diff --git a/linera-sdk/wit/service-system-api.wit b/linera-sdk/wit/service-system-api.wit index abeae2d84e8..1650961bc06 100644 --- a/linera-sdk/wit/service-system-api.wit +++ b/linera-sdk/wit/service-system-api.wit @@ -14,8 +14,8 @@ interface service-system-api { fetch-url: func(url: string) -> list; query-service: func(application-id: application-id, query: list) -> list; http-post: func(query: string, content-type: string, payload: list) -> list; - read-blob-content: func(blob-id: blob-id) -> blob-content; - assert-blob-exists: func(blob-id: blob-id); + read-data-blob: func(hash: crypto-hash) -> list; + assert-data-blob-exists: func(hash: crypto-hash); assert-before: func(timestamp: timestamp); log: func(message: string, level: log-level); @@ -28,19 +28,6 @@ interface service-system-api { creation: message-id, } - record blob-content { - bytes: list, - } - - record blob-id { - hash: crypto-hash, - blob-type: blob-type, - } - - enum blob-type { - data, - } - record block-height { inner0: u64, } diff --git a/linera-service/tests/end_to_end_tests.rs b/linera-service/tests/end_to_end_tests.rs index ba2e250b180..17b81b500ab 100644 --- a/linera-service/tests/end_to_end_tests.rs +++ b/linera-service/tests/end_to_end_tests.rs @@ -21,8 +21,9 @@ use futures::{channel::mpsc, SinkExt, StreamExt}; use linera_base::{ command::resolve_binary, data_types::{Amount, Blob}, - identifiers::{Account, AccountOwner, ApplicationId, BlobId, ChainId}, + identifiers::{Account, AccountOwner, ApplicationId, ChainId}, }; +use linera_sdk::DataBlobHash; #[cfg(any( feature = "dynamodb", feature = "scylladb", @@ -212,7 +213,7 @@ impl NonFungibleApp { application_id: &ApplicationId, name: &String, minter: &AccountOwner, - blob_id: &BlobId, + hash: &DataBlobHash, num_minted_nfts: u64, ) -> String { use base64::engine::{general_purpose::STANDARD_NO_PAD, Engine as _}; @@ -221,7 +222,7 @@ impl NonFungibleApp { application_id, name, minter, - blob_id, + hash, num_minted_nfts, ) .expect("Creating token ID should not fail"); @@ -245,12 +246,12 @@ impl NonFungibleApp { )?) } - async fn mint(&self, minter: &AccountOwner, name: &String, blob_id: &BlobId) -> Value { + async fn mint(&self, minter: &AccountOwner, name: &String, blob_hash: &DataBlobHash) -> Value { let mutation = format!( - "mint(minter: {}, name: {}, blobId: {})", + "mint(minter: {}, name: {}, blobHash: {})", minter.to_value(), name.to_value(), - blob_id.to_value(), + blob_hash.to_value(), ); self.0.mutate(mutation).await.unwrap() } @@ -1046,16 +1047,19 @@ async fn test_wasm_end_to_end_non_fungible(config: impl LineraNetConfig) -> Resu .await?; assert_eq!(nft1_blob_id, blob_id); + let nft1_blob_hash = DataBlobHash(nft1_blob_id.hash); + let nft1_id = NonFungibleApp::create_token_id( &chain1, &application_id.forget_abi(), &nft1_name, &nft1_minter, - &nft1_blob_id, + &nft1_blob_hash, 0, // No NFTs are supposed to have been minted yet in this chain ); - app1.mint(&account_owner1, &nft1_name, &nft1_blob_id).await; + app1.mint(&account_owner1, &nft1_name, &nft1_blob_hash) + .await; let mut expected_nft1 = NftOutput { token_id: nft1_id.clone(), @@ -1173,17 +1177,20 @@ async fn test_wasm_end_to_end_non_fungible(config: impl LineraNetConfig) -> Resu .await?; assert_eq!(nft2_blob_id, blob_id); + let nft2_blob_hash = DataBlobHash(nft2_blob_id.hash); + let nft2_id = NonFungibleApp::create_token_id( &chain2, &application_id.forget_abi(), &nft2_name, &nft2_minter, - &nft2_blob_id, + &nft2_blob_hash, 0, // No NFTs are supposed to have been minted yet in this chain ); // Minting NFT from chain2 - app2.mint(&account_owner2, &nft2_name, &nft2_blob_id).await; + app2.mint(&account_owner2, &nft2_name, &nft2_blob_hash) + .await; let expected_nft2 = NftOutput { token_id: nft2_id.clone(),