Skip to content
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

Make OutputId infallible #1643

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bindings/core/src/method_handler/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result<Response
token_scheme_type,
)),
UtilsMethod::ComputeNftId { output_id } => Response::NftId(NftId::from(&output_id)),
UtilsMethod::ComputeOutputId { id, index } => Response::OutputId(OutputId::new(id, index)?),
UtilsMethod::ComputeOutputId { id, index } => Response::OutputId(OutputId::new(id, index)),
UtilsMethod::ComputeTokenId {
account_id,
serial_number,
Expand Down
2 changes: 1 addition & 1 deletion sdk/examples/how_tos/nft_collection/00_mint_issuer_nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async fn main() -> Result<()> {
if let Output::Nft(nft_output) = output {
// New minted nft id is empty in the output
if nft_output.nft_id().is_null() {
let output_id = OutputId::new(transaction.transaction_id, output_index as u16)?;
let output_id = OutputId::new(transaction.transaction_id, output_index as u16);
let nft_id = NftId::from(&output_id);
println!("New minted issuer NFT id: {nft_id}");
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/client/api/high_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl Client {
UtxoInput::new(
output_with_meta.metadata().transaction_id().to_owned(),
output_with_meta.metadata().output_index(),
)?,
),
output_with_meta.output().amount(),
))
})
Expand Down
11 changes: 4 additions & 7 deletions sdk/src/types/block/input/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ impl UtxoInput {
pub const KIND: u8 = 0;

/// Creates a new [`UtxoInput`].
pub fn new(id: TransactionId, index: u16) -> Result<Self, Error> {
Ok(Self(OutputId::new(id, index)?))
pub fn new(id: TransactionId, index: u16) -> Self {
Self(OutputId::new(id, index))
}

/// Returns the output id of a [`UtxoInput`].
Expand Down Expand Up @@ -62,7 +62,6 @@ pub(crate) mod dto {
use serde::{Deserialize, Serialize};

use super::*;
use crate::types::block::Error;

/// Describes an input which references an unspent transaction output to consume.
#[derive(Serialize, Deserialize)]
Expand All @@ -84,10 +83,8 @@ pub(crate) mod dto {
}
}

impl TryFrom<UtxoInputDto> for UtxoInput {
type Error = Error;

fn try_from(value: UtxoInputDto) -> Result<Self, Self::Error> {
impl From<UtxoInputDto> for UtxoInput {
fn from(value: UtxoInputDto) -> Self {
Self::new(value.transaction_id, value.transaction_output_index)
}
}
Expand Down
14 changes: 6 additions & 8 deletions sdk/src/types/block/output/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,17 @@ mod dto {
latest_commitment_id: SlotCommitmentId,
}

impl TryFrom<OutputMetadataDto> for OutputMetadata {
type Error = crate::types::block::Error;

fn try_from(value: OutputMetadataDto) -> Result<Self, Self::Error> {
Ok(Self {
impl From<OutputMetadataDto> for OutputMetadata {
fn from(value: OutputMetadataDto) -> Self {
Self {
block_id: value.block_id,
output_id: OutputId::new(value.transaction_id, value.output_index)?,
output_id: OutputId::new(value.transaction_id, value.output_index),
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
is_spent: value.is_spent,
commitment_id_spent: value.commitment_id_spent,
transaction_id_spent: value.transaction_id_spent,
included_commitment_id: value.included_commitment_id,
latest_commitment_id: value.latest_commitment_id,
})
}
}
}

Expand All @@ -153,7 +151,7 @@ mod dto {

impl<'de> Deserialize<'de> for OutputMetadata {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
OutputMetadataDto::deserialize(d).and_then(|dto| dto.try_into().map_err(serde::de::Error::custom))
Ok(OutputMetadataDto::deserialize(d)?.into())
}
}

Expand Down
33 changes: 12 additions & 21 deletions sdk/src/types/block/output/output_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,16 @@ pub(crate) type OutputIndex = BoundedU16<{ *OUTPUT_INDEX_RANGE.start() }, { *OUT
#[packable(unpack_error = Error)]
pub struct OutputId {
transaction_id: TransactionId,
#[packable(unpack_error_with = Error::InvalidInputOutputIndex)]
index: OutputIndex,
index: u16,
}

impl OutputId {
/// The length of a [`OutputId`].
pub const LENGTH: usize = TransactionId::LENGTH + core::mem::size_of::<OutputIndex>();

/// Creates a new [`OutputId`].
pub fn new(transaction_id: TransactionId, index: u16) -> Result<Self, Error> {
index
.try_into()
.map(|index| Self { transaction_id, index })
.map_err(Error::InvalidInputOutputIndex)
pub fn new(transaction_id: TransactionId, index: u16) -> Self {
Self { transaction_id, index }
}

/// Returns the [`TransactionId`] of an [`OutputId`].
Expand All @@ -41,7 +37,7 @@ impl OutputId {
/// Returns the index of an [`OutputId`].
#[inline(always)]
pub fn index(&self) -> u16 {
self.index.get()
self.index
}

/// Splits an [`OutputId`] into its [`TransactionId`] and index.
Expand All @@ -60,26 +56,21 @@ impl OutputId {
#[cfg(feature = "serde")]
crate::string_serde_impl!(OutputId);

impl TryFrom<[u8; Self::LENGTH]> for OutputId {
type Error = Error;

fn try_from(bytes: [u8; Self::LENGTH]) -> Result<Self, Self::Error> {
let (transaction_id, index) = bytes.split_at(TransactionId::LENGTH);

Self::new(
// Unwrap is fine because size is already known and valid.
TransactionId::new(transaction_id.try_into().unwrap()),
// Unwrap is fine because size is already known and valid.
u16::from_le_bytes(index.try_into().unwrap()),
)
#[allow(clippy::fallible_impl_from)]
impl From<[u8; Self::LENGTH]> for OutputId {
fn from(bytes: [u8; Self::LENGTH]) -> Self {
// Unwrap is fine because size is already known and valid.
Self::unpack_unverified(bytes).unwrap()
}
}

impl FromStr for OutputId {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(prefix_hex::decode::<[u8; Self::LENGTH]>(s).map_err(Error::Hex)?)
Ok(Self::from(
prefix_hex::decode::<[u8; Self::LENGTH]>(s).map_err(Error::Hex)?,
))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2020-2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use crate::types::block::{output::OutputId, Error};
use crate::types::block::output::OutputId;

crate::impl_id!(
/// The hash of a transaction commitment and output commitment which is used to create a
Expand All @@ -15,7 +15,7 @@ crate::impl_id!(

impl TransactionId {
/// Creates an [`OutputId`] from this [`TransactionId`] and an output index.
pub fn into_output_id(self, index: u16) -> Result<OutputId, Error> {
pub fn into_output_id(self, index: u16) -> OutputId {
OutputId::new(self, index)
}
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/types/block/rand/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use crate::types::block::{

/// Generates a random [`OutputId`].
pub fn rand_output_id() -> OutputId {
OutputId::new(rand_transaction_id(), rand_number_range(OUTPUT_INDEX_RANGE)).unwrap()
OutputId::new(rand_transaction_id(), rand_number_range(OUTPUT_INDEX_RANGE))
}

/// Generates a random [`BasicOutput`](BasicOutput).
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/types/block/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ impl<'a> SemanticValidationContext<'a> {
.filter_map(|(index, output)| {
output.chain_id().map(|chain_id| {
(
chain_id.or_from_output_id(&OutputId::new(*transaction_id, index as u16).unwrap()),
chain_id.or_from_output_id(&OutputId::new(*transaction_id, index as u16)),
output,
)
})
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/wallet/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,8 @@ mod test {
.unwrap();

let transaction_id = TransactionId::new(prefix_hex::decode(TRANSACTION_ID).unwrap());
let input1 = Input::Utxo(UtxoInput::new(transaction_id, 0).unwrap());
let input2 = Input::Utxo(UtxoInput::new(transaction_id, 1).unwrap());
let input1 = Input::Utxo(UtxoInput::new(transaction_id, 0));
let input2 = Input::Utxo(UtxoInput::new(transaction_id, 1));
let bytes: [u8; 32] = prefix_hex::decode(ED25519_ADDRESS).unwrap();
let address = Address::from(Ed25519Address::new(bytes));
let amount = 1_000_000;
Expand Down
5 changes: 1 addition & 4 deletions sdk/src/wallet/storage/participation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,7 @@ mod tests {
);

let outputs_participation = std::iter::once((
TransactionHash::new([3; 32])
.into_transaction_id(0)
.into_output_id(0)
.unwrap(),
TransactionHash::new([3; 32]).into_transaction_id(0).into_output_id(0),
OutputStatusResponse::mock(),
))
.collect::<HashMap<_, _>>();
Expand Down
2 changes: 1 addition & 1 deletion sdk/tests/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ fn build_inputs<'a>(outputs: impl IntoIterator<Item = Build<'a>>) -> Vec<InputSi
output,
output_metadata: OutputMetadata::new(
rand_block_id(),
OutputId::new(rand_transaction_id(), 0).unwrap(),
OutputId::new(rand_transaction_id(), 0),
false,
None,
None,
Expand Down
7 changes: 2 additions & 5 deletions sdk/tests/client/node_api/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,7 @@ async fn test_get_output() {
let client = setup_client_with_node_health_ignored().await;
let (_block_id, transaction_id) = setup_transaction_block(&client).await;

let r = client
.get_output(&OutputId::new(transaction_id, 0).unwrap())
.await
.unwrap();
let r = client.get_output(&OutputId::new(transaction_id, 0)).await.unwrap();

println!("{r:#?}");
}
Expand All @@ -157,7 +154,7 @@ async fn test_get_output() {
async fn test_get_output_raw() {
let client = setup_client_with_node_health_ignored().await;
let (_block_id, transaction_id) = setup_transaction_block(&client).await;
let output_id = OutputId::new(transaction_id, 0).unwrap();
let output_id = OutputId::new(transaction_id, 0);

let output = client.get_output(&output_id).await.unwrap();
let output_raw = Output::unpack_verified(
Expand Down
8 changes: 3 additions & 5 deletions sdk/tests/types/input/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn as_methods() {
#[test]
fn new_output_id() {
let output_id = OutputId::from_str(OUTPUT_ID).unwrap();
let input = UtxoInput::new(*output_id.transaction_id(), output_id.index()).unwrap();
let input = UtxoInput::new(*output_id.transaction_id(), output_id.index());

assert_eq!(*input.output_id(), output_id);
}
Expand Down Expand Up @@ -79,9 +79,7 @@ fn packed_len() {
let output_id = OutputId::from_str(OUTPUT_ID).unwrap();

assert_eq!(
UtxoInput::new(*output_id.transaction_id(), output_id.index())
.unwrap()
.packed_len(),
UtxoInput::new(*output_id.transaction_id(), output_id.index()).packed_len(),
TransactionId::LENGTH + core::mem::size_of::<u16>()
);
assert_eq!(
Expand All @@ -93,7 +91,7 @@ fn packed_len() {
#[test]
fn pack_unpack() {
let output_id = OutputId::from_str(OUTPUT_ID).unwrap();
let utxo_input = UtxoInput::new(*output_id.transaction_id(), output_id.index()).unwrap();
let utxo_input = UtxoInput::new(*output_id.transaction_id(), output_id.index());
let packed_input = utxo_input.pack_to_vec();

assert_eq!(
Expand Down
57 changes: 4 additions & 53 deletions sdk/tests/types/output_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ fn debug_impl() {
id: \"0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c64900000000\", \
slot_index: SlotIndex(0) \
}, \
output_index: BoundedU16(42) \
output_index: 42 \
}"
);
}

#[test]
fn new_valid() {
let transaction_id = TransactionId::from_str(TRANSACTION_ID).unwrap();
let output_id = OutputId::new(transaction_id, 42).unwrap();
let output_id = OutputId::new(transaction_id, 42);

assert_eq!(*output_id.transaction_id(), transaction_id);
assert_eq!(output_id.index(), 42);
Expand All @@ -42,37 +42,21 @@ fn new_valid() {
#[test]
fn null() {
assert_eq!(
format!(
"{}",
TransactionHash::null()
.into_transaction_id(0)
.into_output_id(0)
.unwrap()
),
format!("{}", TransactionHash::null().into_transaction_id(0).into_output_id(0)),
"0x0000000000000000000000000000000000000000000000000000000000000000000000000000"
);
}

#[test]
fn split_valid() {
let transaction_id = TransactionId::from_str(TRANSACTION_ID).unwrap();
let output_id = OutputId::new(transaction_id, 42).unwrap();
let output_id = OutputId::new(transaction_id, 42);
let (transaction_id_s, index) = output_id.split();

assert_eq!(transaction_id_s, transaction_id);
assert_eq!(index, 42);
}

#[test]
fn new_invalid() {
let transaction_id = TransactionId::from_str(TRANSACTION_ID).unwrap();

assert!(matches!(
OutputId::new(transaction_id, 128),
Err(Error::InvalidInputOutputIndex(InvalidBoundedU16(128)))
));
}

#[test]
fn try_from_valid() {
let transaction_id = TransactionId::from_str(TRANSACTION_ID).unwrap();
Expand All @@ -83,16 +67,6 @@ fn try_from_valid() {
assert_eq!(output_id.index(), 42);
}

#[test]
fn try_from_invalid() {
let output_id_bytes: [u8; OutputId::LENGTH] = prefix_hex::decode(OUTPUT_ID_INVALID_INDEX).unwrap();

assert!(matches!(
OutputId::try_from(output_id_bytes),
Err(Error::InvalidInputOutputIndex(InvalidBoundedU16(128)))
));
}

#[test]
fn from_str_valid() {
let transaction_id = TransactionId::from_str(TRANSACTION_ID).unwrap();
Expand All @@ -102,14 +76,6 @@ fn from_str_valid() {
assert_eq!(output_id.index(), 42);
}

#[test]
fn from_str_invalid_index() {
assert!(matches!(
OutputId::from_str(OUTPUT_ID_INVALID_INDEX),
Err(Error::InvalidInputOutputIndex(InvalidBoundedU16(128)))
));
}

#[test]
fn from_str_to_str() {
let output_id = OutputId::from_str(OUTPUT_ID).unwrap();
Expand Down Expand Up @@ -138,18 +104,3 @@ fn pack_unpack_valid() {

assert_eq!(output_id_1, output_id_2);
}

#[test]
fn pack_unpack_invalid() {
let bytes = vec![
82, 253, 252, 7, 33, 130, 101, 79, 22, 63, 95, 15, 154, 98, 29, 114, 149, 102, 199, 77, 16, 3, 124, 77, 123,
187, 4, 7, 209, 226, 198, 73, 0, 0, 0, 0, 128, 0,
];

assert!(matches!(
OutputId::unpack_verified(bytes.as_slice(), &()),
Err(UnpackError::Packable(Error::InvalidInputOutputIndex(
InvalidBoundedU16(128)
)))
));
}
4 changes: 2 additions & 2 deletions sdk/tests/types/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const ED25519_SIGNATURE: &str = "0xc6a40edf9a089f42c18f4ebccb35fe4b578d93b879e99
fn transaction() {
let protocol_parameters = protocol_parameters();
let transaction_id = TransactionId::new(prefix_hex::decode(TRANSACTION_ID).unwrap());
let input1 = Input::Utxo(UtxoInput::new(transaction_id, 0).unwrap());
let input2 = Input::Utxo(UtxoInput::new(transaction_id, 1).unwrap());
let input1 = Input::Utxo(UtxoInput::new(transaction_id, 0));
let input2 = Input::Utxo(UtxoInput::new(transaction_id, 1));
let bytes: [u8; 32] = prefix_hex::decode(ED25519_ADDRESS).unwrap();
let address = Address::from(Ed25519Address::new(bytes));
let amount = 1_000_000;
Expand Down
Loading
Loading