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

Add creation slot to TransactionId and rework some stuff #1414

Merged
merged 27 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
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
33 changes: 14 additions & 19 deletions sdk/src/types/block/block_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,10 @@ use crate::types::block::Error;
impl_id!(pub BlockHash, 32, "The hash of a [`Block`].");

impl BlockHash {
#[cfg(target_endian = "little")]
pub fn with_slot_index(self, slot_index: SlotIndex) -> BlockId {
BlockId { hash: self, slot_index }
}

#[cfg(target_endian = "big")]
pub fn with_slot_index(self, slot_index: SlotIndex) -> BlockId {
pub fn with_slot_index(self, slot_index: impl Into<SlotIndex>) -> BlockId {
BlockId {
hash: self,
slot_index: slot_index.to_le().into(),
slot_index: slot_index.into().to_le_bytes(),
}
}
}
Expand All @@ -27,9 +21,7 @@ impl BlockHash {
#[repr(C)]
pub struct BlockId {
pub(crate) hash: BlockHash,
// IMPORTANT: On big-endian systems this value is misrepresented because it is transmuted directly
// from bytes, so the getter below handles that conversion. Do not access it directly.
slot_index: SlotIndex,
slot_index: [u8; core::mem::size_of::<SlotIndex>()],
}

impl BlockId {
Expand All @@ -46,15 +38,18 @@ impl BlockId {
}

/// Returns the [`BlockId`]'s slot index part.
#[cfg(target_endian = "little")]
pub fn slot_index(&self) -> SlotIndex {
self.slot_index
}

/// Returns the [`BlockId`]'s slot index part.
#[cfg(target_endian = "big")]
pub fn slot_index(&self) -> SlotIndex {
self.slot_index.to_le().into()
unsafe {
#[cfg(target_endian = "little")]
{
core::mem::transmute(self.slot_index)
}

#[cfg(target_endian = "big")]
{
core::mem::transmute(self.slot_index.to_le())
}
}
}
}

Expand Down
11 changes: 1 addition & 10 deletions sdk/src/types/block/output/output_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,6 @@ impl OutputId {
self.index.get()
}

/// Creates a null [`OutputId`].
pub fn null() -> Self {
Self {
transaction_id: TransactionId::null(),
// Unwrap is fine because index is already known and valid.
index: 0u16.try_into().unwrap(),
}
}

/// Splits an [`OutputId`] into its [`TransactionId`] and index.
#[inline(always)]
pub fn split(self) -> (TransactionId, u16) {
Expand All @@ -76,7 +67,7 @@ impl TryFrom<[u8; Self::LENGTH]> for OutputId {

Self::new(
// Unwrap is fine because size is already known and valid.
From::<[u8; TransactionId::LENGTH]>::from(transaction_id.try_into().unwrap()),
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()),
)
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/types/block/payload/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use packable::{error::UnpackError, packer::Packer, unpacker::Unpacker, Packable,
pub(crate) use self::essence::{ContextInputCount, InputCount, OutputCount};
pub use self::{
essence::{RegularTransactionEssence, RegularTransactionEssenceBuilder, TransactionEssence},
transaction_id::TransactionId,
transaction_id::{TransactionHash, TransactionId},
};
use crate::types::block::{protocol::ProtocolParameters, unlock::Unlocks, Error};

Expand Down Expand Up @@ -51,7 +51,7 @@ impl TransactionPayload {
hasher.update(Self::KIND.to_le_bytes());
hasher.update(self.pack_to_vec());

TransactionId::new(hasher.finalize().into())
TransactionHash::new(hasher.finalize().into()).with_slot_index(self.essence.creation_slot())
}
}

Expand Down
115 changes: 110 additions & 5 deletions sdk/src/types/block/payload/transaction/transaction_id.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,116 @@
// Copyright 2020-2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

impl_id!(
pub TransactionId,
32,
"A transaction identifier, the BLAKE2b-256 hash of the transaction bytes. See <https://www.blake2.net/> for more information."
);
use crate::types::block::{output::OutputId, slot::SlotIndex, ConvertTo, Error};

impl_id!(pub TransactionHash, 32, "The hash of a [`TransactionPayload`].");

impl TransactionHash {
pub fn with_slot_index(self, slot_index: impl Into<SlotIndex>) -> TransactionId {
TransactionId {
hash: self,
slot_index: slot_index.into().to_le_bytes(),
}
}
}

/// A transaction identifier.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Debug, packable::Packable)]
#[packable(unpack_error = Error)]
#[repr(C)]
pub struct TransactionId {
pub(crate) hash: TransactionHash,
slot_index: [u8; core::mem::size_of::<SlotIndex>()],
}

impl TransactionId {
/// The length of a [`TransactionId`]
pub const LENGTH: usize = 40;
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved

pub fn new(bytes: [u8; Self::LENGTH]) -> Self {
unsafe { core::mem::transmute(bytes) }
}

/// Returns the [`TransactionId`]'s hash part.
pub fn hash(&self) -> &TransactionHash {
&self.hash
}

/// Returns the [`TransactionId`]'s slot index part.
pub fn slot_index(&self) -> SlotIndex {
unsafe {
#[cfg(target_endian = "little")]
{
core::mem::transmute(self.slot_index)
}

#[cfg(target_endian = "big")]
{
core::mem::transmute(self.slot_index.to_le())
}
}
}

/// Creates an [`OutputId`] from this [`TransactionId`] and an output index.
pub fn with_output_index(self, index: u16) -> Result<OutputId, Error> {
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
OutputId::new(self, index)
}
}

impl AsRef<[u8]> for TransactionId {
fn as_ref(&self) -> &[u8] {
unsafe { core::mem::transmute::<_, &[u8; Self::LENGTH]>(self) }
}
}

impl core::str::FromStr for TransactionId {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::new(prefix_hex::decode(s).map_err(Error::Hex)?))
}
}

impl core::fmt::Display for TransactionId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
prefix_hex::encode(self.as_ref()).fmt(f)
}
}

