Skip to content

Commit

Permalink
fix(genesis-geremony): fix examine-genesis-checkpoint (#4829)
Browse files Browse the repository at this point in the history
* fix: examine genesis checkpoint

* chore: clippy and fmt

* refactor: is_maybe to try_from

* chore: remove unused functions

* refactor: remove unwrap from func

* chore: remove unused to_bytes funcs

* chore: cargo fmt

* chore: correct spelling

* fix(genesis-inspector): consider  TimelockedStakedIota as a stake

* refactor(genesis-inspector): get migration objects as iterator

* add timelock balance assert

* fixes coming from different review comments

---------

Co-authored-by: Mirko Zichichi <miker83z@proton.me>
Co-authored-by: miker83z <mirko.zichichi@iota.org>
  • Loading branch information
3 people authored Jan 28, 2025
1 parent 32c6d20 commit 794e8f4
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 32 deletions.
16 changes: 15 additions & 1 deletion crates/iota-genesis-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,25 @@ impl Builder {
}
}

/// Return an iterator of migration objects if this genesis is with
/// migration objects.
pub fn tx_migration_objects(&self) -> impl Iterator<Item = Object> + '_ {
self.migration_tx_data
.as_ref()
.map(|tx_data| tx_data.get_objects())
.into_iter()
.flatten()
}

/// Set the genesis delegation to be a `OneToAll` kind and set the
/// delegator address.
pub fn with_delegator(mut self, delegator: IotaAddress) -> Self {
self.delegation = Some(GenesisDelegation::OneToAll(delegator));
self
}

