Skip to content

Commit

Permalink
Merge branch '2.0' into remove-ChangeAddress-RemainderValueStrategy
Browse files Browse the repository at this point in the history
  • Loading branch information
thibault-martinez authored Jan 29, 2024
2 parents c1b1d95 + abcdd61 commit 7b05291
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 26 deletions.
4 changes: 2 additions & 2 deletions bindings/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ impl Serialize for Error {
kind: self.as_ref().to_owned(),
error: match &self {
// Only Client and wallet have a proper serde impl
Self::Client(e) => serde_json::to_value(e).map_err(|e| serde::ser::Error::custom(e))?,
Self::Wallet(e) => serde_json::to_value(e).map_err(|e| serde::ser::Error::custom(e))?,
Self::Client(e) => serde_json::to_value(e).map_err(serde::ser::Error::custom)?,
Self::Wallet(e) => serde_json::to_value(e).map_err(serde::ser::Error::custom)?,
_ => serde_json::Value::String(self.to_string()),
},
}
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/types/block/output/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ impl DelegationOutput {
// Transition, just without full SemanticValidationContext.
pub(crate) fn transition_inner(current_state: &Self, next_state: &Self) -> Result<(), StateTransitionError> {
#[allow(clippy::nonminimal_bool)]
if !(current_state.delegation_id.is_null() && !next_state.delegation_id().is_null()) {
if !(current_state.delegation_id.is_null() && !next_state.delegation_id.is_null()) {
return Err(StateTransitionError::NonDelayedClaimingTransition);
}

Expand All @@ -334,7 +334,7 @@ impl DelegationOutput {
{
return Err(StateTransitionError::MutatedImmutableField);
}
// TODO add end_epoch validation rules

Ok(())
}
}
Expand Down
10 changes: 7 additions & 3 deletions sdk/src/types/block/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,16 @@ impl ProtocolParameters {

/// Gets the first [`SlotIndex`] of a given [`EpochIndex`].
pub fn first_slot_of(&self, epoch_index: impl Into<EpochIndex>) -> SlotIndex {
epoch_index.into().first_slot_index(self.slots_per_epoch_exponent())
epoch_index
.into()
.first_slot_index(self.genesis_slot, self.slots_per_epoch_exponent())
}

/// Gets the last [`SlotIndex`] of a given [`EpochIndex`].
pub fn last_slot_of(&self, epoch_index: impl Into<EpochIndex>) -> SlotIndex {
epoch_index.into().last_slot_index(self.slots_per_epoch_exponent())
epoch_index
.into()
.last_slot_index(self.genesis_slot, self.slots_per_epoch_exponent())
}

/// Calculates the number of slots before the next epoch.
Expand All @@ -226,7 +230,7 @@ impl ProtocolParameters {

/// Gets the [`EpochIndex`] of a given [`SlotIndex`].
pub fn epoch_index_of(&self, slot_index: impl Into<SlotIndex>) -> EpochIndex {
EpochIndex::from_slot_index(slot_index.into(), self.slots_per_epoch_exponent())
EpochIndex::from_slot_index(slot_index, self.genesis_slot, self.slots_per_epoch_exponent())
}

/// Calculates the duration of an epoch in seconds.
Expand Down
77 changes: 74 additions & 3 deletions sdk/src/types/block/semantic/state_transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use crate::types::block::{
context_input::CommitmentContextInput,
output::{
AccountOutput, AnchorOutput, BasicOutput, ChainId, DelegationOutput, FoundryOutput, NftOutput, Output,
TokenScheme,
Expand All @@ -25,6 +26,7 @@ pub enum StateTransitionError {
InvalidBlockIssuerTransition,
IssuerNotUnlocked,
MissingAccountForFoundry,
MissingCommitmentContextInput,
MutatedFieldWithoutRights,
MutatedImmutableField,
NonDelayedClaimingTransition,
Expand Down Expand Up @@ -327,7 +329,9 @@ impl StateTransitionVerifier for NftOutput {
}

impl StateTransitionVerifier for DelegationOutput {
fn creation(next_state: &Self, _context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
fn creation(next_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
let protocol_parameters = &context.protocol_parameters;

if !next_state.delegation_id().is_null() {
return Err(StateTransitionError::NonZeroCreatedId);
}
Expand All @@ -340,15 +344,82 @@ impl StateTransitionVerifier for DelegationOutput {
return Err(StateTransitionError::NonZeroDelegationEndEpoch);
}

let slot_commitment_id = context
.transaction
.context_inputs()
.iter()
.find(|i| i.kind() == CommitmentContextInput::KIND)
.map(|s| s.as_commitment().slot_commitment_id())
.ok_or(StateTransitionError::MissingCommitmentContextInput)?;

let past_bounded_slot_index = slot_commitment_id.past_bounded_slot(protocol_parameters.max_committable_age);
let past_bounded_epoch_index = past_bounded_slot_index.to_epoch_index(
protocol_parameters.genesis_slot,
protocol_parameters.slots_per_epoch_exponent,
);

let registration_slot = (past_bounded_epoch_index + 1).registration_slot(
protocol_parameters.genesis_slot,
protocol_parameters.slots_per_epoch_exponent,
protocol_parameters.epoch_nearing_threshold,
);

let expected_start_epoch = if past_bounded_slot_index <= registration_slot {
past_bounded_epoch_index + 1
} else {
past_bounded_epoch_index + 2
};

if next_state.start_epoch() != expected_start_epoch {
// TODO: specific tx failure reason https://github.com/iotaledger/iota-core/issues/679
return Err(StateTransitionError::TransactionFailure(
TransactionFailureReason::SemanticValidationFailed,
));
}

Ok(())
}

fn transition(
current_state: &Self,
next_state: &Self,
_context: &SemanticValidationContext<'_>,
context: &SemanticValidationContext<'_>,
) -> Result<(), StateTransitionError> {
Self::transition_inner(current_state, next_state)
Self::transition_inner(current_state, next_state)?;

let protocol_parameters = &context.protocol_parameters;

let slot_commitment_id = context
.transaction
.context_inputs()
.iter()
.find(|i| i.kind() == CommitmentContextInput::KIND)
.map(|s| s.as_commitment().slot_commitment_id())
.ok_or(StateTransitionError::MissingCommitmentContextInput)?;

let future_bounded_slot_index = slot_commitment_id.future_bounded_slot(protocol_parameters.min_committable_age);
let future_bounded_epoch_index = future_bounded_slot_index.to_epoch_index(
protocol_parameters.genesis_slot,
protocol_parameters.slots_per_epoch_exponent,
);

let registration_slot = (future_bounded_epoch_index + 1).registration_slot(
protocol_parameters.genesis_slot,
protocol_parameters.slots_per_epoch_exponent,
protocol_parameters.epoch_nearing_threshold,
);

let expected_end_epoch = if future_bounded_slot_index <= registration_slot {
future_bounded_epoch_index
} else {
future_bounded_epoch_index + 1
};

if next_state.end_epoch() != expected_end_epoch {
return Err(StateTransitionError::NonDelayedClaimingTransition);
}

Ok(())
}

fn destruction(
Expand Down
21 changes: 21 additions & 0 deletions sdk/src/types/block/slot/commitment_id.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use super::SlotIndex;

crate::impl_id!(
/// The hash of a [`SlotCommitment`](crate::types::block::slot::SlotCommitment).
pub SlotCommitmentHash {
Expand All @@ -9,3 +11,22 @@ crate::impl_id!(
/// A [`SlotCommitment`](crate::types::block::slot::SlotCommitment) identifier.
pub SlotCommitmentId;
);

impl SlotCommitmentId {
/// Calculates the past bounded slot for the given slot of the SlotCommitment.
/// Given any slot index of a commitment input, the result of this function is a slot index
/// that is at least equal to the slot of the block in which it was issued, or higher.
/// That means no commitment input can be chosen such that the index lies behind the slot index of the block,
/// hence the past is bounded.
pub fn past_bounded_slot(self, max_committable_age: u32) -> SlotIndex {
self.slot_index() + max_committable_age
}
/// Calculates the future bounded slot for the given slot of the SlotCommitment.
/// Given any slot index of a commitment input, the result of this function is a slot index
/// that is at most equal to the slot of the block in which it was issued, or lower.
/// That means no commitment input can be chosen such that the index lies ahead of the slot index of the block,
/// hence the future is bounded.
pub fn future_bounded_slot(self, min_committable_age: u32) -> SlotIndex {
self.slot_index() + min_committable_age
}
}
51 changes: 39 additions & 12 deletions sdk/src/types/block/slot/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,48 @@ pub struct EpochIndex(pub u32);

impl EpochIndex {
/// Gets the range of slots this epoch contains.
pub fn slot_index_range(&self, slots_per_epoch_exponent: u8) -> core::ops::RangeInclusive<SlotIndex> {
self.first_slot_index(slots_per_epoch_exponent)..=self.last_slot_index(slots_per_epoch_exponent)
pub fn slot_index_range(
&self,
genesis_slot: impl Into<SlotIndex> + Copy,
slots_per_epoch_exponent: u8,
) -> core::ops::RangeInclusive<SlotIndex> {
self.first_slot_index(genesis_slot, slots_per_epoch_exponent)
..=self.last_slot_index(genesis_slot, slots_per_epoch_exponent)
}

/// Gets the epoch index given a [`SlotIndex`].
pub fn from_slot_index(slot_index: SlotIndex, slots_per_epoch_exponent: u8) -> Self {
Self(*slot_index >> slots_per_epoch_exponent)
pub fn from_slot_index(
slot_index: impl Into<SlotIndex>,
genesis_slot: impl Into<SlotIndex>,
slots_per_epoch_exponent: u8,
) -> Self {
let genesis_slot = genesis_slot.into();
let slot_index = slot_index.into();
if slot_index <= genesis_slot {
return Self(0);
}
Self(*(slot_index - genesis_slot) >> slots_per_epoch_exponent)
}

/// Gets the first [`SlotIndex`] of this epoch.
pub fn first_slot_index(self, slots_per_epoch_exponent: u8) -> SlotIndex {
SlotIndex::from_epoch_index(self, slots_per_epoch_exponent)
pub fn first_slot_index(self, genesis_slot: impl Into<SlotIndex>, slots_per_epoch_exponent: u8) -> SlotIndex {
SlotIndex::from_epoch_index(self, genesis_slot, slots_per_epoch_exponent)
}

/// Gets the last [`SlotIndex`] of this epoch.
pub fn last_slot_index(self, slots_per_epoch_exponent: u8) -> SlotIndex {
SlotIndex::from_epoch_index(self + 1, slots_per_epoch_exponent) - 1
pub fn last_slot_index(self, genesis_slot: impl Into<SlotIndex>, slots_per_epoch_exponent: u8) -> SlotIndex {
SlotIndex::from_epoch_index(self + 1, genesis_slot, slots_per_epoch_exponent) - 1
}

/// Returns the slot at the end of which the validator and delegator registration ends and the voting power
/// for the epoch is calculated.
pub fn registration_slot(
&self,
genesis_slot: impl Into<SlotIndex>,
slots_per_epoch_exponent: u8,
epoch_nearing_threshold: u32,
) -> SlotIndex {
self.first_slot_index(genesis_slot, slots_per_epoch_exponent) - epoch_nearing_threshold - 1
}
}

Expand Down Expand Up @@ -131,18 +156,20 @@ mod test {
..Default::default()
};
let slot_index = SlotIndex(3000);
let epoch_index = EpochIndex::from_slot_index(slot_index, params.slots_per_epoch_exponent());
let epoch_index =
EpochIndex::from_slot_index(slot_index, params.genesis_slot, params.slots_per_epoch_exponent());
assert_eq!(epoch_index, EpochIndex(2));
assert_eq!(
epoch_index.slot_index_range(params.slots_per_epoch_exponent()),
epoch_index.slot_index_range(params.genesis_slot, params.slots_per_epoch_exponent()),
SlotIndex(2048)..=SlotIndex(3071)
);

let slot_index = SlotIndex(10 * params.slots_per_epoch() + 2000);
let epoch_index = EpochIndex::from_slot_index(slot_index, params.slots_per_epoch_exponent());
let epoch_index =
EpochIndex::from_slot_index(slot_index, params.genesis_slot, params.slots_per_epoch_exponent());
assert_eq!(epoch_index, EpochIndex(11));
assert_eq!(
epoch_index.slot_index_range(params.slots_per_epoch_exponent()),
epoch_index.slot_index_range(params.genesis_slot, params.slots_per_epoch_exponent()),
SlotIndex(11 * params.slots_per_epoch())..=SlotIndex(12 * params.slots_per_epoch() - 1)
);
}
Expand Down
13 changes: 9 additions & 4 deletions sdk/src/types/block/slot/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,17 @@ pub struct SlotIndex(pub u32);

impl SlotIndex {
/// Gets the [`EpochIndex`] of this slot.
pub fn to_epoch_index(self, slots_per_epoch_exponent: u8) -> EpochIndex {
EpochIndex::from_slot_index(self, slots_per_epoch_exponent)
pub fn to_epoch_index(self, genesis_slot: impl Into<Self>, slots_per_epoch_exponent: u8) -> EpochIndex {
EpochIndex::from_slot_index(self, genesis_slot, slots_per_epoch_exponent)
}

pub fn from_epoch_index(epoch_index: EpochIndex, slots_per_epoch_exponent: u8) -> Self {
Self(*epoch_index << slots_per_epoch_exponent)
/// Gets the first [`SlotIndex`] of the provided epoch.
pub fn from_epoch_index(
epoch_index: EpochIndex,
genesis_slot: impl Into<Self>,
slots_per_epoch_exponent: u8,
) -> Self {
genesis_slot.into() + Self(*epoch_index << slots_per_epoch_exponent)
}

/// Gets the slot index of a unix timestamp in seconds.
Expand Down

0 comments on commit 7b05291

Please sign in to comment.