impl TryFrom<&alloc::string::String> for TransactionId {
type Error = Error;

fn try_from(s: &alloc::string::String) -> Result<Self, Self::Error> {
core::str::FromStr::from_str(s.as_str())
}
}

impl TryFrom<&str> for TransactionId {
type Error = Error;

fn try_from(s: &str) -> Result<Self, Self::Error> {
core::str::FromStr::from_str(s)
}
}

impl ConvertTo<TransactionId> for &alloc::string::String {
fn convert(self) -> Result<TransactionId, Error> {
self.try_into()
}
}

impl ConvertTo<TransactionId> for &str {
fn convert(self) -> Result<TransactionId, Error> {
self.try_into()
}
}

impl core::ops::Deref for TransactionId {
type Target = [u8; Self::LENGTH];

fn deref(&self) -> &Self::Target {
unsafe { core::mem::transmute::<_, &[u8; Self::LENGTH]>(self) }
}
}
#[cfg(feature = "serde")]
string_serde_impl!(TransactionId);
7 changes: 5 additions & 2 deletions sdk/src/wallet/storage/participation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl StorageManager {
#[cfg(test)]
mod tests {
use super::*;
use crate::{types::block::payload::transaction::TransactionId, wallet::storage::adapter::memory::Memory};
use crate::{types::block::payload::transaction::TransactionHash, wallet::storage::adapter::memory::Memory};

#[tokio::test]
async fn insert_get_remove_participation_event() {
Expand Down Expand Up @@ -155,7 +155,10 @@ mod tests {
);

let outputs_participation = std::iter::once((
OutputId::new(TransactionId::new([3; 32]), 0).unwrap(),
TransactionHash::new([3; 32])
.with_slot_index(0)
.with_output_index(0)
.unwrap(),
OutputStatusResponse::mock(),
))
.collect::<HashMap<_, _>>();
Expand Down
6 changes: 3 additions & 3 deletions sdk/tests/types/block_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use core::str::FromStr;

use iota_sdk::types::{
block::{
output::RentStructure, protocol::ProtocolParameters, rand::bytes::rand_bytes_array, BlockHash, BlockId,
BlockWrapper, BlockWrapperDto,
output::RentStructure, protocol::ProtocolParameters, rand::bytes::rand_bytes_array, slot::SlotIndex, BlockHash,
BlockId, BlockWrapper, BlockWrapperDto,
},
TryFromDto,
};
Expand Down Expand Up @@ -59,7 +59,7 @@ fn pack_unpack_valid() {
#[test]
fn memory_layout() {
let block_hash = BlockHash::new(rand_bytes_array());
let slot_index = 12345.into();
let slot_index = SlotIndex::new(12345);
let block_id = block_hash.with_slot_index(slot_index);
assert_eq!(slot_index, block_id.slot_index());
let memory_layout =
Expand Down
8 changes: 6 additions & 2 deletions sdk/tests/types/output_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

use core::str::FromStr;

use iota_sdk::types::block::{output::OutputId, payload::transaction::TransactionId, Error};
use iota_sdk::types::block::{
output::OutputId,
payload::transaction::{TransactionHash, TransactionId},
Error,
};
use packable::{bounded::InvalidBoundedU16, error::UnpackError, PackableExt};

const TRANSACTION_ID: &str = "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649";
Expand All @@ -30,7 +34,7 @@ fn new_valid() {
#[test]
fn null() {
assert_eq!(
format!("{:?}", OutputId::null()),
format!("{:?}", TransactionHash::null().with_slot_index(0).with_output_index(0)),
"OutputId(0x00000000000000000000000000000000000000000000000000000000000000000000)"
);
}
Expand Down
8 changes: 4 additions & 4 deletions sdk/tests/wallet/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use iota_sdk::{
types::block::{
address::{Address, Bech32Address, Ed25519Address},
input::{Input, UtxoInput},
output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output, OutputId},
payload::transaction::{RegularTransactionEssence, TransactionEssence, TransactionId},
output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output},
payload::transaction::{RegularTransactionEssence, TransactionEssence, TransactionHash, TransactionId},
protocol::protocol_parameters,
rand::{
mana::rand_mana_allotment,
Expand Down Expand Up @@ -44,7 +44,7 @@ fn wallet_events_serde() {
}));

let output_data_dto = OutputDataDto::from(&OutputData {
output_id: OutputId::null(),
output_id: TransactionHash::null().with_slot_index(0).with_output_index(0).unwrap(),
metadata: rand_output_metadata(),
output: Output::from(rand_basic_output(1_813_620_509_061_365)),
is_spent: false,
Expand All @@ -65,7 +65,7 @@ fn wallet_events_serde() {
})));

assert_serde_eq(WalletEvent::TransactionInclusion(TransactionInclusionEvent {
transaction_id: TransactionId::null(),
transaction_id: TransactionHash::null().with_slot_index(0),
inclusion_state: InclusionState::Conflicting,
}));

Expand Down