/// Set the genesis delegation to be a `ManyToMany` kind and set the
/// delegator map.
pub fn with_delegations(mut self, delegations: Delegations) -> Self {
self.delegation = Some(GenesisDelegation::ManyToMany(delegations));
self
Expand Down Expand Up @@ -259,7 +273,7 @@ impl Builder {
self.built_genesis.clone()
}

fn load_migration_sources(&mut self) -> anyhow::Result<()> {
pub fn load_migration_sources(&mut self) -> anyhow::Result<()> {
for source in &self.migration_sources {
tracing::info!("Adding migration objects from {:?}", source);
self.migration_objects
Expand Down
29 changes: 28 additions & 1 deletion crates/iota-types/src/base_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use crate::{
object::{Object, Owner},
parse_iota_struct_tag,
signature::GenericSignature,
stardust::output::Nft,
stardust::output::{AliasOutput, BasicOutput, Nft, NftOutput},
timelock::{
timelock::{self, TimeLock},
timelocked_staked_iota::TimelockedStakedIota,
Expand Down Expand Up @@ -380,6 +380,33 @@ impl MoveObjectType {
}
}

pub fn is_alias_output(&self) -> bool {
match &self.0 {
MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
false
}
MoveObjectType_::Other(s) => AliasOutput::is_alias_output(s),
}
}

pub fn is_basic_output(&self) -> bool {
match &self.0 {
MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
false
}
MoveObjectType_::Other(s) => BasicOutput::is_basic_output(s),
}
}

pub fn is_nft_output(&self) -> bool {
match &self.0 {
MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
false
}
MoveObjectType_::Other(s) => NftOutput::is_nft_output(s),
}
}

pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> IotaResult<TypeTag> {
match &self.0 {
MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
Expand Down
38 changes: 35 additions & 3 deletions crates/iota-types/src/stardust/output/alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use crate::{
STARDUST_PACKAGE_ID, TypeTag,
STARDUST_ADDRESS, TypeTag,
balance::Balance,
base_types::{IotaAddress, ObjectID, SequenceNumber, TxContext},
collection_types::Bag,
error::IotaError,
id::UID,
object::{Data, MoveObject, Object, Owner},
stardust::{coin_type::CoinType, stardust_to_iota_address},
Expand Down Expand Up @@ -54,7 +55,7 @@ impl Alias {
/// [`Alias`] in its move package.
pub fn tag() -> StructTag {
StructTag {
address: STARDUST_PACKAGE_ID.into(),
address: STARDUST_ADDRESS,
module: ALIAS_MODULE_NAME.to_owned(),
name: ALIAS_STRUCT_NAME.to_owned(),
type_params: Vec::new(),
Expand Down Expand Up @@ -154,7 +155,7 @@ impl AliasOutput {
/// [`AliasOutput`] in its move package.
pub fn tag(type_param: TypeTag) -> StructTag {
StructTag {
address: STARDUST_PACKAGE_ID.into(),
address: STARDUST_ADDRESS,
module: ALIAS_OUTPUT_MODULE_NAME.to_owned(),
name: ALIAS_OUTPUT_STRUCT_NAME.to_owned(),
type_params: vec![type_param],
Expand Down Expand Up @@ -201,4 +202,35 @@ impl AliasOutput {

Ok(move_alias_output_object)
}

/// Create an `AliasOutput` from BCS bytes.
pub fn from_bcs_bytes(content: &[u8]) -> Result<Self, IotaError> {
bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
error: format!("Unable to deserialize AliasOutput object: {:?}", err),
})
}

pub fn is_alias_output(s: &StructTag) -> bool {
s.address == STARDUST_ADDRESS
&& s.module.as_ident_str() == ALIAS_OUTPUT_MODULE_NAME
&& s.name.as_ident_str() == ALIAS_OUTPUT_STRUCT_NAME
}
}

impl TryFrom<&Object> for AliasOutput {
type Error = IotaError;
fn try_from(object: &Object) -> Result<Self, Self::Error> {
match &object.data {
Data::Move(o) => {
if o.type_().is_alias_output() {
return AliasOutput::from_bcs_bytes(o.contents());
}
}
Data::Package(_) => {}
}

Err(IotaError::Type {
error: format!("Object type is not an AliasOutput: {:?}", object),
})
}
}
37 changes: 35 additions & 2 deletions crates/iota-types/src/stardust/output/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ use super::unlock_conditions::{
ExpirationUnlockCondition, StorageDepositReturnUnlockCondition, TimelockUnlockCondition,
};
use crate::{
STARDUST_PACKAGE_ID, TypeTag,
STARDUST_ADDRESS, TypeTag,
balance::Balance,
base_types::{IotaAddress, MoveObjectType, ObjectID, SequenceNumber, TxContext},
coin::Coin,
collection_types::Bag,
error::IotaError,
id::UID,
object::{Data, MoveObject, Object, Owner},
stardust::{coin_type::CoinType, stardust_to_iota_address},
Expand Down Expand Up @@ -108,7 +109,7 @@ impl BasicOutput {
/// Returns the struct tag of the BasicOutput struct
pub fn tag(type_param: TypeTag) -> StructTag {
StructTag {
address: STARDUST_PACKAGE_ID.into(),
address: STARDUST_ADDRESS,
module: BASIC_OUTPUT_MODULE_NAME.to_owned(),
name: BASIC_OUTPUT_STRUCT_NAME.to_owned(),
type_params: vec![type_param],
Expand Down Expand Up @@ -181,6 +182,20 @@ impl BasicOutput {
coin_type,
)
}

/// Create a `BasicOutput` from BCS bytes.
pub fn from_bcs_bytes(content: &[u8]) -> Result<Self, IotaError> {
bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
error: format!("Unable to deserialize BasicOutput object: {:?}", err),
})
}

/// Whether the given `StructTag` represents a `BasicOutput`.
pub fn is_basic_output(s: &StructTag) -> bool {
s.address == STARDUST_ADDRESS
&& s.module.as_ident_str() == BASIC_OUTPUT_MODULE_NAME
&& s.name.as_ident_str() == BASIC_OUTPUT_STRUCT_NAME
}
}

pub(crate) fn create_coin(
Expand Down Expand Up @@ -209,3 +224,21 @@ pub(crate) fn create_coin(
tx_context.digest(),
))
}

impl TryFrom<&Object> for BasicOutput {
type Error = IotaError;
fn try_from(object: &Object) -> Result<Self, Self::Error> {
match &object.data {
Data::Move(o) => {
if o.type_().is_basic_output() {
return BasicOutput::from_bcs_bytes(o.contents());
}
}
Data::Package(_) => {}
}

Err(IotaError::Type {
error: format!("Object type is not a BasicOutput: {:?}", object),
})
}
}
38 changes: 35 additions & 3 deletions crates/iota-types/src/stardust/output/nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ use super::unlock_conditions::{
ExpirationUnlockCondition, StorageDepositReturnUnlockCondition, TimelockUnlockCondition,
};
use crate::{
STARDUST_PACKAGE_ID, TypeTag,
STARDUST_ADDRESS, TypeTag,
balance::Balance,
base_types::{IotaAddress, ObjectID, SequenceNumber, TxContext},
collection_types::{Bag, Entry, VecMap},
error::IotaError,
id::UID,
object::{Data, MoveObject, Object, Owner},
stardust::{coin_type::CoinType, stardust_to_iota_address},
Expand Down Expand Up @@ -261,7 +262,7 @@ impl Nft {
/// [`Nft`] in its move package.
pub fn tag() -> StructTag {
StructTag {
address: STARDUST_PACKAGE_ID.into(),
address: STARDUST_ADDRESS,
module: NFT_MODULE_NAME.to_owned(),
name: NFT_STRUCT_NAME.to_owned(),
type_params: Vec::new(),
Expand Down Expand Up @@ -404,7 +405,7 @@ impl NftOutput {
/// [`NftOutput`] in its move package.
pub fn tag(type_param: TypeTag) -> StructTag {
StructTag {
address: STARDUST_PACKAGE_ID.into(),
address: STARDUST_ADDRESS,
module: NFT_OUTPUT_MODULE_NAME.to_owned(),
name: NFT_OUTPUT_STRUCT_NAME.to_owned(),
type_params: vec![type_param],
Expand Down Expand Up @@ -469,4 +470,35 @@ impl NftOutput {

Ok(move_nft_output_object)
}

/// Create an `NftOutput` from BCS bytes.
pub fn from_bcs_bytes(content: &[u8]) -> Result<Self, IotaError> {
bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
error: format!("Unable to deserialize NftOutput object: {:?}", err),
})
}

pub fn is_nft_output(s: &StructTag) -> bool {
s.address == STARDUST_ADDRESS
&& s.module.as_ident_str() == NFT_OUTPUT_MODULE_NAME
&& s.name.as_ident_str() == NFT_OUTPUT_STRUCT_NAME
}
}

impl TryFrom<&Object> for NftOutput {
type Error = IotaError;
fn try_from(object: &Object) -> Result<Self, Self::Error> {
match &object.data {
Data::Move(o) => {
if o.type_().is_nft_output() {
return NftOutput::from_bcs_bytes(o.contents());
}
}
Data::Package(_) => {}
}

Err(IotaError::Type {
error: format!("Object type is not a NftOutput: {:?}", object),
})
}
}
47 changes: 44 additions & 3 deletions crates/iota-types/src/timelock/timelock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use crate::{
IOTA_FRAMEWORK_ADDRESS,
balance::Balance,
base_types::{IotaAddress, MoveObjectType, ObjectID, SequenceNumber, TxContext},
error::ExecutionError,
error::{ExecutionError, IotaError},
gas_coin::GasCoin,
id::UID,
object::{Data, MoveObject, Object, Owner},
};
Expand Down Expand Up @@ -202,8 +203,10 @@ where
T: Serialize + Deserialize<'de>,
{
/// Create a `TimeLock` from BCS bytes.
pub fn from_bcs_bytes(content: &'de [u8]) -> Result<Self, bcs::Error> {
bcs::from_bytes(content)
pub fn from_bcs_bytes(content: &'de [u8]) -> Result<Self, IotaError> {
bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
error: format!("Unable to deserialize TimeLock object: {:?}", err),
})
}

/// Serialize a `TimeLock` as a `Vec<u8>` of BCS.
Expand Down Expand Up @@ -234,3 +237,41 @@ pub fn is_timelocked_balance(other: &StructTag) -> bool {
_ => false,
}
}

/// Is this other StructTag representing a `TimeLock<Balance<IOTA>>`?
pub fn is_timelocked_gas_balance(other: &StructTag) -> bool {
if !is_timelock(other) {
return false;
}

if other.type_params.len() != 1 {
return false;
}

match &other.type_params[0] {
TypeTag::Struct(tag) => GasCoin::is_gas_balance(tag),
_ => false,
}
}

impl<'de, T> TryFrom<&'de Object> for TimeLock<T>
where
T: Serialize + Deserialize<'de>,
{
type Error = IotaError;

fn try_from(object: &'de Object) -> Result<Self, Self::Error> {
match &object.data {
Data::Move(o) => {
if o.type_().is_timelock() {
return TimeLock::from_bcs_bytes(o.contents());
}
}
Data::Package(_) => {}
}

Err(IotaError::Type {
error: format!("Object type is not a TimeLock: {:?}", object),
})
}
}
2 changes: 1 addition & 1 deletion crates/iota/src/genesis_ceremony.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ pub async fn run(cmd: Ceremony) -> Result<()> {
));
};

examine_genesis_checkpoint(unsigned_genesis);
examine_genesis_checkpoint(unsigned_genesis, builder.tx_migration_objects());
}

CeremonyCommand::VerifyAndSign { key_file } => {
Expand Down
Loading

0 comments on commit 794e8f4

Please sign in to comment.