Skip to content

Commit

Permalink
Remove metadata from output endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
thibault-martinez committed Jun 26, 2023
1 parent 1535a10 commit bb05e3c
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 44 deletions.
14 changes: 6 additions & 8 deletions sdk/examples/client/output/nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,16 @@ async fn main() -> Result<()> {
let output_ids_response = client
.basic_output_ids([QueryParameter::Address(bech32_nft_address)])
.await?;
let output_with_meta = client.get_output(&output_ids_response.items[0]).await?;
let output = client.get_output(&output_ids_response.items[0]).await?;

let block = client
.block()
.with_secret_manager(&secret_manager)
.with_input(nft_output_id.into())?
.with_input(output_ids_response.items[0].into())?
.with_outputs([
NftOutputBuilder::new_with_amount(1_000_000 + output_with_meta.output().amount(), nft_id)
.add_unlock_condition(AddressUnlockCondition::new(bech32_nft_address))
.finish_output(token_supply)?,
])?
.with_outputs([NftOutputBuilder::new_with_amount(1_000_000 + output.amount(), nft_id)
.add_unlock_condition(AddressUnlockCondition::new(bech32_nft_address))
.finish_output(token_supply)?])?
.finish()
.await?;

Expand All @@ -113,8 +111,8 @@ async fn main() -> Result<()> {
//////////////////////////////////

let nft_output_id = get_nft_output_id(block.payload().unwrap())?;
let output_with_meta = client.get_output(&nft_output_id).await?;
let outputs = [BasicOutputBuilder::new_with_amount(output_with_meta.output().amount())
let output = client.get_output(&nft_output_id).await?;
let outputs = [BasicOutputBuilder::new_with_amount(output.amount())
.add_unlock_condition(AddressUnlockCondition::new(bech32_nft_address))
.finish_output(token_supply)?];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl<'a> ClientBlockBuilder<'a> {
.items,
);

self.client.get_outputs(&output_ids).await
self.client.get_outputs_with_metadata(&output_ids).await
}

/// Searches inputs for provided outputs, by requesting the outputs from the account addresses or for
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/client/api/block_builder/input_selection/manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl<'a> ClientBlockBuilder<'a> {

if let Some(inputs) = &self.inputs {
for input in inputs {
let output_with_meta = self.client.get_output(input.output_id()).await?;
let output_with_meta = self.client.get_output_with_metadata(input.output_id()).await?;

if !output_with_meta.metadata().is_spent() {
let alias_transition = is_alias_transition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl<'a> ClientBlockBuilder<'a> {
}
}) {
let output_id = self.client.alias_output_id(*alias_id).await?;
let output_with_meta = self.client.get_output(&output_id).await?;
let output_with_meta = self.client.get_output_with_metadata(&output_id).await?;
if let Output::Alias(alias_output) = output_with_meta.output() {
// State transition if we add them to inputs
let unlock_address = alias_output.state_controller_address();
Expand Down Expand Up @@ -156,7 +156,7 @@ impl<'a> ClientBlockBuilder<'a> {
}
}) {
let output_id = self.client.nft_output_id(*nft_id).await?;
let output_with_meta = self.client.get_output(&output_id).await?;
let output_with_meta = self.client.get_output_with_metadata(&output_id).await?;
if let Output::Nft(nft_output) = output_with_meta.output() {
let unlock_address = nft_output
.unlock_conditions()
Expand Down
10 changes: 5 additions & 5 deletions sdk/src/client/api/block_builder/input_selection/utxo_chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub(crate) async fn get_alias_and_nft_outputs_recursively(
match unlock_address {
Address::Alias(address) => {
let input_id = client.alias_output_id(*address.alias_id()).await?;
let input = client.get_output(&input_id).await?;
let input = client.get_output_with_metadata(&input_id).await?;
if let Output::Alias(alias_input) = input.output() {
// State transition if we add them to inputs
let alias_unlock_address = alias_input.state_controller_address();
Expand All @@ -72,7 +72,7 @@ pub(crate) async fn get_alias_and_nft_outputs_recursively(
}
Address::Nft(address) => {
let input_id = client.nft_output_id(*address.nft_id()).await?;
let input = client.get_output(&input_id).await?;
let input = client.get_output_with_metadata(&input_id).await?;
if let Output::Nft(nft_input) = input.output() {
let unlock_address = nft_input
.unlock_conditions()
Expand Down Expand Up @@ -120,7 +120,7 @@ impl<'a> ClientBlockBuilder<'a> {
// Check if the transaction is a governance_transition, by checking if the new index is the same
// as the previous index
let output_id = client.alias_output_id(*alias_output.alias_id()).await?;
let input = client.get_output(&output_id).await?;
let input = client.get_output_with_metadata(&output_id).await?;
if let Output::Alias(alias_input) = input.output() {
// A governance transition is identified by an unchanged State Index in next
// state.
Expand All @@ -136,7 +136,7 @@ impl<'a> ClientBlockBuilder<'a> {
// If the id is null then this output creates it and we can't have a previous output
if !nft_output.nft_id().is_null() {
let output_id = client.nft_output_id(*nft_output.nft_id()).await?;
let input = client.get_output(&output_id).await?;
let input = client.get_output_with_metadata(&output_id).await?;
if let Output::Nft(nft_input) = input.output() {
let unlock_address = nft_input
.unlock_conditions()
Expand All @@ -149,7 +149,7 @@ impl<'a> ClientBlockBuilder<'a> {
Output::Foundry(foundry_output) => {
// if it's the first foundry output, then we can't have it as input
if let Ok(output_id) = client.foundry_output_id(foundry_output.id()).await {
let input = client.get_output(&output_id).await?;
let input = client.get_output_with_metadata(&output_id).await?;
if let Output::Foundry(foundry_input_output) = input.output() {
utxo_chains.push((Address::Alias(*foundry_input_output.alias_address()), input));
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/client/api/consolidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Client {
])
.await?;

let basic_outputs_responses = self.get_outputs(&output_ids_response.items).await?;
let basic_outputs_responses = self.get_outputs_with_metadata(&output_ids_response.items).await?;

if !basic_outputs_responses.is_empty() {
// If we reach the same index again
Expand Down
8 changes: 4 additions & 4 deletions sdk/src/client/api/high_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl Client {
})
.collect::<Vec<_>>();

self.get_outputs(&input_ids).await
self.get_outputs_with_metadata(&input_ids).await
}

/// A generic send function for easily sending transaction or tagged data blocks.
Expand Down Expand Up @@ -179,7 +179,7 @@ impl Client {
})
.and_then(|res| async {
let items = res.items;
self.get_outputs(&items).await
self.get_outputs_with_metadata(&items).await
})
.try_collect::<Vec<_>>()
.await?;
Expand Down Expand Up @@ -232,7 +232,7 @@ impl Client {
output_ids: &[OutputId],
addresses: &[Bech32Address],
) -> Result<Vec<OutputWithMetadata>> {
let mut output_responses = self.get_outputs(output_ids).await?;
let mut output_responses = self.get_outputs_with_metadata(output_ids).await?;

// Use `get_address()` API to get the address outputs first,
// then collect the `UtxoInput` in the HashSet.
Expand All @@ -247,7 +247,7 @@ impl Client {
])
.await?;

output_responses.extend(self.get_outputs(&output_ids_response.items).await?);
output_responses.extend(self.get_outputs_with_metadata(&output_ids_response.items).await?);
}

Ok(output_responses.clone())
Expand Down
69 changes: 66 additions & 3 deletions sdk/src/client/node_api/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ pub mod routes;
use crate::client::constants::MAX_PARALLEL_API_REQUESTS;
use crate::{
client::{Client, Result},
types::block::output::{OutputId, OutputMetadata, OutputWithMetadata},
types::block::output::{Output, OutputId, OutputMetadata, OutputWithMetadata},
};

impl Client {
/// Request outputs by their output ID in parallel
pub async fn get_outputs(&self, output_ids: &[OutputId]) -> Result<Vec<OutputWithMetadata>> {
pub async fn get_outputs(&self, output_ids: &[OutputId]) -> Result<Vec<Output>> {
#[cfg(target_family = "wasm")]
let outputs = futures::future::try_join_all(output_ids.iter().map(|id| self.get_output(id))).await?;

Expand All @@ -38,9 +38,38 @@ impl Client {
Ok(outputs)
}

/// Request outputs and their metadata by their output ID in parallel
pub async fn get_outputs_with_metadata(&self, output_ids: &[OutputId]) -> Result<Vec<OutputWithMetadata>> {
#[cfg(target_family = "wasm")]
let outputs =
futures::future::try_join_all(output_ids.iter().map(|id| self.get_output_with_metadata(id))).await?;

#[cfg(not(target_family = "wasm"))]
let outputs =
futures::future::try_join_all(output_ids.chunks(MAX_PARALLEL_API_REQUESTS).map(|output_ids_chunk| {
let client = self.clone();
let output_ids_chunk = output_ids_chunk.to_vec();
async move {
tokio::spawn(async move {
futures::future::try_join_all(
output_ids_chunk.iter().map(|id| client.get_output_with_metadata(id)),
)
.await
})
.await?
}
}))
.await?
.into_iter()
.flatten()
.collect();

Ok(outputs)
}

/// Request outputs by their output ID in parallel, ignoring failed requests
/// Useful to get data about spent outputs, that might not be pruned yet
pub async fn get_outputs_ignore_errors(&self, output_ids: &[OutputId]) -> Result<Vec<OutputWithMetadata>> {
pub async fn get_outputs_ignore_errors(&self, output_ids: &[OutputId]) -> Result<Vec<Output>> {
#[cfg(target_family = "wasm")]
let outputs = futures::future::join_all(output_ids.iter().map(|id| self.get_output(id)))
.await
Expand Down Expand Up @@ -69,6 +98,40 @@ impl Client {
Ok(outputs)
}

/// Request outputs and their metadata by their output ID in parallel, ignoring failed requests
/// Useful to get data about spent outputs, that might not be pruned yet
pub async fn get_outputs_with_metadata_ignore_errors(
&self,
output_ids: &[OutputId],
) -> Result<Vec<OutputWithMetadata>> {
#[cfg(target_family = "wasm")]
let outputs = futures::future::join_all(output_ids.iter().map(|id| self.get_output_with_metadata(id)))
.await
.into_iter()
.filter_map(Result::ok)
.collect();

#[cfg(not(target_family = "wasm"))]
let outputs =
futures::future::try_join_all(output_ids.chunks(MAX_PARALLEL_API_REQUESTS).map(|output_ids_chunk| {
let client = self.clone();
let output_ids_chunk = output_ids_chunk.to_vec();
tokio::spawn(async move {
futures::future::join_all(output_ids_chunk.iter().map(|id| client.get_output_with_metadata(id)))
.await
.into_iter()
.filter_map(Result::ok)
.collect::<Vec<_>>()
})
}))
.await?
.into_iter()
.flatten()
.collect();

Ok(outputs)
}

/// Requests metadata for outputs by their output ID in parallel, ignoring failed requests
pub async fn get_outputs_metadata_ignore_errors(&self, output_ids: &[OutputId]) -> Result<Vec<OutputMetadata>> {
#[cfg(target_family = "wasm")]
Expand Down
30 changes: 20 additions & 10 deletions sdk/src/client/node_api/core/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ use crate::{
},
types::{
api::core::response::{
BlockMetadataResponse, BlockResponse, InfoResponse, OutputWithMetadataResponse, PeerResponse,
RoutesResponse, SubmitBlockResponse, TipsResponse,
BlockMetadataResponse, BlockResponse, InfoResponse, PeerResponse, RoutesResponse, SubmitBlockResponse,
TipsResponse,
},
block::{
output::{dto::OutputMetadataDto, Output, OutputId, OutputMetadata, OutputWithMetadata},
output::{
dto::{OutputDto, OutputMetadataDto},
Output, OutputId, OutputMetadata, OutputWithMetadata,
},
payload::transaction::TransactionId,
slot::{SlotCommitment, SlotCommitmentId, SlotIndex},
Block, BlockDto, BlockId,
Expand Down Expand Up @@ -260,21 +263,18 @@ impl ClientInner {

/// Finds an output by its ID and returns it as object.
/// GET /api/core/v3/outputs/{outputId}
pub async fn get_output(&self, output_id: &OutputId) -> Result<OutputWithMetadata> {
pub async fn get_output(&self, output_id: &OutputId) -> Result<Output> {
let path = &format!("api/core/v3/outputs/{output_id}");

let response: OutputWithMetadataResponse = self
let output = self
.node_manager
.read()
.await
.get_request(path, None, self.get_timeout().await, false, true)
.get_request::<OutputDto>(path, None, self.get_timeout().await, false, true)
.await?;

let token_supply = self.get_token_supply().await?;
let output = Output::try_from_dto(response.output, token_supply)?;
let metadata = OutputMetadata::try_from(response.metadata)?;

Ok(OutputWithMetadata::new(output, metadata))
Ok(Output::try_from_dto(output, token_supply)?)
}

/// Finds an output by its ID and returns it as raw bytes.
Expand Down Expand Up @@ -304,6 +304,16 @@ impl ClientInner {
Ok(OutputMetadata::try_from(metadata)?)
}

// Finds output and its metadata by output ID.
/// GET /api/core/v3/outputs/{outputId}
/// + GET /api/core/v3/outputs/{outputId}/metadata
pub async fn get_output_with_metadata(&self, output_id: &OutputId) -> Result<OutputWithMetadata> {
Ok(OutputWithMetadata::new(
self.get_output(output_id).await?,
self.get_output_metadata(output_id).await?,
))
}

/// Returns the block that was included in the ledger for a given transaction ID, as object.
/// GET /api/core/v3/transactions/{transactionId}/included-block
pub async fn get_included_block(&self, transaction_id: &TransactionId) -> Result<Block> {
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/wallet/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,9 @@ where

// Foundry was not found in the account, try to get it from the node
let foundry_output_id = self.client().foundry_output_id(foundry_id).await?;
let output_response = self.client().get_output(&foundry_output_id).await?;
let output = self.client().get_output(&foundry_output_id).await?;

Ok(output_response.output().to_owned())
Ok(output)
}

/// Save the account to the database, accepts the updated_account as option so we don't need to drop it before
Expand Down
6 changes: 3 additions & 3 deletions sdk/src/wallet/account/operations/syncing/foundries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ where
.await?;

// Update account with new foundries.
for foundry_output_with_metadata in results.into_iter().flatten() {
if let Output::Foundry(foundry) = foundry_output_with_metadata.output() {
foundries.insert(foundry.id(), foundry.to_owned());
for foundry in results.into_iter().flatten() {
if let Output::Foundry(foundry) = foundry {
foundries.insert(foundry.id(), foundry);
}
}

Expand Down
4 changes: 2 additions & 2 deletions sdk/src/wallet/account/operations/syncing/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ where
drop(account_details);

if !unknown_outputs.is_empty() {
outputs.extend(self.client().get_outputs(&unknown_outputs).await?);
outputs.extend(self.client().get_outputs_with_metadata(&unknown_outputs).await?);
}

log::debug!(
Expand Down Expand Up @@ -216,7 +216,7 @@ pub(crate) async fn get_inputs_for_transaction_payload(
.collect::<Vec<_>>();

client
.get_outputs_ignore_errors(&output_ids)
.get_outputs_with_metadata_ignore_errors(&output_ids)
.await
.map_err(|e| e.into())
}
4 changes: 2 additions & 2 deletions sdk/tests/client/consolidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async fn consolidate_outputs() -> Result<()> {
assert_eq!(output_ids_response.items.len(), 1);

let initial_output = client.get_output(&output_ids_response.items[0]).await?;
let initial_base_coin_amount = initial_output.output().amount();
let initial_base_coin_amount = initial_output.amount();

// First split funds to multiple addresses
let token_supply = client.get_token_supply().await?;
Expand Down Expand Up @@ -77,7 +77,7 @@ async fn consolidate_outputs() -> Result<()> {
assert_eq!(output_ids_response.items.len(), 1);

let final_output = client.get_output(&output_ids_response.items[0]).await?;
let final_base_coin_amount = final_output.output().amount();
let final_base_coin_amount = final_output.amount();
// The output has the full amount again
assert_eq!(final_base_coin_amount, initial_base_coin_amount);

Expand Down

0 comments on commit bb05e3c

Please sign in to comment.